From b96cf38e1729761e1065c8a83748584e89128613 Mon Sep 17 00:00:00 2001 From: Thomas Krijnen Date: Tue, 2 Jun 2015 13:29:08 +0000 Subject: [PATCH] Minimize passing STL objects past dynamic library boundaries to improve stability of IfcOpenShell-Python --- .../ifcopenshell/__init__.py | 6 +- src/ifcwrap/IfcGeomWrapper.i | 88 +++++++++++++++---- src/ifcwrap/IfcParseWrapper.i | 27 ++++-- src/ifcwrap/IfcPython.i | 49 +++++++++-- 4 files changed, 138 insertions(+), 32 deletions(-) diff --git a/src/ifcopenshell-python/ifcopenshell/__init__.py b/src/ifcopenshell-python/ifcopenshell/__init__.py index 6cd22a9b6c..40918b2771 100644 --- a/src/ifcopenshell-python/ifcopenshell/__init__.py +++ b/src/ifcopenshell-python/ifcopenshell/__init__.py @@ -43,9 +43,11 @@ class entity_instance(object): def __init__(self, e): super(entity_instance, self).__setattr__('wrapped_data', e) def __getattr__(self, name): - if name in self.wrapped_data.get_attribute_names(): + INVALID, FORWARD, INVERSE = range(3) + attr_cat = self.wrapped_data.get_attribute_category(name) + if attr_cat == FORWARD: return entity_instance.wrap_value(self.wrapped_data.get_argument(self.wrapped_data.get_argument_index(name))) - elif name in self.wrapped_data.get_inverse_attribute_names(): + elif attr_cat == INVERSE: return entity_instance.wrap_value(self.wrapped_data.get_inverse(name)) else: raise AttributeError("entity instance of type '%s' has no attribute '%s'"%(self.wrapped_data.is_a(), name)) @staticmethod diff --git a/src/ifcwrap/IfcGeomWrapper.i b/src/ifcwrap/IfcGeomWrapper.i index 666ee342ef..3b669d069b 100644 --- a/src/ifcwrap/IfcGeomWrapper.i +++ b/src/ifcwrap/IfcGeomWrapper.i @@ -93,6 +93,47 @@ %ignore IfcGeom::Iterator::Iterator(const IfcGeom::IteratorSettings&, void*, int); %ignore IfcGeom::Iterator::Iterator(const IfcGeom::IteratorSettings&, std::istream&, int); +// Ignore the std::vector accessors and replace them to pairs that will +// be expanded to Python tuples by means of typemaps. This in order to +// minimize passing STL objects across dynamic library boundaries. +%ignore IfcGeom::Representation::Triangulation::verts; +%ignore IfcGeom::Representation::Triangulation::faces; +%ignore IfcGeom::Representation::Triangulation::edges; +%ignore IfcGeom::Representation::Triangulation::normals; +%ignore IfcGeom::Representation::Triangulation::material_ids; +%ignore IfcGeom::Representation::Triangulation::materials; +%extend IfcGeom::Representation::Triangulation { + std::pair get_faces() { + return std::make_pair(&$self->faces()[0], $self->faces().size()); + } + std::pair get_edges() { + return std::make_pair(&$self->edges()[0], $self->edges().size()); + } + std::pair get_material_ids() { + return std::make_pair(&$self->material_ids()[0], $self->material_ids().size()); + } + std::pair get_materials() { + return std::make_pair(&$self->materials()[0], $self->materials().size()); + } +} +%extend IfcGeom::Representation::Triangulation { + std::pair get_verts() { + return std::make_pair(&$self->verts()[0], $self->verts().size()); + } + std::pair get_normals() { + return std::make_pair(&$self->normals()[0], $self->normals().size()); + } +} +%extend IfcGeom::Representation::Triangulation { + std::pair get_verts() { + return std::make_pair(&$self->verts()[0], $self->verts().size()); + } + std::pair get_normals() { + return std::make_pair(&$self->normals()[0], $self->normals().size()); + } +} + + %extend IfcGeom::IteratorSettings { %pythoncode %{ attrs = ("convert_back_units", "deflection_tolerance", "disable_opening_subtractions", "disable_triangulation", "faster_booleans", "sew_shells", "use_brep_data", "use_world_coords", "weld_vertices") @@ -114,17 +155,34 @@ }; %extend IfcGeom::Representation::Triangulation { - %pythoncode %{ + %pythoncode %{ if _newclass: # Hide the getters with read-only property implementations id = property(id) - verts = property(verts) - faces = property(faces) - edges = property(edges) - normals = property(normals) - material_ids = property(material_ids) - materials = property(materials) - %} + faces = property(get_faces) + edges = property(get_edges) + material_ids = property(get_material_ids) + materials = property(get_materials) + %} +}; + +// Specialized accessors follow later, for otherwise property definitions +// would appear before templated getter functions are defined. +%extend IfcGeom::Representation::Triangulation { + %pythoncode %{ + if _newclass: + # Hide the getters with read-only property implementations + verts = property(get_verts) + normals = property(get_normals) + %} +}; +%extend IfcGeom::Representation::Triangulation { + %pythoncode %{ + if _newclass: + # Hide the getters with read-only property implementations + verts = property(get_verts) + normals = property(get_normals) + %} }; %extend IfcGeom::Representation::Serialization { @@ -133,7 +191,7 @@ # Hide the getters with read-only property implementations id = property(id) brep_data = property(brep_data) - %} + %} }; %extend IfcGeom::Element { @@ -148,7 +206,7 @@ context = property(context) unique_id = property(unique_id) transformation = property(transformation) - %} + %} }; %extend IfcGeom::TriangulationElement { @@ -156,7 +214,7 @@ if _newclass: # Hide the getters with read-only property implementations geometry = property(geometry) - %} + %} }; %extend IfcGeom::SerializedElement { @@ -164,7 +222,7 @@ if _newclass: # Hide the getters with read-only property implementations geometry = property(geometry) - %} + %} }; %extend IfcGeom::Material { @@ -180,7 +238,7 @@ transparency = property(transparency) specularity = property(specularity) name = property(name) - %} + %} }; %extend IfcGeom::Transformation { @@ -188,7 +246,7 @@ if _newclass: # Hide the getters with read-only property implementations matrix = property(matrix) - %} + %} }; %extend IfcGeom::Matrix { @@ -196,7 +254,7 @@ if _newclass: # Hide the getters with read-only property implementations data = property(data) - %} + %} }; %inline %{ diff --git a/src/ifcwrap/IfcParseWrapper.i b/src/ifcwrap/IfcParseWrapper.i index cb719eb512..3219e99093 100644 --- a/src/ifcwrap/IfcParseWrapper.i +++ b/src/ifcwrap/IfcParseWrapper.i @@ -216,8 +216,8 @@ namespace IfcUtil { std::ofstream f(fn.c_str()); f << (*$self); } - std::vector entity_names() const { - std::vector keys; + std::vector entity_names() const { + 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.push_back(it->first); @@ -228,10 +228,23 @@ namespace IfcUtil { if _newclass: # Hide the getters with read-only property implementations header = property(header) - %} + %} } %extend IfcParse::IfcLateBoundEntity { + int get_attribute_category(const std::string& name) const { + const std::vector names = $self->getAttributeNames(); + if (std::find(names.begin(), names.end(), name) != names.end()) { + return 1; + } else { + const std::vector names = $self->getInverseAttributeNames(); + if (std::find(names.begin(), names.end(), name) != names.end()) { + return 2; + } else { + return 0; + } + } + } %pythoncode %{ set_argument = lambda self,x,y: self._set_argument(x) if y is None else self._set_argument(x,y) %} @@ -244,7 +257,7 @@ namespace IfcUtil { file_description = property(file_description) file_name = property(file_name) file_schema = property(file_schema) - %} + %} }; %extend IfcParse::FileDescription { @@ -257,7 +270,7 @@ namespace IfcUtil { __swig_getmethods__["implementation_level"] = implementation_level __swig_setmethods__["implementation_level"] = implementation_level implementation_level = property(implementation_level, implementation_level) - %} + %} }; %extend IfcParse::FileName { @@ -285,7 +298,7 @@ namespace IfcUtil { __swig_getmethods__["authorization"] = authorization __swig_setmethods__["authorization"] = authorization authorization = property(authorization, authorization) - %} + %} }; %extend IfcParse::FileSchema { @@ -295,7 +308,7 @@ namespace IfcUtil { __swig_getmethods__["schema_identifiers"] = schema_identifiers __swig_setmethods__["schema_identifiers"] = schema_identifiers schema_identifiers = property(schema_identifiers, schema_identifiers) - %} + %} }; %include "../ifcparse/IfcSpfHeader.h" diff --git a/src/ifcwrap/IfcPython.i b/src/ifcwrap/IfcPython.i index 76b9d072bc..dce1433441 100644 --- a/src/ifcwrap/IfcPython.i +++ b/src/ifcwrap/IfcPython.i @@ -41,13 +41,46 @@ #include "../ifcparse/IfcLateBoundEntity.h" %} +// The following typemaps are an alternative for the read-only std::vector +// implementations provided by SWIG, as passing STL objects across dynamic +// library boundaries can be problematic. +%typemap(out) std::pair { + $result = PyTuple_New($1.second); + for (unsigned i = 0; i < $1.second; ++i) { + PyTuple_SetItem($result, i, PyFloat_FromDouble($1.first[i])); + } +} +%typemap(out) std::pair { + $result = PyTuple_New($1.second); + for (unsigned i = 0; i < $1.second; ++i) { + PyTuple_SetItem($result, i, PyFloat_FromDouble($1.first[i])); + } +} +%typemap(out) std::pair { + $result = PyTuple_New($1.second); + for (unsigned i = 0; i < $1.second; ++i) { + PyTuple_SetItem($result, i, PyLong_FromLong($1.first[i])); + } +} +%typemap(out) std::pair { + $result = PyTuple_New($1.second); + for (unsigned i = 0; i < $1.second; ++i) { + PyTuple_SetItem($result, i, PyUnicode_FromString($1.first[i]->c_str())); + } +} +%typemap(out) std::pair { + $result = PyTuple_New($1.second); + for (unsigned i = 0; i < $1.second; ++i) { + PyTuple_SetItem($result, i, SWIG_NewPointerObj(SWIG_as_voidptr(&$1.first[i]), SWIGTYPE_p_IfcGeom__Material, 0)); + } +} +%typemap(out) std::vector { + $result = PyTuple_New($1.size()); + unsigned i = 0; + for (std::vector::const_iterator it = $1.begin(); it != $1.end(); ++it) { + PyTuple_SetItem($result, i++, PyLong_FromLong(*it)); + } +} + %include "IfcGeomWrapper.i" %include "IfcParseWrapper.i" - -namespace std { - %template(int_vector) vector; - %template(float_vector) vector; - %template(double_vector) vector; - %template(string_vector) vector; - %template(material_vector) vector; -}; \ No newline at end of file