Skip to content

Commit b96cf38

Browse files
committed
Minimize passing STL objects past dynamic library boundaries to improve stability of IfcOpenShell-Python
1 parent 5a994eb commit b96cf38

4 files changed

Lines changed: 138 additions & 32 deletions

File tree

src/ifcopenshell-python/ifcopenshell/__init__.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -43,9 +43,11 @@ class entity_instance(object):
4343
def __init__(self, e):
4444
super(entity_instance, self).__setattr__('wrapped_data', e)
4545
def __getattr__(self, name):
46-
if name in self.wrapped_data.get_attribute_names():
46+
INVALID, FORWARD, INVERSE = range(3)
47+
attr_cat = self.wrapped_data.get_attribute_category(name)
48+
if attr_cat == FORWARD:
4749
return entity_instance.wrap_value(self.wrapped_data.get_argument(self.wrapped_data.get_argument_index(name)))
48-
elif name in self.wrapped_data.get_inverse_attribute_names():
50+
elif attr_cat == INVERSE:
4951
return entity_instance.wrap_value(self.wrapped_data.get_inverse(name))
5052
else: raise AttributeError("entity instance of type '%s' has no attribute '%s'"%(self.wrapped_data.is_a(), name))
5153
@staticmethod

src/ifcwrap/IfcGeomWrapper.i

Lines changed: 73 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,47 @@
9393
%ignore IfcGeom::Iterator<double>::Iterator(const IfcGeom::IteratorSettings&, void*, int);
9494
%ignore IfcGeom::Iterator<double>::Iterator(const IfcGeom::IteratorSettings&, std::istream&, int);
9595

96+
// Ignore the std::vector accessors and replace them to pairs that will
97+
// be expanded to Python tuples by means of typemaps. This in order to
98+
// minimize passing STL objects across dynamic library boundaries.
99+
%ignore IfcGeom::Representation::Triangulation::verts;
100+
%ignore IfcGeom::Representation::Triangulation::faces;
101+
%ignore IfcGeom::Representation::Triangulation::edges;
102+
%ignore IfcGeom::Representation::Triangulation::normals;
103+
%ignore IfcGeom::Representation::Triangulation::material_ids;
104+
%ignore IfcGeom::Representation::Triangulation::materials;
105+
%extend IfcGeom::Representation::Triangulation {
106+
std::pair<const int*, unsigned> get_faces() {
107+
return std::make_pair(&$self->faces()[0], $self->faces().size());
108+
}
109+
std::pair<const int*, unsigned> get_edges() {
110+
return std::make_pair(&$self->edges()[0], $self->edges().size());
111+
}
112+
std::pair<const int*, unsigned> get_material_ids() {
113+
return std::make_pair(&$self->material_ids()[0], $self->material_ids().size());
114+
}
115+
std::pair<const IfcGeom::Material*, unsigned> get_materials() {
116+
return std::make_pair(&$self->materials()[0], $self->materials().size());
117+
}
118+
}
119+
%extend IfcGeom::Representation::Triangulation<float> {
120+
std::pair<const float*, unsigned> get_verts() {
121+
return std::make_pair(&$self->verts()[0], $self->verts().size());
122+
}
123+
std::pair<const float*, unsigned> get_normals() {
124+
return std::make_pair(&$self->normals()[0], $self->normals().size());
125+
}
126+
}
127+
%extend IfcGeom::Representation::Triangulation<double> {
128+
std::pair<const double*, unsigned> get_verts() {
129+
return std::make_pair(&$self->verts()[0], $self->verts().size());
130+
}
131+
std::pair<const double*, unsigned> get_normals() {
132+
return std::make_pair(&$self->normals()[0], $self->normals().size());
133+
}
134+
}
135+
136+
96137
%extend IfcGeom::IteratorSettings {
97138
%pythoncode %{
98139
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 @@
114155
};
115156

116157
%extend IfcGeom::Representation::Triangulation {
117-
%pythoncode %{
158+
%pythoncode %{
118159
if _newclass:
119160
# Hide the getters with read-only property implementations
120161
id = property(id)
121-
verts = property(verts)
122-
faces = property(faces)
123-
edges = property(edges)
124-
normals = property(normals)
125-
material_ids = property(material_ids)
126-
materials = property(materials)
127-
%}
162+
faces = property(get_faces)
163+
edges = property(get_edges)
164+
material_ids = property(get_material_ids)
165+
materials = property(get_materials)
166+
%}
167+
};
168+
169+
// Specialized accessors follow later, for otherwise property definitions
170+
// would appear before templated getter functions are defined.
171+
%extend IfcGeom::Representation::Triangulation<float> {
172+
%pythoncode %{
173+
if _newclass:
174+
# Hide the getters with read-only property implementations
175+
verts = property(get_verts)
176+
normals = property(get_normals)
177+
%}
178+
};
179+
%extend IfcGeom::Representation::Triangulation<double> {
180+
%pythoncode %{
181+
if _newclass:
182+
# Hide the getters with read-only property implementations
183+
verts = property(get_verts)
184+
normals = property(get_normals)
185+
%}
128186
};
129187

130188
%extend IfcGeom::Representation::Serialization {
@@ -133,7 +191,7 @@
133191
# Hide the getters with read-only property implementations
134192
id = property(id)
135193
brep_data = property(brep_data)
136-
%}
194+
%}
137195
};
138196

139197
%extend IfcGeom::Element {
@@ -148,23 +206,23 @@
148206
context = property(context)
149207
unique_id = property(unique_id)
150208
transformation = property(transformation)
151-
%}
209+
%}
152210
};
153211

154212
%extend IfcGeom::TriangulationElement {
155213
%pythoncode %{
156214
if _newclass:
157215
# Hide the getters with read-only property implementations
158216
geometry = property(geometry)
159-
%}
217+
%}
160218
};
161219

162220
%extend IfcGeom::SerializedElement {
163221
%pythoncode %{
164222
if _newclass:
165223
# Hide the getters with read-only property implementations
166224
geometry = property(geometry)
167-
%}
225+
%}
168226
};
169227

170228
%extend IfcGeom::Material {
@@ -180,23 +238,23 @@
180238
transparency = property(transparency)
181239
specularity = property(specularity)
182240
name = property(name)
183-
%}
241+
%}
184242
};
185243

186244
%extend IfcGeom::Transformation {
187245
%pythoncode %{
188246
if _newclass:
189247
# Hide the getters with read-only property implementations
190248
matrix = property(matrix)
191-
%}
249+
%}
192250
};
193251

194252
%extend IfcGeom::Matrix {
195253
%pythoncode %{
196254
if _newclass:
197255
# Hide the getters with read-only property implementations
198256
data = property(data)
199-
%}
257+
%}
200258
};
201259

202260
%inline %{

src/ifcwrap/IfcParseWrapper.i

Lines changed: 20 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -216,8 +216,8 @@ namespace IfcUtil {
216216
std::ofstream f(fn.c_str());
217217
f << (*$self);
218218
}
219-
std::vector<int> entity_names() const {
220-
std::vector<int> keys;
219+
std::vector<unsigned> entity_names() const {
220+
std::vector<unsigned> keys;
221221
keys.reserve(std::distance($self->begin(), $self->end()));
222222
for (IfcParse::IfcFile::entity_by_id_t::const_iterator it = $self->begin(); it != $self->end(); ++ it) {
223223
keys.push_back(it->first);
@@ -228,10 +228,23 @@ namespace IfcUtil {
228228
if _newclass:
229229
# Hide the getters with read-only property implementations
230230
header = property(header)
231-
%}
231+
%}
232232
}
233233

234234
%extend IfcParse::IfcLateBoundEntity {
235+
int get_attribute_category(const std::string& name) const {
236+
const std::vector<std::string> names = $self->getAttributeNames();
237+
if (std::find(names.begin(), names.end(), name) != names.end()) {
238+
return 1;
239+
} else {
240+
const std::vector<std::string> names = $self->getInverseAttributeNames();
241+
if (std::find(names.begin(), names.end(), name) != names.end()) {
242+
return 2;
243+
} else {
244+
return 0;
245+
}
246+
}
247+
}
235248
%pythoncode %{
236249
set_argument = lambda self,x,y: self._set_argument(x) if y is None else self._set_argument(x,y)
237250
%}
@@ -244,7 +257,7 @@ namespace IfcUtil {
244257
file_description = property(file_description)
245258
file_name = property(file_name)
246259
file_schema = property(file_schema)
247-
%}
260+
%}
248261
};
249262

250263
%extend IfcParse::FileDescription {
@@ -257,7 +270,7 @@ namespace IfcUtil {
257270
__swig_getmethods__["implementation_level"] = implementation_level
258271
__swig_setmethods__["implementation_level"] = implementation_level
259272
implementation_level = property(implementation_level, implementation_level)
260-
%}
273+
%}
261274
};
262275

263276
%extend IfcParse::FileName {
@@ -285,7 +298,7 @@ namespace IfcUtil {
285298
__swig_getmethods__["authorization"] = authorization
286299
__swig_setmethods__["authorization"] = authorization
287300
authorization = property(authorization, authorization)
288-
%}
301+
%}
289302
};
290303

291304
%extend IfcParse::FileSchema {
@@ -295,7 +308,7 @@ namespace IfcUtil {
295308
__swig_getmethods__["schema_identifiers"] = schema_identifiers
296309
__swig_setmethods__["schema_identifiers"] = schema_identifiers
297310
schema_identifiers = property(schema_identifiers, schema_identifiers)
298-
%}
311+
%}
299312
};
300313

301314
%include "../ifcparse/IfcSpfHeader.h"

src/ifcwrap/IfcPython.i

Lines changed: 41 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -41,13 +41,46 @@
4141
#include "../ifcparse/IfcLateBoundEntity.h"
4242
%}
4343

44+
// The following typemaps are an alternative for the read-only std::vector
45+
// implementations provided by SWIG, as passing STL objects across dynamic
46+
// library boundaries can be problematic.
47+
%typemap(out) std::pair<const float*, unsigned> {
48+
$result = PyTuple_New($1.second);
49+
for (unsigned i = 0; i < $1.second; ++i) {
50+
PyTuple_SetItem($result, i, PyFloat_FromDouble($1.first[i]));
51+
}
52+
}
53+
%typemap(out) std::pair<const double*, unsigned> {
54+
$result = PyTuple_New($1.second);
55+
for (unsigned i = 0; i < $1.second; ++i) {
56+
PyTuple_SetItem($result, i, PyFloat_FromDouble($1.first[i]));
57+
}
58+
}
59+
%typemap(out) std::pair<const int*, unsigned> {
60+
$result = PyTuple_New($1.second);
61+
for (unsigned i = 0; i < $1.second; ++i) {
62+
PyTuple_SetItem($result, i, PyLong_FromLong($1.first[i]));
63+
}
64+
}
65+
%typemap(out) std::pair<const std::string*, unsigned> {
66+
$result = PyTuple_New($1.second);
67+
for (unsigned i = 0; i < $1.second; ++i) {
68+
PyTuple_SetItem($result, i, PyUnicode_FromString($1.first[i]->c_str()));
69+
}
70+
}
71+
%typemap(out) std::pair<const IfcGeom::Material*, unsigned> {
72+
$result = PyTuple_New($1.second);
73+
for (unsigned i = 0; i < $1.second; ++i) {
74+
PyTuple_SetItem($result, i, SWIG_NewPointerObj(SWIG_as_voidptr(&$1.first[i]), SWIGTYPE_p_IfcGeom__Material, 0));
75+
}
76+
}
77+
%typemap(out) std::vector<unsigned> {
78+
$result = PyTuple_New($1.size());
79+
unsigned i = 0;
80+
for (std::vector<unsigned>::const_iterator it = $1.begin(); it != $1.end(); ++it) {
81+
PyTuple_SetItem($result, i++, PyLong_FromLong(*it));
82+
}
83+
}
84+
4485
%include "IfcGeomWrapper.i"
4586
%include "IfcParseWrapper.i"
46-
47-
namespace std {
48-
%template(int_vector) vector<int>;
49-
%template(float_vector) vector<float>;
50-
%template(double_vector) vector<double>;
51-
%template(string_vector) vector<std::string>;
52-
%template(material_vector) vector<IfcGeom::Material>;
53-
};

0 commit comments

Comments
 (0)