diff --git a/src/ifcopenshell-python/ifcopenshell/__init__.py b/src/ifcopenshell-python/ifcopenshell/__init__.py index 3f4a6acaa2..0afb15267a 100644 --- a/src/ifcopenshell-python/ifcopenshell/__init__.py +++ b/src/ifcopenshell-python/ifcopenshell/__init__.py @@ -71,7 +71,7 @@ except Exception as e: from . import guid from .file import file -from .entity_instance import entity_instance, register_schema_attributes +from .entity_instance import entity_instance from .sql import sqlite, sqlite_entity try: from .stream import stream, stream_entity @@ -171,7 +171,7 @@ def create_entity(type, schema="IFC4", *args, **kwargs): model = ifcopenshell.file() model.add(person) # #1=IfcPerson($,$,$,$,$,$,$,$) """ - e = entity_instance((schema, type)) + e = ifcopenshell_wrapper.make_instance(schema, type) attrs = list(enumerate(args)) + [(e.wrapped_data.get_argument_index(name), arg) for name, arg in kwargs.items()] for idx, arg in attrs: e[idx] = arg @@ -195,7 +195,6 @@ def register_schema(schema): schema.schema.this.disown() schema.disown() ifcopenshell_wrapper.register_schema(schema.schema) - register_schema_attributes(schema.schema) def schema_by_name( diff --git a/src/ifcopenshell-python/ifcopenshell/entity_instance.py b/src/ifcopenshell-python/ifcopenshell/entity_instance.py index 00e441d9ac..f73bc42ba3 100644 --- a/src/ifcopenshell-python/ifcopenshell/entity_instance.py +++ b/src/ifcopenshell-python/ifcopenshell/entity_instance.py @@ -42,66 +42,8 @@ except ImportError as e: T = TypeVar("T") - -def set_derived_attribute(*args): - raise TypeError("Unable to set derived attribute") - - -def set_unsupported_attribute(*args): - raise TypeError("This is an unsupported attribute type") - - -# For every schema and its entities populate a list -# of functions for every entity attribute (including -# inherited attributes) to set that particular -# attribute by index. -# For example. IFC2X3.IfcWall with have a list of -# 9 methods. The first will point at -# ifcopenshell.ifcopenshell_wrapper.entity_instance.setArgumentAsString -# because the first attribute GlobalId ultimately -# is of type string. -# Previously, resolving the appropriate function was -# done for each invocation of __setitem__. Now this -# mapping is built once during initialization of the -# module. -_method_dict = {} - - -def register_schema_attributes(schema): - for decl in schema.declarations(): - if hasattr(decl, "argument_types"): - fq_name = ".".join((schema.name(), decl.name())) - - # get type strings as reported by IfcOpenShell C++ - type_strs = decl.argument_types() - - # convert case for setter function - type_strs = [x.title().replace(" ", "") for x in type_strs] - - # binary and enumeration are passed from python as string as well - type_strs = [x.replace("Binary", "String") for x in type_strs] - type_strs = [x.replace("Enumeration", "String") for x in type_strs] - - # prefix to get method names - fn_names = ["setArgumentAs" + x for x in type_strs] - - # resolve to actual functions in wrapper - functions = [ - set_derived_attribute - if mname == "setArgumentAsDerived" - else set_unsupported_attribute - if mname == "setArgumentAsUnknown" - else getattr(ifcopenshell_wrapper.entity_instance, mname) - for mname in fn_names - ] - - _method_dict[fq_name] = functions - - -for nm in ifcopenshell_wrapper.schema_names(): - schema = ifcopenshell_wrapper.schema_by_name(nm) - register_schema_attributes(schema) - +# poor man's enum +ATTR_INVALID, ATTR_FORWARD, ATTR_INVERSE, ATTR_FORWARD_DERIVED = range(4) class entity_instance(object): """Base class for all IFC objects. @@ -120,44 +62,22 @@ class entity_instance(object): >>> #423=IfcProductDefinitionShape($,$,(#409,#421)) """ - wrapped_data: ifcopenshell_wrapper.entity_instance - - def __init__(self, e, file=None): - if isinstance(e, tuple): - e = ifcopenshell_wrapper.new_IfcBaseClass(*e) - super(entity_instance, self).__setattr__("wrapped_data", e) - super(entity_instance, self).__setattr__("method_list", None) - - # Make sure the file is not gc'ed while we have live instances - self.wrapped_data.file = file - - def __del__(self): - """ - #2471 while the precise chain of action is unclear, creating - instance references prevents file gc, even with all instance - refs deleted. This is a work-around for that. - """ - self.wrapped_data.file = None - @property def file(self): # ugh circular imports, name collisions from . import file - return file.from_pointer(self.wrapped_data.file_pointer()) + return file.from_pointer(self.file_pointer()) def __getattr__(self, name): - INVALID, FORWARD, INVERSE = range(3) - attr_cat = self.wrapped_data.get_attribute_category(name) - if attr_cat == FORWARD: - idx = self.wrapped_data.get_argument_index(name) - if _method_dict[self.is_a(True)][idx] != set_derived_attribute: - # A bit ugly, but we fall through to derived attribute handling below - return entity_instance.wrap_value(self.wrapped_data.get_argument(idx), self.wrapped_data.file) - elif attr_cat == INVERSE: - vs = entity_instance.wrap_value(self.wrapped_data.get_inverse(name), self.wrapped_data.file) + attr_cat = self.get_attribute_category(name) + if attr_cat == ATTR_FORWARD: + idx = self.get_argument_index(name) + return self.get_argument(idx) + elif attr_cat == ATTR_INVERSE: + vs = self.get_inverse(name) if settings.unpack_non_aggregate_inverses: - schema_name = self.wrapped_data.is_a(True).split(".")[0] + schema_name = self.is_a(True).split(".")[0] ent = ifcopenshell_wrapper.schema_by_name(schema_name).declaration_by_name(self.is_a()) inv = [i for i in ent.all_inverse_attributes() if i.name() == name][0] if (inv.bound1(), inv.bound2()) == (-1, -1): @@ -166,9 +86,9 @@ class entity_instance(object): else: vs = None return vs - + # derived attribute perhaps? - schema_name = self.wrapped_data.is_a(True).split(".")[0] + schema_name = self.is_a(True).split(".")[0] try: rules = importlib.import_module(f"ifcopenshell.express.rules.{schema_name}") except: @@ -180,21 +100,21 @@ class entity_instance(object): subprocess.run([sys.executable, "-m", "ifcopenshell.express.rule_compiler", schema_path, fn], check=True) time.sleep(1.) rules = importlib.import_module(schema_name) - + def yield_supertypes(): decl = ifcopenshell_wrapper.schema_by_name(schema_name).declaration_by_name(self.is_a()) while decl: yield decl.name() decl = decl.supertype() - + for sty in yield_supertypes(): fn = getattr(rules, f"calc_{sty}_{name}", None) if fn: return fn(self) - + if attr_cat != FORWARD: raise AttributeError( - "entity instance of type '%s' has no attribute '%s'" % (self.wrapped_data.is_a(True), name) + "entity instance of type '%s' has no attribute '%s'" % (self.is_a(True), name) ) @staticmethod @@ -235,26 +155,6 @@ class entity_instance(object): else: return value - @staticmethod - def wrap_value(v, file): - def wrap(e): - return entity_instance(e, file) - - def is_instance(e): - return isinstance(e, ifcopenshell_wrapper.entity_instance) - - return entity_instance.walk(is_instance, wrap, v) - - @staticmethod - def unwrap_value(v): - def unwrap(e): - return e.wrapped_data - - def is_instance(e): - return isinstance(e, entity_instance) - - return entity_instance.walk(is_instance, unwrap, v) - def attribute_type(self, attr: int) -> str: """Return the data type of a positional attribute of the element @@ -262,8 +162,8 @@ class entity_instance(object): :type attr: int :rtype: string """ - attr_idx = attr if isinstance(attr, numbers.Integral) else self.wrapped_data.get_argument_index(attr) - return self.wrapped_data.get_argument_type(attr_idx) + attr_idx = attr if isinstance(attr, numbers.Integral) else self.get_argument_index(attr) + return self.get_argument_type(attr_idx) def attribute_name(self, attr_idx: int) -> str: """Return the name of a positional attribute of the element @@ -272,37 +172,28 @@ class entity_instance(object): :type attr_idx: int :rtype: string """ - return self.wrapped_data.get_argument_name(attr_idx) + return self.get_argument_name(attr_idx) def __setattr__(self, key: str, value: Any) -> None: - index = self.wrapped_data.get_argument_index(key) + index = self.get_argument_index(key) self[index] = value def __getitem__(self, key: int) -> Any: if key < 0 or key >= len(self): raise IndexError("Attribute index {} out of range for instance of type {}".format(key, self.is_a())) - return entity_instance.wrap_value(self.wrapped_data.get_argument(key), self.wrapped_data.file) + return self.get_argument(key) def __setitem__(self, idx: int, value: T) -> T: - if self.wrapped_data.file and self.wrapped_data.file.transaction: - self.wrapped_data.file.transaction.store_edit(self, idx, value) - - if self.method_list is None: - super(entity_instance, self).__setattr__("method_list", _method_dict[self.is_a(True)]) - - method = self.method_list[idx] - - if value is None: - if method is not set_derived_attribute: - self.wrapped_data.setArgumentAsNull(idx) - else: - self.method_list[idx](self.wrapped_data, idx, entity_instance.unwrap_value(value)) - + if self.file and self.file.transaction: + self.file.transaction.store_edit(self, idx, value) + + self.setAttribute(idx, value) + return value - + def __len__(self): return len(self.wrapped_data) - + def __repr__(self): return repr(self.wrapped_data) @@ -315,7 +206,7 @@ class entity_instance(object): the specific control directives. """ - return self.wrapped_data.to_string(valid_spf) + return self.to_string(valid_spf) @overload def is_a(self) -> str: ... @@ -348,19 +239,19 @@ class entity_instance(object): f.is_a('IfcPerson') >>> True """ - return self.wrapped_data.is_a(*args) + return self.is_a(*args) def id(self) -> int: """Return the STEP numerical identifier :rtype: int """ - return self.wrapped_data.id() + return self.id() def __eq__(self, other): if not isinstance(self, type(other)): return False - elif None in (self.wrapped_data.file, other.wrapped_data.file): + elif None in (self.file, other.file): # when not added to a file, we can only compare attribute values # and we need this for where rule evaluation return self.get_info(recursive=True, include_identifier=False) == other.get_info( @@ -373,10 +264,10 @@ class entity_instance(object): if self.id(): return self.wrapped_data == other.wrapped_data else: - return (self.is_a(), self[0], self.wrapped_data.file_pointer()) == ( + return (self.is_a(), self[0], self.file_pointer()) == ( other.is_a(), other[0], - other.wrapped_data.file_pointer(), + other.file_pointer(), ) def is_entity(self) -> bool: @@ -385,7 +276,7 @@ class entity_instance(object): Returns: bool: True if the instance is an entity """ - schema_name = self.wrapped_data.is_a(True).split(".")[0] + schema_name = self.is_a(True).split(".")[0] decl = ifcopenshell_wrapper.schema_by_name(schema_name).declaration_by_name(self.is_a()) return isinstance(decl, ifcopenshell_wrapper.entity) @@ -459,17 +350,17 @@ class entity_instance(object): # step id. Selected type instances (such as IfcPropertySingleValue.NominalValue # always have id=0, so we hash if self.id(): - return hash((self.id(), self.wrapped_data.file_pointer())) + return hash((self.id(), self.file_pointer())) else: - return hash((self.is_a(), self[0], self.wrapped_data.file_pointer())) + return hash((self.is_a(), self[0], self.file_pointer())) def __dir__(self): return sorted( set( itertools.chain( dir(type(self)), - map(str, self.wrapped_data.get_attribute_names()), - map(str, self.wrapped_data.get_inverse_attribute_names()), + map(str, self.get_attribute_names()), + map(str, self.get_inverse_attribute_names()), ) ) ) @@ -513,7 +404,7 @@ class entity_instance(object): logging.exception("unhandled exception while getting id / type info on {}".format(self)) for i in range(len(self)): try: - if self.wrapped_data.get_attribute_names()[i] in ignore: + if self.get_attribute_names()[i] in ignore: continue attr_value = self[i] @@ -548,7 +439,8 @@ class entity_instance(object): return return_type(_()) - __dict__ = property(get_info) + # @todo this is no longer possible with the direct integration into the swig type + # __dict__ = property(get_info) def get_info_2(self, include_identifier=True, recursive=False, return_type=dict, ignore=()): """More perfomant version of `.get_info()` but with limited arguments values.\n diff --git a/src/ifcopenshell-python/ifcopenshell/file.py b/src/ifcopenshell-python/ifcopenshell/file.py index d5446d731e..e8b79dd669 100644 --- a/src/ifcopenshell-python/ifcopenshell/file.py +++ b/src/ifcopenshell-python/ifcopenshell/file.py @@ -328,7 +328,7 @@ class file(object): """ eid = kwargs.pop("id", -1) - e = entity_instance((self.schema_identifier, type), self) + e = ifcopenshell_wrapper.make_instance(self.schema_identifier, type) # Create pairs of {attribute index, attribute value}. # Keyword arguments are mapped to their corresponding @@ -337,7 +337,7 @@ class file(object): # @todo we should probably check that values for # attributes are not passed as duplicates using # both regular arguments and keyword arguments. - attrs = list(enumerate(args)) + [(e.wrapped_data.get_argument_index(name), arg) for name, arg in kwargs.items()] + attrs = list(enumerate(args)) + [(e.get_argument_index(name), arg) for name, arg in kwargs.items()] # Don't store these attributes as transactions # as the creation it self is already stored with @@ -353,14 +353,13 @@ class file(object): if attrs: self.transaction = transaction - # Once the values are populated add the instance + # Once the values are populated move the instance # to the file. - self.wrapped_data.add(e.wrapped_data, eid) + e = self.wrapped_data.add(e, eid) # The file container now handles the lifetime of # this instance. Tell SWIG that it is no longer # the owner. - e.wrapped_data.this.disown() if self.transaction: self.transaction.store_create(e) @@ -398,9 +397,9 @@ class file(object): def __getitem__(self, key): if isinstance(key, numbers.Integral): - return entity_instance(self.wrapped_data.by_id(key), self) + return self.wrapped_data.by_id(key) elif isinstance(key, basestring): - return entity_instance(self.wrapped_data.by_guid(str(key)), self) + return self.wrapped_data.by_guid(str(key)) def by_id(self, id: int) -> ifcopenshell.entity_instance: """Return an IFC entity instance filtered by IFC ID. @@ -441,8 +440,8 @@ class file(object): if self.transaction: max_id = self.wrapped_data.getMaxId() - inst.wrapped_data.this.disown() - result = entity_instance(self.wrapped_data.add(inst.wrapped_data, -1 if _id is None else _id), self) + # inst.wrapped_data.this.disown() + result = self.wrapped_data.add(inst, -1 if _id is None else _id) if self.transaction: added_elements = [e for e in self.traverse(result) if e.id() > max_id] [self.transaction.store_create(e) for e in reversed(added_elements)] @@ -461,8 +460,8 @@ class file(object): :rtype: list[ifcopenshell.entity_instance.entity_instance] """ if include_subtypes: - return [entity_instance(e, self) for e in self.wrapped_data.by_type(type)] - return [entity_instance(e, self) for e in self.wrapped_data.by_type_excl_subtypes(type)] + return self.wrapped_data.by_type(type) + return self.wrapped_data.by_type_excl_subtypes(type) def traverse( self, inst: ifcopenshell.entity_instance, max_levels=None, breadth_first=False @@ -486,7 +485,7 @@ class file(object): else: fn = self.wrapped_data.traverse - return [entity_instance(e, self) for e in fn(inst.wrapped_data, max_levels)] + return fn(inst, max_levels) def get_inverse( self, inst: ifcopenshell.entity_instance, allow_duplicate=False, with_attribute_indices=False @@ -504,11 +503,11 @@ class file(object): if with_attribute_indices and not allow_duplicate: raise ValueError("with_attribute_indices requires allow_duplicate to be True") - inverses = [entity_instance(e, self) for e in self.wrapped_data.get_inverse(inst.wrapped_data)] + inverses = self.wrapped_data.get_inverse(inst) if allow_duplicate: if with_attribute_indices: - idxs = self.wrapped_data.get_inverse_indices(inst.wrapped_data) + idxs = self.wrapped_data.get_inverse_indices(inst) return list(zip(inverses, idxs)) else: return inverses @@ -523,7 +522,7 @@ class file(object): :returns: The total number of references :rtype: int """ - return self.wrapped_data.get_total_inverses(inst.wrapped_data) + return self.wrapped_data.get_total_inverses(inst) def remove(self, inst: ifcopenshell.entity_instance) -> None: """Deletes an IFC object in the file. @@ -538,7 +537,7 @@ class file(object): """ if self.transaction: self.transaction.store_delete(inst) - return self.wrapped_data.remove(inst.wrapped_data) + return self.wrapped_data.remove(inst) def batch(self): """Low-level mechanism to speed up deletion of large subgraphs""" @@ -605,4 +604,4 @@ class file(object): @staticmethod def from_pointer(v): - return file_dict.get(v)() + return file_dict.get(v)() if v else None diff --git a/src/ifcparse/IfcParse.cpp b/src/ifcparse/IfcParse.cpp index 1c27e94b96..fa101f9f1d 100644 --- a/src/ifcparse/IfcParse.cpp +++ b/src/ifcparse/IfcParse.cpp @@ -1902,8 +1902,6 @@ IfcFile::instance_storage_type IfcFile::addEntity(IfcUtil::IfcBaseClass* entity, return mit->second; } - instance_storage_type new_entity = instance_storage_type(entity); - // Obtain all forward references by a depth-first // traversal and add them to the file. if (parsing_complete_) { @@ -1922,12 +1920,17 @@ IfcFile::instance_storage_type IfcFile::addEntity(IfcUtil::IfcBaseClass* entity, } } + instance_storage_type new_entity; + // See whether the instance is already part of a file - if (entity->data().file != 0) { + if (entity->data().file == 0) { + // We can simply wrap it into a new smart pointer + new_entity = instance_storage_type(entity); + } else { if (entity->data().file == this) { if (entity->declaration().as_entity() == nullptr) { // While not a mapping that can be queried, we do need to free the instance later on - byidentity_[new_entity->identity()] = new_entity; + byidentity_[entity->identity()] = instance_storage_type(entity); } // If it is part of this file diff --git a/src/ifcwrap/CMakeLists.txt b/src/ifcwrap/CMakeLists.txt index d6ac9e3771..df1af202b7 100644 --- a/src/ifcwrap/CMakeLists.txt +++ b/src/ifcwrap/CMakeLists.txt @@ -44,6 +44,10 @@ INCLUDE_DIRECTORIES(${PYTHON_INCLUDE_DIRS}) INCLUDE_DIRECTORIES(${CMAKE_CURRENT_SOURCE_DIR}) SET(CMAKE_SWIG_FLAGS ${SWIG_DEFINES}) + +# track file modifications to reinvoke swig wrapper code gen +set(USE_SWIG_DEPENDENCIES TRUE) + # NOTE Workaround for most likely missing debug Python libraries on Windows (requires Python built from the source). # Python 3.5 intaller and onwards will have an option to install the debug libraries too. # NOTE PYTHON_DEBUG_LIBRARIES appears to be a deprecated variable @@ -68,6 +72,15 @@ endif() # directory in which the wrapper can be installed. FIND_PACKAGE(PythonInterp) IF((PYTHONINTERP_FOUND AND NOT "${PYTHON_EXECUTABLE}" STREQUAL "") OR PYTHON_MODULE_INSTALL_DIR) +# Find Python interpreter and get its version + execute_process( + COMMAND ${PYTHON_EXECUTABLE} -c "import sysconfig; print(sysconfig.get_config_var('EXT_SUFFIX'))" + OUTPUT_VARIABLE PYTHON_EXTENSION_SUFFIX + OUTPUT_STRIP_TRAILING_WHITESPACE + ) + + set_target_properties(${SWIG_MODULE_ifcopenshell_wrapper_REAL_NAME} PROPERTIES SUFFIX ${PYTHON_EXTENSION_SUFFIX}) + if (PYTHON_MODULE_INSTALL_DIR) set(python_package_dir "${PYTHON_MODULE_INSTALL_DIR}") else() diff --git a/src/ifcwrap/IfcGeomWrapper.i b/src/ifcwrap/IfcGeomWrapper.i index 802dade8ed..78801a37de 100644 --- a/src/ifcwrap/IfcGeomWrapper.i +++ b/src/ifcwrap/IfcGeomWrapper.i @@ -587,13 +587,6 @@ struct ShapeRTTI : public boost::static_visitor %} }; -%extend IfcGeom::Matrix { - %pythoncode %{ - # Hide the getters with read-only property implementations - data = property(data) - %} -}; - %{ template std::string to_locale_invariant_string(const T& t) { diff --git a/src/ifcwrap/IfcParseWrapper.i b/src/ifcwrap/IfcParseWrapper.i index 6e04a5dc2a..323392fb52 100644 --- a/src/ifcwrap/IfcParseWrapper.i +++ b/src/ifcwrap/IfcParseWrapper.i @@ -50,10 +50,15 @@ private: %ignore IfcParse::IfcFile::instances_by_type; %ignore IfcParse::IfcFile::instances_by_type_excl_subtypes; -%rename("file") IfcFile; %ignore IfcParse::IfcFile::addEntity; +%ignore IfcParse::IfcFile::addEntities; %ignore IfcParse::IfcFile::removeEntity; +%ignore IfcParse::IfcFile; +%ignore IfcUtil::IfcBaseClass; +%ignore IfcUtil::IfcBaseEntity; +%ignore IfcUtil::IfcBaseInterface; + class attribute_value_derived {}; %{ class attribute_value_derived {}; @@ -87,65 +92,190 @@ PyObject* get_feature(const std::string& x) { %} +%ignore entity_instance::entity_instance; + %{ class entity_instance; const std::string& helper_fn_declaration_get_name(const IfcParse::declaration* decl); IfcUtil::ArgumentType helper_fn_attribute_type(const entity_instance* inst, unsigned i); +bool check_aggregate_of_type(PyObject* aggregate, void* type_obj); +bool check_aggregate_of_aggregate_of_type(PyObject* aggregate, void* type_obj); %} +%pythoncode %{ +### hack hack hack +### we trick swig into inheriting from our own extension class +### that way we do not constantly need to decorate/undecorate +# @todo is there no official way to do this? +_old_object = _object +from .entity_instance import entity_instance as custom_base +_object = custom_base +%} + +%include "utils/type_conversion.i" +%include "utils/typemaps_in.i" +%include "utils/typemaps_out.i" %inline %{ class entity_instance { boost::variant< - IfcParse::IfcFile::instance_storage_type, - std::weak_ptr + IfcUtil::IfcBaseClass*, + std::weak_ptr, + boost::blank > data_; + std::shared_ptr file_; struct visitor { - IfcParse::IfcFile::instance_storage_type operator()(const IfcParse::IfcFile::instance_storage_type& t) { + const IfcUtil::IfcBaseClass* const operator()(const IfcUtil::IfcBaseClass* const & t) { return t; } - IfcParse::IfcFile::instance_storage_type operator()(const std::weak_ptr& t) { + const IfcUtil::IfcBaseClass* const operator()(const std::weak_ptr& t) { auto u = t.lock(); if (u) { - return u; + // @todo this is not really safe, nor ideal as we don't retain the scope + // until used. + return &*u; + } else { + throw std::runtime_error("No longer availabe"); + } + } + IfcUtil::IfcBaseClass* operator()(boost::blank) { + throw std::runtime_error("No longer availabe"); + } + IfcUtil::IfcBaseClass* operator()(IfcUtil::IfcBaseClass* & t) { + return t; + } + IfcUtil::IfcBaseClass* operator()(std::weak_ptr& t) { + auto u = t.lock(); + if (u) { + // @todo this is not really safe, nor ideal as we don't retain the scope + // until used. + return &*u; } else { throw std::runtime_error("No longer availabe"); } } }; public: - entity_instance(const IfcParse::IfcFile::instance_storage_type& shared) + entity_instance( + const IfcParse::IfcFile::instance_storage_type& shared, + const std::shared_ptr& file + ) : data_(std::weak_ptr(shared)) - {} + , file_(file) + { + std::cout << "init " << this << " " << data_.which() << std::endl; + } entity_instance(IfcUtil::IfcBaseClass* naked) - : data_(IfcParse::IfcFile::instance_storage_type(naked)) - {} + : data_(naked) + { + std::cout << "init " << this << " " << data_.which() << std::endl; + } - operator IfcUtil::IfcBaseClass*() const { - return &*boost::apply_visitor(visitor{}, data_); + ~entity_instance() { + if (data_.which() == 0) { + delete (const IfcUtil::IfcBaseClass*)*this; + } + } + + IfcUtil::IfcBaseClass* move() { + auto old = i(); + data_ = boost::blank{}; + std::cout << "reset " << this << " " << data_.which() << std::endl; + return old; + } + + entity_instance(const entity_instance&) = delete; + entity_instance& operator=(const entity_instance&) = delete; + + operator const IfcUtil::IfcBaseClass*() const { + std::cout << "visit" << this << std::endl; + return boost::apply_visitor(visitor{}, data_); + } + + const IfcUtil::IfcBaseClass* i() const { + std::cout << "visit" << this << std::endl; + return boost::apply_visitor(visitor{}, data_); + } + + operator IfcUtil::IfcBaseClass*() { + std::cout << "visit" << this << std::endl; + return boost::apply_visitor(visitor{}, data_); + } + + IfcUtil::IfcBaseClass* i() { + std::cout << "visit" << this << std::endl; + return boost::apply_visitor(visitor{}, data_); } const IfcParse::declaration& declaration() const { + std::cout << "visit" << this << std::endl; return boost::apply_visitor(visitor{}, data_)->declaration(); } - const IfcEntityInstanceData& data() const { + const IfcEntityInstanceData& data() const { + std::cout << "visit" << this << std::endl; return boost::apply_visitor(visitor{}, data_)->data(); } - IfcEntityInstanceData& data() { + IfcEntityInstanceData& data() { + std::cout << "visit" << this << std::endl; return boost::apply_visitor(visitor{}, data_)->data(); } uint32_t identity() const { + std::cout << "visit" << this << std::endl; return boost::apply_visitor(visitor{}, data_)->identity(); } + + const std::shared_ptr& file_pointer() const { + return file_; + } }; %} +%pythoncode %{ +### hack hack hack +### restore +_object = _old_object +%} + +%inline %{ +// Using swig's builtin shared_ptr() feature does not work, +// because we wouldn't actually get access to this pointer +// in extend() calls, which is need to propagate the shared_ptr +// control block to the references to it in dependent instances. +class file { +private: + std::shared_ptr ptr_; + +public: + file() + : ptr_(new IfcParse::IfcFile) + {} + + IfcParse::IfcFile& operator* () + { + return *ptr_; + } + + IfcParse::IfcFile* operator-> () + { + return &*ptr_; + } + + operator const std::shared_ptr&() const { + return ptr_; + } + + std::string __repr__() const { + return "ifcopenshell.file object at " + std::to_string(reinterpret_cast(ptr_.get())); + } +}; +%} + %{ const std::string& helper_fn_declaration_get_name(const IfcParse::declaration* decl) { @@ -174,100 +304,102 @@ IfcUtil::ArgumentType helper_fn_attribute_type(const entity_instance* inst, unsi } %} -%extend IfcParse::IfcFile { +%extend file { // Use to correlate to entity_instance.file_pointer, so that we // can trace file ownership of instances on the python side. - size_t file_pointer() const { - return reinterpret_cast($self); + size_t file_pointer() { + return reinterpret_cast(&**self); } - entity_instance by_guid(const std::string& guid) { - return $self->instance_by_guid(guid); + entity_instance* by_guid(const std::string& guid) { + return new entity_instance((*$self)->instance_by_guid(guid), (*$self)); } - entity_instance by_id(int i) { - return $self->instance_by_id_2(i); + entity_instance* by_id(int i) { + return new entity_instance((*$self)->instance_by_id_2(i), (*$self)); } - std::vector by_type(const std::string& ty) { - std::vector vec; - auto range = $self->instances_by_type_range(ty); + std::vector by_type(const std::string& ty) { + std::vector vec; + auto range = (*$self)->instances_by_type_range(ty); for (auto& inst : boost::make_iterator_range(range)) { - vec.push_back(inst); + vec.push_back(new entity_instance(inst, *$self)); } return vec; } - std::vector by_type_excl_subtypes(const std::string& ty) { - std::vector vec; - auto range = $self->instances_by_type_excl_subtypes_range(ty); + std::vector by_type_excl_subtypes(const std::string& ty) { + std::vector vec; + auto range = (*$self)->instances_by_type_excl_subtypes_range(ty); for (auto& inst : boost::make_iterator_range(range)) { - vec.push_back(inst); + vec.push_back(new entity_instance(inst, *$self)); } return vec; } - entity_instance add(entity_instance& e, int i) { - return $self->addEntity(e, i); + entity_instance* add(entity_instance* e, int i) { + return new entity_instance((*$self)->addEntity(e->move(), i), *self); } - void remove(entity_instance& e) { - return $self->removeEntity(e); + void remove(entity_instance* e) { + return (*$self)->removeEntity(*e); } - aggregate_of_instance::ptr get_inverse(entity_instance& e) { - return $self->getInverse(e.data().id(), 0, -1); + aggregate_of_instance::ptr get_inverse(entity_instance* e) { + return (*$self)->getInverse(e->data().id(), 0, -1); } - std::vector get_inverse_indices(entity_instance& e) { - return $self->get_inverse_indices(e.data().id()); + std::vector get_inverse_indices(entity_instance* e) { + return (*$self)->get_inverse_indices(e->data().id()); } - int get_total_inverses(entity_instance& e) { - return $self->getTotalInverses(e.data().id()); + int get_total_inverses(entity_instance* e) { + return (*$self)->getTotalInverses(e->data().id()); } void write(const std::string& fn) { std::ofstream f(IfcUtil::path::from_utf8(fn).c_str()); - f << (*$self); + f << (**$self); } std::string to_string() { std::stringstream s; - s << (*$self); + s << (**$self); return s.str(); } - std::vector entity_names() const { + std::vector entity_names() { std::vector keys; - keys.reserve(std::distance($self->begin(), $self->end())); - for (IfcParse::IfcFile::entity_by_id_t::const_iterator it = $self->begin(); it != $self->end(); ++ it) { + keys.reserve(std::distance((*$self)->begin(), (*$self)->end())); + for (IfcParse::IfcFile::entity_by_id_t::const_iterator it = (*$self)->begin(); it != (*$self)->end(); ++ it) { keys.push_back(it->first); } return keys; } - std::vector types() const { - const size_t n = std::distance($self->types_begin(), $self->types_end()); + std::vector types() { + const size_t n = std::distance((*$self)->types_begin(), (*$self)->types_end()); std::vector ts; ts.reserve(n); - std::transform($self->types_begin(), $self->types_end(), std::back_inserter(ts), helper_fn_declaration_get_name); + std::transform((*$self)->types_begin(), (*$self)->types_end(), std::back_inserter(ts), helper_fn_declaration_get_name); return ts; } - std::vector types_with_super() const { - const size_t n = std::distance($self->types_incl_super_begin(), $self->types_incl_super_end()); + std::vector types_with_super() { + const size_t n = std::distance((*$self)->types_incl_super_begin(), (*$self)->types_incl_super_end()); std::vector ts; ts.reserve(n); - std::transform($self->types_incl_super_begin(), $self->types_incl_super_end(), std::back_inserter(ts), helper_fn_declaration_get_name); + std::transform((*$self)->types_incl_super_begin(), (*$self)->types_incl_super_end(), std::back_inserter(ts), helper_fn_declaration_get_name); return ts; } - std::string schema_name() const { - if ($self->schema() == 0) return ""; - return $self->schema()->name(); + std::string schema_name() { + if ((*$self)->schema() == 0) return ""; + return (*$self)->schema()->name(); } + IfcParse::IfcSpfHeader& header() { return (*$self)->header(); } + %pythoncode %{ # Hide the getters with read-only property implementations header = property(header) @@ -287,6 +419,10 @@ IfcUtil::ArgumentType helper_fn_attribute_type(const entity_instance* inst, unsi std::vector::const_iterator it = attrs.begin(); for (; it != attrs.end(); ++it) { if ((*it)->name() == name) { + if (self->declaration().as_entity()->derived()[std::distance(attrs.begin(), it)]) { + // derived + return 3; + } return 1; } } @@ -368,13 +504,13 @@ IfcUtil::ArgumentType helper_fn_attribute_type(const entity_instance* inst, unsi return t; } - std::pair get_argument(unsigned i) { - return std::pair($self->data().getArgument(i)->type(), $self->data().getArgument(i)); + std::tuple get_argument(unsigned i) { + return { $self->data().getArgument(i)->type(), $self->data().getArgument(i), self }; } - std::pair get_argument(const std::string& a) { + std::tuple get_argument(const std::string& a) { unsigned i = $self->declaration().as_entity()->attribute_index(a); - return std::pair($self->data().getArgument(i)->type(), $self->data().getArgument(i)); + return { $self->data().getArgument(i)->type(), $self->data().getArgument(i), self }; } bool __eq__(entity_instance* other) const { @@ -430,202 +566,213 @@ IfcUtil::ArgumentType helper_fn_attribute_type(const entity_instance* inst, unsi void setAttribute(unsigned int i, PyObject* obj) { bool is_optional = $self->declaration().as_entity()->attribute_by_index(i)->optional(); - if () - if (is_optional) { - self->data().setArgument(i, new IfcWrite::IfcWriteArgument()); - } else { - throw IfcParse::IfcException("Attribute not set"); - } - - IfcUtil::ArgumentType arg_type = helper_fn_attribute_type($self, i); - } - - void setArgumentAsInt(unsigned int i, int v) { - IfcUtil::ArgumentType arg_type = helper_fn_attribute_type($self, i); - if (arg_type == IfcUtil::Argument_INT) { - IfcWrite::IfcWriteArgument* arg = new IfcWrite::IfcWriteArgument(); - arg->set(v); - self->data().setArgument(i, arg); - } else if ( (arg_type == IfcUtil::Argument_BOOL) && ( (v == 0) || (v == 1) ) ) { - IfcWrite::IfcWriteArgument* arg = new IfcWrite::IfcWriteArgument(); - arg->set(v == 1); - self->data().setArgument(i, arg); - } else { - throw IfcParse::IfcException("Attribute not set"); - } - } - - void setArgumentAsBool(unsigned int i, bool v) { - IfcUtil::ArgumentType arg_type = helper_fn_attribute_type($self, i); - if (arg_type == IfcUtil::Argument_BOOL) { - IfcWrite::IfcWriteArgument* arg = new IfcWrite::IfcWriteArgument(); - arg->set(v); - self->data().setArgument(i, arg); - } else { - throw IfcParse::IfcException("Attribute not set"); - } - } - - void setArgumentAsLogical(unsigned int i, boost::logic::tribool v) { - IfcUtil::ArgumentType arg_type = helper_fn_attribute_type($self, i); - if (arg_type == IfcUtil::Argument_LOGICAL) { - IfcWrite::IfcWriteArgument* arg = new IfcWrite::IfcWriteArgument(); - arg->set(v); - self->data().setArgument(i, arg); - } else { - throw IfcParse::IfcException("Attribute not set"); - } - } - - void setArgumentAsDouble(unsigned int i, double v) { - IfcUtil::ArgumentType arg_type = helper_fn_attribute_type($self, i); - if (arg_type == IfcUtil::Argument_DOUBLE) { - IfcWrite::IfcWriteArgument* arg = new IfcWrite::IfcWriteArgument(); - arg->set(v); - self->data().setArgument(i, arg); - } else { - throw IfcParse::IfcException("Attribute not set"); - } - } - - void setArgumentAsString(unsigned int i, const std::string& a) { - IfcUtil::ArgumentType arg_type = helper_fn_attribute_type($self, i); - if (arg_type == IfcUtil::Argument_STRING) { - IfcWrite::IfcWriteArgument* arg = new IfcWrite::IfcWriteArgument(); - arg->set(a); - self->data().setArgument(i, arg); - } else if (arg_type == IfcUtil::Argument_ENUMERATION) { - const IfcParse::enumeration_type* enum_type = $self->declaration().schema()->declaration_by_name($self->declaration().type())->as_entity()-> - attribute_by_index(i)->type_of_attribute()->as_named_type()->declared_type()->as_enumeration_type(); - - std::vector::const_iterator it = std::find( - enum_type->enumeration_items().begin(), - enum_type->enumeration_items().end(), - a); - - if (it == enum_type->enumeration_items().end()) { - throw IfcParse::IfcException(a + " does not name a valid item for " + enum_type->name()); - } - - IfcWrite::IfcWriteArgument* arg = new IfcWrite::IfcWriteArgument(); - arg->set(IfcWrite::IfcWriteArgument::EnumerationReference(it - enum_type->enumeration_items().begin(), it->c_str())); - self->data().setArgument(i, arg); - } else if (arg_type == IfcUtil::Argument_BINARY) { - if (IfcUtil::valid_binary_string(a)) { - boost::dynamic_bitset<> bits(a); - IfcWrite::IfcWriteArgument* arg = new IfcWrite::IfcWriteArgument(); - arg->set(bits); - self->data().setArgument(i, arg); + if (obj == Py_None) { + if (is_optional) { + self->i()->unset_value(i); + return; } else { - throw IfcParse::IfcException("String not a valid binary representation"); + throw IfcParse::IfcException("Attribute not set"); } - } else { - throw IfcParse::IfcException("Attribute not set"); } - } - - void setArgumentAsAggregateOfInt(unsigned int i, const std::vector& v) { + IfcUtil::ArgumentType arg_type = helper_fn_attribute_type($self, i); - if (arg_type == IfcUtil::Argument_AGGREGATE_OF_INT) { - IfcWrite::IfcWriteArgument* arg = new IfcWrite::IfcWriteArgument(); - arg->set(v); - self->data().setArgument(i, arg); - } else { - throw IfcParse::IfcException("Attribute not set"); + + if (PyLong_Check(obj)) { + long v = PyLong_AsLong(obj); + if (arg_type == IfcUtil::Argument_INT) { + self->i()->set_value(i, v); + } else if ( (arg_type == IfcUtil::Argument_BOOL) && ( (v == 0) || (v == 1) ) ) { + self->i()->set_value(i, v == 1); + } else { + throw IfcParse::IfcException("Attribute not set"); + } } - } - void setArgumentAsAggregateOfDouble(unsigned int i, const std::vector& v) { - IfcUtil::ArgumentType arg_type = helper_fn_attribute_type($self, i); - if (arg_type == IfcUtil::Argument_AGGREGATE_OF_DOUBLE) { - IfcWrite::IfcWriteArgument* arg = new IfcWrite::IfcWriteArgument(); - arg->set(v); - self->data().setArgument(i, arg); - } else { - throw IfcParse::IfcException("Attribute not set"); + else if (PyBool_Check(obj)) { + bool v = PyObject_IsTrue(obj); + if (arg_type == IfcUtil::Argument_BOOL || arg_type == IfcUtil::Argument_LOGICAL) { + self->i()->set_value(i, v); + } else { + throw IfcParse::IfcException("Attribute not set"); + } } - } - void setArgumentAsAggregateOfString(unsigned int i, const std::vector& v) { - IfcUtil::ArgumentType arg_type = helper_fn_attribute_type($self, i); - if (arg_type == IfcUtil::Argument_AGGREGATE_OF_STRING) { - IfcWrite::IfcWriteArgument* arg = new IfcWrite::IfcWriteArgument(); - arg->set(v); - self->data().setArgument(i, arg); - } else if (arg_type == IfcUtil::Argument_AGGREGATE_OF_BINARY) { - std::vector< boost::dynamic_bitset<> > bits; - bits.reserve(v.size()); - for (std::vector::const_iterator it = v.begin(); it != v.end(); ++it) { - if (IfcUtil::valid_binary_string(*it)) { - bits.push_back(boost::dynamic_bitset<>(*it)); + else if (PyUnicode_Check(obj)) { + PyObject* ascii = PyUnicode_AsEncodedString(obj, "UTF-8", "strict"); + if (ascii) { + auto s = std::string(PyBytes_AS_STRING(ascii)); + Py_DECREF(ascii); + if (arg_type == IfcUtil::Argument_LOGICAL) { + if (s == "UNKNOWN") { + self->i()->set_value(i, boost::logic::tribool(boost::logic::indeterminate)); + } else { + throw IfcParse::IfcException("Attribute not set"); + } + } else if (arg_type == IfcUtil::Argument_STRING) { + self->i()->set_value(i, s); + } else if (arg_type == IfcUtil::Argument_ENUMERATION) { + const IfcParse::enumeration_type* enum_type = $self->declaration().schema()->declaration_by_name($self->declaration().type())->as_entity()-> + attribute_by_index(i)->type_of_attribute()->as_named_type()->declared_type()->as_enumeration_type(); + + std::vector::const_iterator it = std::find( + enum_type->enumeration_items().begin(), + enum_type->enumeration_items().end(), + s); + + if (it == enum_type->enumeration_items().end()) { + throw IfcParse::IfcException(s + " does not name a valid item for " + enum_type->name()); + } + + self->i()->set_value(i, IfcWrite::IfcWriteArgument::EnumerationReference(it - enum_type->enumeration_items().begin(), it->c_str())); + } else if (arg_type == IfcUtil::Argument_BINARY) { + if (IfcUtil::valid_binary_string(s)) { + boost::dynamic_bitset<> bits(s); + self->i()->set_value(i, bits); + } else { + throw IfcParse::IfcException("String not a valid binary representation"); + } } else { - throw IfcParse::IfcException("String not a valid binary representation"); - } + throw IfcParse::IfcException("Attribute not set"); + } } - IfcWrite::IfcWriteArgument* arg = new IfcWrite::IfcWriteArgument(); - arg->set(bits); - self->data().setArgument(i, arg); - } else { - throw IfcParse::IfcException("Attribute not set"); } - } - void setArgumentAsEntityInstance(unsigned int i, entity_instance* v) { - IfcUtil::ArgumentType arg_type = helper_fn_attribute_type($self, i); - if (arg_type == IfcUtil::Argument_ENTITY_INSTANCE) { - IfcWrite::IfcWriteArgument* arg = new IfcWrite::IfcWriteArgument(); - arg->set(v); - self->data().setArgument(i, arg); - } else { - throw IfcParse::IfcException("Attribute not set"); + else if (PyFloat_Check(obj)) { + double v = PyFloat_AsDouble(obj); + if (arg_type == IfcUtil::Argument_DOUBLE) { + self->i()->set_value(i, v); + } else { + throw IfcParse::IfcException("Attribute not set"); + } } - } - void setArgumentAsAggregateOfEntityInstance(unsigned int i, aggregate_of_instance::ptr v) { - IfcUtil::ArgumentType arg_type = helper_fn_attribute_type($self, i); - if (arg_type == IfcUtil::Argument_AGGREGATE_OF_ENTITY_INSTANCE) { - IfcWrite::IfcWriteArgument* arg = new IfcWrite::IfcWriteArgument(); - arg->set(v); - self->data().setArgument(i, arg); - } else { - throw IfcParse::IfcException("Attribute not set"); + else if (check_aggregate_of_type(obj, get_python_type())) { + auto v = python_sequence_as_vector(obj); + if (arg_type == IfcUtil::Argument_AGGREGATE_OF_INT) { + self->i()->set_value(i, v); + } else { + throw IfcParse::IfcException("Attribute not set"); + } } - } - void setArgumentAsAggregateOfAggregateOfInt(unsigned int i, const std::vector< std::vector >& v) { - IfcUtil::ArgumentType arg_type = helper_fn_attribute_type($self, i); - if (arg_type == IfcUtil::Argument_AGGREGATE_OF_AGGREGATE_OF_INT) { - IfcWrite::IfcWriteArgument* arg = new IfcWrite::IfcWriteArgument(); - arg->set(v); - self->data().setArgument(i, arg); - } else { - throw IfcParse::IfcException("Attribute not set"); + else if (check_aggregate_of_type(obj, get_python_type())) { + auto v = python_sequence_as_vector(obj); + if (arg_type == IfcUtil::Argument_AGGREGATE_OF_DOUBLE) { + self->i()->set_value(i, v); + } else { + throw IfcParse::IfcException("Attribute not set"); + } } - } - void setArgumentAsAggregateOfAggregateOfDouble(unsigned int i, const std::vector< std::vector >& v) { - IfcUtil::ArgumentType arg_type = helper_fn_attribute_type($self, i); - if (arg_type == IfcUtil::Argument_AGGREGATE_OF_AGGREGATE_OF_DOUBLE) { - IfcWrite::IfcWriteArgument* arg = new IfcWrite::IfcWriteArgument(); - arg->set(v); - self->data().setArgument(i, arg); - } else { - throw IfcParse::IfcException("Attribute not set"); + else if (check_aggregate_of_type(obj, get_python_type())) { + auto v = python_sequence_as_vector(obj); + if (arg_type == IfcUtil::Argument_AGGREGATE_OF_STRING) { + self->i()->set_value(i, v); + } else if (arg_type == IfcUtil::Argument_AGGREGATE_OF_BINARY) { + std::vector< boost::dynamic_bitset<> > bits; + bits.reserve(v.size()); + for (std::vector::const_iterator it = v.begin(); it != v.end(); ++it) { + if (IfcUtil::valid_binary_string(*it)) { + bits.push_back(boost::dynamic_bitset<>(*it)); + } else { + throw IfcParse::IfcException("String not a valid binary representation"); + } + } + self->i()->set_value(i, bits); + } else { + throw IfcParse::IfcException("Attribute not set"); + } } - } - void setArgumentAsAggregateOfAggregateOfEntityInstance(unsigned int i, aggregate_of_aggregate_of_instance::ptr v) { - IfcUtil::ArgumentType arg_type = helper_fn_attribute_type($self, i); - if (arg_type == IfcUtil::Argument_AGGREGATE_OF_AGGREGATE_OF_ENTITY_INSTANCE) { - IfcWrite::IfcWriteArgument* arg = new IfcWrite::IfcWriteArgument(); - arg->set(v); - self->data().setArgument(i, arg); - } else { - throw IfcParse::IfcException("Attribute not set"); + else if (auto inst = cast_pyobject(obj)) { + if (arg_type == IfcUtil::Argument_ENTITY_INSTANCE) { + self->i()->set_value(i, inst->i()); + } else { + throw IfcParse::IfcException("Attribute not set"); + } + } + + else if (check_aggregate_of_aggregate_of_type(obj, get_python_type())) { + auto v = python_sequence_as_vector_of_vector(obj); + if (arg_type == IfcUtil::Argument_AGGREGATE_OF_AGGREGATE_OF_INT) { + self->i()->set_value(i, v); + } else { + throw IfcParse::IfcException("Attribute not set"); + } + } + + else if (check_aggregate_of_aggregate_of_type(obj, get_python_type())) { + auto v = python_sequence_as_vector_of_vector(obj); + if (arg_type == IfcUtil::Argument_AGGREGATE_OF_AGGREGATE_OF_DOUBLE) { + self->i()->set_value(i, v); + } else { + throw IfcParse::IfcException("Attribute not set"); + } + } + + else if (PySequence_Check(obj)) { + aggregate_of_instance::ptr insts1d(new aggregate_of_instance); + aggregate_of_aggregate_of_instance::ptr insts2d(new aggregate_of_aggregate_of_instance); + for(Py_ssize_t i = 0; i < PySequence_Size(obj); ++i) { + PyObject* element_i = PySequence_GetItem(obj, i); + if (auto inst = cast_pyobject(element_i); insts1d) { + insts2d.reset(); + + insts1d->push(*inst); + } else if (PySequence_Check(element_i) && insts2d) { + insts1d.reset(); + + std::vector inner; + for(Py_ssize_t j = 0; j < PySequence_Size(element_i); ++j) { + PyObject* element_j = PySequence_GetItem(element_i, j); + if (auto inst = cast_pyobject(element_j); insts2d) { + inner.push_back(*inst); + } else { + insts2d.reset(); + } + Py_DECREF(element_j); + if (!insts2d) { + break; + } + } + if (insts2d) { + insts2d->push(inner); + } + } else { + insts1d.reset(); + insts2d.reset(); + } + Py_DECREF(element_i); + if (!insts1d && !insts2d) { + break; + } + } + if (arg_type == IfcUtil::Argument_AGGREGATE_OF_ENTITY_INSTANCE && insts1d) { + self->i()->set_value(i, insts1d); + } else if (arg_type == IfcUtil::Argument_AGGREGATE_OF_AGGREGATE_OF_ENTITY_INSTANCE && insts2d) { + self->i()->set_value(i, insts2d); + } else { + throw IfcParse::IfcException("Attribute not set"); + } } } } +%extend entity_instance { + %pythoncode %{ + def __getattr__(self, name): + try: + return custom_base.__getattr__(self, name) + except: + return _swig_getattr(self, entity_instance, name) + def __setattr__(self, name, value): + try: + return custom_base.__setattr__(self, name, value) + except: + return _swig_setattr(self, entity_instance, name, value) + %} +}; + %extend IfcParse::IfcSpfHeader { %pythoncode %{ # Hide the getters with read-only property implementations @@ -696,7 +843,7 @@ IfcUtil::ArgumentType helper_fn_attribute_type(const entity_instance* inst, unsi return IFCOPENSHELL_VERSION; } - entity_instance new_IfcBaseClass(const std::string& schema_identifier, const std::string& name) { + entity_instance* make_instance(const std::string& schema_identifier, const std::string& name) { const IfcParse::schema_definition* schema = IfcParse::schema_by_name(schema_identifier); const IfcParse::declaration* decl = schema->declaration_by_name(name); IfcEntityInstanceData* data = new IfcEntityInstanceData(decl); @@ -719,7 +866,7 @@ IfcUtil::ArgumentType helper_fn_attribute_type(const entity_instance* inst, unsi } } - return schema->instantiate(data); + return new entity_instance(schema->instantiate(data)); } %} diff --git a/src/ifcwrap/IfcPython.i b/src/ifcwrap/IfcPython.i index 8ef186ab8d..27866d8b0f 100644 --- a/src/ifcwrap/IfcPython.i +++ b/src/ifcwrap/IfcPython.i @@ -149,12 +149,6 @@ // Create docstrings for generated python code. %feature("autodoc", "1"); -%include "utils/type_conversion.i" - -%include "utils/typemaps_in.i" - -%include "utils/typemaps_out.i" - %module ifcopenshell_wrapper %{ #include "../ifcgeom/Converter.h" #include "../ifcgeom/taxonomy.h" diff --git a/src/ifcwrap/utils/type_conversion.i b/src/ifcwrap/utils/type_conversion.i index d6d0912a55..642d6bb9be 100644 --- a/src/ifcwrap/utils/type_conversion.i +++ b/src/ifcwrap/utils/type_conversion.i @@ -79,10 +79,10 @@ } template <> - IfcUtil::IfcBaseClass* cast_pyobject(PyObject* element) { + entity_instance* cast_pyobject(PyObject* element) { void *arg = 0; - int res = SWIG_ConvertPtr(element, &arg, SWIGTYPE_p_IfcUtil__IfcBaseClass, 0); - return static_cast(SWIG_IsOK(res) ? arg : 0); + int res = SWIG_ConvertPtr(element, &arg, SWIGTYPE_p_entity_instance, 0); + return static_cast(SWIG_IsOK(res) ? arg : 0); } template @@ -156,6 +156,7 @@ PyObject* pythonize(const double& t) { return PyFloat_FromDouble(t); } PyObject* pythonize(const std::string& t) { return PyUnicode_FromString(t.c_str()); } PyObject* pythonize(const IfcUtil::IfcBaseClass* t) { return SWIG_NewPointerObj(SWIG_as_voidptr(t), SWIGTYPE_p_IfcUtil__IfcBaseClass, 0); } + PyObject* pythonize(const entity_instance* t) { return SWIG_NewPointerObj(SWIG_as_voidptr(t), SWIGTYPE_p_entity_instance, SWIG_POINTER_OWN);} PyObject* pythonize(const IfcParse::attribute* t) { return SWIG_NewPointerObj(SWIG_as_voidptr(t), SWIGTYPE_p_IfcParse__attribute, 0); } PyObject* pythonize(const IfcParse::inverse_attribute* t) { return SWIG_NewPointerObj(SWIG_as_voidptr(t), SWIGTYPE_p_IfcParse__inverse_attribute, 0); } PyObject* pythonize(const IfcParse::entity* t) { return SWIG_NewPointerObj(SWIG_as_voidptr(t), SWIGTYPE_p_IfcParse__entity, 0); } diff --git a/src/ifcwrap/utils/typemaps_out.i b/src/ifcwrap/utils/typemaps_out.i index 63d5e72c30..2659486296 100644 --- a/src/ifcwrap/utils/typemaps_out.i +++ b/src/ifcwrap/utils/typemaps_out.i @@ -23,13 +23,17 @@ $result = SWIG_Python_str_FromChar(data_type_strings[(int)$1]); } -%typemap(out) std::pair { +%typemap(out) std::tuple { // The SWIG %exception directive does not take care // of our typemap. So the attribute conversion block // is wrapped in a try-catch block manually. try { - const Argument& arg = *($1.second); - const IfcUtil::ArgumentType type = $1.first; + + std::tuple& res = $1; + const Argument& arg = *std::get(res); + const IfcUtil::ArgumentType type = std::get(res); + entity_instance* host = std::get(res); + if (arg.isNull()) { Py_INCREF(Py_None); $result = Py_None; @@ -81,7 +85,10 @@ break; } case IfcUtil::Argument_ENTITY_INSTANCE: { IfcUtil::IfcBaseClass* v = arg; - $result = pythonize(v); + if (!v->data().file) { + throw std::runtime_error("No file"); + } + $result = pythonize(new entity_instance(v->data().file->instance_by_id_2(v->data().id()), host->file_pointer())); break; } case IfcUtil::Argument_AGGREGATE_OF_ENTITY_INSTANCE: { aggregate_of_instance::ptr v = arg;