Skip to content

Commit 48676a7

Browse files
committed
Rework SWIG python binding not to rely on polymorhpic functions. Use hand-written typemaps that map vectors to tuples and iterables to vectors. Add support for multidimensional aggregates in Python.
1 parent f8c45b2 commit 48676a7

File tree

10 files changed

+454
-176
lines changed

10 files changed

+454
-176
lines changed

src/ifcopenshell-python/ifcopenshell/__init__.py

Lines changed: 9 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -51,22 +51,15 @@ def __getattr__(self, name):
5151
return entity_instance.wrap_value(self.wrapped_data.get_inverse(name))
5252
else: raise AttributeError("entity instance of type '%s' has no attribute '%s'"%(self.wrapped_data.is_a(), name))
5353
@staticmethod
54-
def map_value(v):
55-
if isinstance(v, entity_instance): return v.wrapped_data
56-
elif isinstance(v, (tuple, list)) and len(v):
57-
classes = list(map(type, v))
58-
if float in classes: return ifcopenshell_wrapper.double_vector(v)
59-
elif int in classes: return ifcopenshell_wrapper.int_vector(v)
60-
elif str in classes: return ifcopenshell_wrapper.string_vector(v)
61-
elif entity_instance in classes: return list(map(lambda e: e.wrapped_data, v))
62-
return v
63-
@staticmethod
6454
def wrap_value(v):
6555
wrap = lambda e: entity_instance(e)
6656
if isinstance(v, ifcopenshell_wrapper.entity_instance): return wrap(v)
6757
elif isinstance(v, (tuple, list)) and len(v):
68-
classes = list(map(type, v))
69-
if ifcopenshell_wrapper.entity_instance in classes: return list(map(wrap, v))
58+
classes = set(map(type, v))
59+
if {ifcopenshell_wrapper.entity_instance} == classes: return list(map(wrap, v))
60+
elif {list} == classes:
61+
classes = set(map(type, v[0]))
62+
if {ifcopenshell_wrapper.entity_instance} == classes: return [list(map(wrap, x)) for x in v]
7063
return v
7164
def attribute_type(self, attr):
7265
attr_idx = attr if isinstance(attr, int) else self.wrapped_data.get_argument_index(attr)
@@ -78,7 +71,10 @@ def __setattr__(self, key, value):
7871
def __getitem__(self, key):
7972
return entity_instance.wrap_value(self.wrapped_data.get_argument(key))
8073
def __setitem__(self, idx, value):
81-
self.wrapped_data.set_argument(idx, entity_instance.map_value(value))
74+
attr_type = self.attribute_type(idx).title().replace(' ', '')
75+
attr_type = attr_type.replace('Binary', 'String')
76+
attr_type = attr_type.replace('Enumeration', 'String')
77+
getattr(self.wrapped_data, "setArgumentAs%s" % attr_type)(idx, value)
8278
def __len__(self): return len(self.wrapped_data)
8379
def __repr__(self): return repr(self.wrapped_data)
8480
def is_a(self, *args): return self.wrapped_data.is_a(*args)

src/ifcparse/IfcLateBoundEntity.cpp

Lines changed: 37 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -110,86 +110,106 @@ void IfcParse::IfcLateBoundEntity::invalid_argument(unsigned int i, const std::s
110110
const std::string arg_name = IfcSchema::Type::GetAttributeName(_type,i);
111111
throw IfcException(t + " is not a valid type for '" + arg_name + "'");
112112
}
113-
void IfcParse::IfcLateBoundEntity::setArgument(unsigned int i) {
113+
void IfcParse::IfcLateBoundEntity::setArgumentAsNull(unsigned int i) {
114114
bool is_optional = IfcSchema::Type::GetAttributeOptional(_type, i);
115115
if (is_optional) {
116116
writable_entity()->setArgument(i);
117117
} else invalid_argument(i,"NULL");
118118
}
119-
void IfcParse::IfcLateBoundEntity::setArgument(unsigned int i, int v) {
119+
void IfcParse::IfcLateBoundEntity::setArgumentAsInt(unsigned int i, int v) {
120120
IfcUtil::ArgumentType arg_type = IfcSchema::Type::GetAttributeType(_type,i);
121121
if (arg_type == Argument_INT) {
122122
writable_entity()->setArgument(i,v);
123123
} else if ( (arg_type == Argument_BOOL) && ( (v == 0) || (v == 1) ) ) {
124124
writable_entity()->setArgument(i, v == 1);
125125
} else invalid_argument(i,"INTEGER");
126126
}
127-
void IfcParse::IfcLateBoundEntity::setArgument(unsigned int i, bool v) {
127+
void IfcParse::IfcLateBoundEntity::setArgumentAsBool(unsigned int i, bool v) {
128128
IfcUtil::ArgumentType arg_type = IfcSchema::Type::GetAttributeType(_type,i);
129129
if (arg_type == Argument_BOOL) {
130130
writable_entity()->setArgument(i,v);
131131
} else invalid_argument(i,"BOOLEAN");
132132
}
133-
void IfcParse::IfcLateBoundEntity::setArgument(unsigned int i, double v) {
133+
void IfcParse::IfcLateBoundEntity::setArgumentAsDouble(unsigned int i, double v) {
134134
IfcUtil::ArgumentType arg_type = IfcSchema::Type::GetAttributeType(_type,i);
135135
if (arg_type == Argument_DOUBLE) {
136136
writable_entity()->setArgument(i,v);
137137
} else invalid_argument(i,"REAL");
138138
}
139-
void IfcParse::IfcLateBoundEntity::setArgument(unsigned int i, const std::string& a) {
139+
void IfcParse::IfcLateBoundEntity::setArgumentAsString(unsigned int i, const std::string& a) {
140140
IfcUtil::ArgumentType arg_type = IfcSchema::Type::GetAttributeType(_type,i);
141141
if (arg_type == Argument_STRING) {
142142
writable_entity()->setArgument(i,a);
143143
} else if (arg_type == Argument_ENUMERATION) {
144144
std::pair<const char*, int> enum_data = IfcSchema::Type::GetEnumerationIndex(IfcSchema::Type::GetAttributeEntity(_type, i), a);
145145
writable_entity()->setArgument(i, enum_data.second, enum_data.first);
146146
} else if (arg_type == Argument_BINARY) {
147-
boost::dynamic_bitset<> bits(a);
148-
writable_entity()->setArgument(i, bits);
147+
if (valid_binary_string(a)) {
148+
boost::dynamic_bitset<> bits(a);
149+
writable_entity()->setArgument(i, bits);
150+
} else {
151+
throw IfcException("String not a valid binary representation");
152+
}
149153
} else invalid_argument(i,"STRING");
150154
}
151-
void IfcParse::IfcLateBoundEntity::setArgument(unsigned int i, const std::vector<int>& v) {
155+
void IfcParse::IfcLateBoundEntity::setArgumentAsAggregateOfInt(unsigned int i, const std::vector<int>& v) {
152156
IfcUtil::ArgumentType arg_type = IfcSchema::Type::GetAttributeType(_type,i);
153157
if (arg_type == Argument_AGGREGATE_OF_INT) {
154158
writable_entity()->setArgument(i,v);
155159
} else invalid_argument(i,"AGGREGATE OF INT");
156160
}
157-
void IfcParse::IfcLateBoundEntity::setArgument(unsigned int i, const std::vector<double>& v) {
161+
void IfcParse::IfcLateBoundEntity::setArgumentAsAggregateOfDouble(unsigned int i, const std::vector<double>& v) {
158162
IfcUtil::ArgumentType arg_type = IfcSchema::Type::GetAttributeType(_type,i);
159163
if (arg_type == Argument_AGGREGATE_OF_DOUBLE) {
160164
writable_entity()->setArgument(i,v);
161165
} else invalid_argument(i,"AGGREGATE OF DOUBLE");
162166
}
163-
void IfcParse::IfcLateBoundEntity::setArgument(unsigned int i, const std::vector<std::string>& v) {
167+
void IfcParse::IfcLateBoundEntity::setArgumentAsAggregateOfString(unsigned int i, const std::vector<std::string>& v) {
164168
IfcUtil::ArgumentType arg_type = IfcSchema::Type::GetAttributeType(_type,i);
165169
if (arg_type == Argument_AGGREGATE_OF_STRING) {
166170
writable_entity()->setArgument(i,v);
167171
} else if (arg_type == Argument_AGGREGATE_OF_BINARY) {
168172
std::vector< boost::dynamic_bitset<> > bits;
169173
bits.reserve(v.size());
170174
for (std::vector<std::string>::const_iterator it = v.begin(); it != v.end(); ++it) {
171-
bits.push_back(boost::dynamic_bitset<>(*it));
175+
if (valid_binary_string(*it)) {
176+
bits.push_back(boost::dynamic_bitset<>(*it));
177+
} else {
178+
throw IfcException("String not a valid binary representation");
179+
}
172180
}
173181
writable_entity()->setArgument(i, bits);
174182
} else invalid_argument(i,"AGGREGATE OF STRING");
175183
}
176-
void IfcParse::IfcLateBoundEntity::setArgument(unsigned int i, IfcParse::IfcLateBoundEntity* v) {
184+
void IfcParse::IfcLateBoundEntity::setArgumentAsEntityInstance(unsigned int i, IfcParse::IfcLateBoundEntity* v) {
177185
IfcUtil::ArgumentType arg_type = IfcSchema::Type::GetAttributeType(_type,i);
178186
if (arg_type == Argument_ENTITY_INSTANCE) {
179187
writable_entity()->setArgument(i,v);
180188
} else invalid_argument(i,"ENTITY INSTANCE");
181189
}
182-
void IfcParse::IfcLateBoundEntity::setArgument(unsigned int i, IfcEntityList::ptr v) {
190+
void IfcParse::IfcLateBoundEntity::setArgumentAsAggregateOfEntityInstance(unsigned int i, IfcEntityList::ptr v) {
183191
IfcUtil::ArgumentType arg_type = IfcSchema::Type::GetAttributeType(_type,i);
184192
if (arg_type == Argument_AGGREGATE_OF_ENTITY_INSTANCE) {
185193
writable_entity()->setArgument(i,v);
186194
} else invalid_argument(i,"AGGREGATE OF ENTITY INSTANCE");
187195
}
188-
std::pair<IfcUtil::ArgumentType,Argument*> IfcParse::IfcLateBoundEntity::get_argument(unsigned i) {
189-
return std::pair<IfcUtil::ArgumentType,Argument*>(getArgumentType(i),getArgument(i));
196+
void IfcParse::IfcLateBoundEntity::setArgumentAsAggregateOfAggregateOfInt(unsigned int i, const std::vector< std::vector<int> >& v) {
197+
IfcUtil::ArgumentType arg_type = IfcSchema::Type::GetAttributeType(_type,i);
198+
if (arg_type == Argument_AGGREGATE_OF_AGGREGATE_OF_INT) {
199+
writable_entity()->setArgument(i,v);
200+
} else invalid_argument(i,"AGGREGATE OF AGGREGATE OF INT");
190201
}
191-
std::pair<IfcUtil::ArgumentType,Argument*> IfcParse::IfcLateBoundEntity::get_argument(const std::string& a) {
192-
return get_argument(IfcSchema::Type::GetAttributeIndex(_type,a));
202+
void IfcParse::IfcLateBoundEntity::setArgumentAsAggregateOfAggregateOfDouble(unsigned int i, const std::vector< std::vector<double> >& v) {
203+
IfcUtil::ArgumentType arg_type = IfcSchema::Type::GetAttributeType(_type,i);
204+
if (arg_type == Argument_AGGREGATE_OF_AGGREGATE_OF_DOUBLE) {
205+
writable_entity()->setArgument(i,v);
206+
} else invalid_argument(i,"AGGREGATE OF AGGREGATE OF DOUBLE");
207+
}
208+
void IfcParse::IfcLateBoundEntity::setArgumentAsAggregateOfAggregateOfEntityInstance(unsigned int i, IfcEntityListList::ptr v) {
209+
IfcUtil::ArgumentType arg_type = IfcSchema::Type::GetAttributeType(_type,i);
210+
if (arg_type == Argument_AGGREGATE_OF_AGGREGATE_OF_ENTITY_INSTANCE) {
211+
writable_entity()->setArgument(i,v);
212+
} else invalid_argument(i,"AGGREGATE OF AGGREGATE OF ENTITY INSTANCE");
193213
}
194214
unsigned IfcParse::IfcLateBoundEntity::getArgumentIndex(const std::string& a) const {
195215
return IfcSchema::Type::GetAttributeIndex(_type,a);

src/ifcparse/IfcLateBoundEntity.h

Lines changed: 15 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -60,22 +60,23 @@ namespace IfcParse {
6060
std::vector<std::string> getAttributeNames() const;
6161
std::vector<std::string> getInverseAttributeNames() const;
6262

63-
void setArgument(unsigned int i);
64-
void setArgument(unsigned int i, int v);
65-
void setArgument(unsigned int i, bool v);
66-
void setArgument(unsigned int i, double v);
67-
void setArgument(unsigned int i, const std::string& v);
68-
void setArgument(unsigned int i, const std::vector<int>& v);
69-
void setArgument(unsigned int i, const std::vector<double>& v);
70-
void setArgument(unsigned int i, const std::vector<std::string>& v);
71-
void setArgument(unsigned int i, IfcLateBoundEntity* v);
72-
void setArgument(unsigned int i, IfcEntityList::ptr v);
63+
void setArgumentAsNull(unsigned int i);
64+
void setArgumentAsInt(unsigned int i, int v);
65+
void setArgumentAsBool(unsigned int i, bool v);
66+
void setArgumentAsDouble(unsigned int i, double v);
67+
void setArgumentAsString(unsigned int i, const std::string& v);
68+
void setArgumentAsEntityInstance(unsigned int i, IfcLateBoundEntity* v);
69+
70+
void setArgumentAsAggregateOfInt(unsigned int i, const std::vector<int>& v);
71+
void setArgumentAsAggregateOfDouble(unsigned int i, const std::vector<double>& v);
72+
void setArgumentAsAggregateOfString(unsigned int i, const std::vector<std::string>& v);
73+
void setArgumentAsAggregateOfEntityInstance(unsigned int i, IfcEntityList::ptr v);
74+
75+
void setArgumentAsAggregateOfAggregateOfInt(unsigned int i, const std::vector< std::vector<int> >& v);
76+
void setArgumentAsAggregateOfAggregateOfDouble(unsigned int i, const std::vector< std::vector<double> >& v);
77+
void setArgumentAsAggregateOfAggregateOfEntityInstance(unsigned int i, IfcEntityListList::ptr v);
7378

7479
std::string toString();
75-
76-
// TODO: Write as SWIG extension methods?
77-
std::pair<IfcUtil::ArgumentType,Argument*> get_argument(unsigned i);
78-
std::pair<IfcUtil::ArgumentType,Argument*> get_argument(const std::string& a);
7980

8081
bool is_valid();
8182
};

src/ifcparse/IfcUtil.cpp

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -137,3 +137,10 @@ static const char* const argument_type_string[] = {
137137
const char* IfcUtil::ArgumentTypeToString(ArgumentType argument_type) {
138138
return argument_type_string[static_cast<int>(argument_type)];
139139
}
140+
141+
bool IfcUtil::valid_binary_string(const std::string& s) {
142+
for (std::string::const_iterator it = s.begin(); it != s.end(); ++it) {
143+
if (*it != '0' && *it != '1') return false;
144+
}
145+
return true;
146+
}

src/ifcparse/IfcUtil.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,8 @@ namespace IfcUtil {
102102
const char* getArgumentName(unsigned int i) const;
103103
IfcSchema::Type::Enum getArgumentEntity(unsigned int i) const { return IfcSchema::Type::UNDEFINED; }
104104
};
105+
106+
bool valid_binary_string(const std::string& s);
105107
}
106108

107109
template <class T>

src/ifcwrap/IfcParseWrapper.i

Lines changed: 12 additions & 130 deletions
Original file line numberDiff line numberDiff line change
@@ -62,139 +62,15 @@ namespace IfcUtil {
6262
%rename("get_argument_optionality") getArgumentOptionality;
6363
%rename("get_attribute_names") getAttributeNames;
6464
%rename("get_inverse_attribute_names") getInverseAttributeNames;
65-
%rename("_set_argument") setArgument;
6665
%rename("__repr__") toString;
6766
%rename("entity_instance") IfcLateBoundEntity;
6867
%rename("file") IfcFile;
6968

70-
%typemap(typecheck,precedence=SWIG_TYPECHECK_INTEGER) IfcEntityList::ptr {
71-
$1 = (PySequence_Check($input) && !PyUnicode_Check($input) && !PyString_Check($input)) ? 1 : 0;
72-
}
69+
%include "utils/type_conversion.i"
7370

74-
%typemap(in) IfcEntityList::ptr {
75-
if (PySequence_Check($input)) {
76-
$1 = IfcEntityList::ptr(new IfcEntityList());
77-
for(Py_ssize_t i = 0; i < PySequence_Size($input); ++i) {
78-
PyObject* obj = PySequence_GetItem($input, i);
79-
if (obj) {
80-
void *arg = 0;
81-
int res = SWIG_ConvertPtr(obj, &arg, SWIGTYPE_p_IfcParse__IfcLateBoundEntity, 0);
82-
if (!SWIG_IsOK(res)) {
83-
SWIG_exception_fail(SWIG_ArgError(res), "Sequence element not of type IfcParse::IfcLateBoundEntity*");
84-
} else {
85-
$1->push(reinterpret_cast<IfcParse::IfcLateBoundEntity*>(arg));
86-
}
87-
}
88-
}
89-
} else {
90-
SWIG_exception(SWIG_RuntimeError,"Unknown argument type");
91-
}
92-
}
71+
%include "utils/typemaps_in.i"
9372

94-
%typemap(out) IfcEntityList::ptr {
95-
const unsigned size = $1 ? $1->size() : 0;
96-
$result = PyList_New(size);
97-
for (unsigned i = 0; i < size; ++i) {
98-
PyObject *o = SWIG_NewPointerObj(SWIG_as_voidptr((*$1)[i]), SWIGTYPE_p_IfcParse__IfcLateBoundEntity, 0);
99-
PyList_SetItem($result,i,o);
100-
}
101-
}
102-
103-
%typemap(out) IfcUtil::ArgumentType {
104-
$result = SWIG_Python_str_FromChar(IfcUtil::ArgumentTypeToString($1));
105-
}
106-
107-
%typemap(out) std::pair<IfcUtil::ArgumentType, Argument*> {
108-
// The SWIG %exception directive does not take care
109-
// of our typemap. So the argument conversion block
110-
// is wrapped in a try-catch block manually.
111-
try {
112-
const Argument& arg = *($1.second);
113-
const IfcUtil::ArgumentType type = $1.first;
114-
if (arg.isNull() || type == IfcUtil::Argument_DERIVED) {
115-
Py_INCREF(Py_None);
116-
$result = Py_None;
117-
} else {
118-
switch(type) {
119-
case IfcUtil::Argument_INT:
120-
$result = PyInt_FromLong((int)arg);
121-
break;
122-
case IfcUtil::Argument_BOOL:
123-
$result = PyBool_FromLong((bool)arg);
124-
break;
125-
case IfcUtil::Argument_DOUBLE:
126-
$result = PyFloat_FromDouble(arg);
127-
break;
128-
case IfcUtil::Argument_ENUMERATION:
129-
case IfcUtil::Argument_STRING: {
130-
const std::string s = arg;
131-
$result = PyString_FromString(s.c_str());
132-
break; }
133-
case IfcUtil::Argument_BINARY: {
134-
const boost::dynamic_bitset<> b = arg;
135-
std::string bitstring;
136-
boost::to_string(b, bitstring);
137-
$result = PyString_FromString(bitstring.c_str());
138-
break; }
139-
case IfcUtil::Argument_AGGREGATE_OF_INT: {
140-
const std::vector<int> v = arg;
141-
const unsigned size = v.size();
142-
$result = PyList_New(size);
143-
for (unsigned int i = 0; i < size; ++i) {
144-
PyList_SetItem($result,i,PyInt_FromLong(v[i]));
145-
}
146-
break; }
147-
case IfcUtil::Argument_AGGREGATE_OF_DOUBLE: {
148-
const std::vector<double> v = arg;
149-
const unsigned size = v.size();
150-
$result = PyList_New(size);
151-
for (unsigned int i = 0; i < size; ++i) {
152-
PyList_SetItem($result,i,PyFloat_FromDouble(v[i]));
153-
}
154-
break; }
155-
case IfcUtil::Argument_AGGREGATE_OF_STRING: {
156-
const std::vector<std::string> v = arg;
157-
const unsigned size = v.size();
158-
$result = PyList_New(size);
159-
for (unsigned int i = 0; i < size; ++i) {
160-
PyList_SetItem($result,i,PyString_FromString(v[i].c_str()));
161-
}
162-
break; }
163-
case IfcUtil::Argument_ENTITY_INSTANCE: {
164-
IfcUtil::IfcBaseClass* e = arg;
165-
$result = SWIG_NewPointerObj(SWIG_as_voidptr(e), SWIGTYPE_p_IfcParse__IfcLateBoundEntity, 0);
166-
break; }
167-
case IfcUtil::Argument_AGGREGATE_OF_ENTITY_INSTANCE: {
168-
IfcEntityList::ptr es = arg;
169-
const unsigned size = es->size();
170-
$result = PyList_New(size);
171-
for (unsigned i = 0; i < size; ++i) {
172-
PyObject *o = SWIG_NewPointerObj(SWIG_as_voidptr((*es)[i]), SWIGTYPE_p_IfcParse__IfcLateBoundEntity, 0);
173-
PyList_SetItem($result,i,o);
174-
}
175-
break; }
176-
case IfcUtil::Argument_AGGREGATE_OF_BINARY: {
177-
const std::vector< boost::dynamic_bitset<> > bs = arg;
178-
const unsigned size = bs.size();
179-
$result = PyList_New(size);
180-
for (unsigned int i = 0; i < size; ++i) {
181-
std::string bitstring;
182-
boost::to_string(bs[i], bitstring);
183-
PyList_SetItem($result,i,PyString_FromString(bitstring.c_str()));
184-
}
185-
break; }
186-
case IfcUtil::Argument_UNKNOWN:
187-
default:
188-
SWIG_exception(SWIG_RuntimeError,"Unknown argument type");
189-
break;
190-
}
191-
}
192-
} catch(IfcParse::IfcException& e) {
193-
SWIG_exception(SWIG_RuntimeError, e.what());
194-
} catch(...) {
195-
SWIG_exception(SWIG_RuntimeError, "An unknown error occurred");
196-
}
197-
}
73+
%include "utils/typemaps_out.i"
19874

19975
%extend IfcParse::IfcFile {
20076
IfcParse::IfcLateBoundEntity* by_id(unsigned id) {
@@ -245,9 +121,15 @@ namespace IfcUtil {
245121
}
246122
}
247123
}
248-
%pythoncode %{
249-
set_argument = lambda self,x,y: self._set_argument(x) if y is None else self._set_argument(x,y)
250-
%}
124+
125+
std::pair<IfcUtil::ArgumentType,Argument*> get_argument(unsigned i) {
126+
return std::pair<IfcUtil::ArgumentType,Argument*>($self->getArgumentType(i), $self->getArgument(i));
127+
}
128+
129+
std::pair<IfcUtil::ArgumentType,Argument*> get_argument(const std::string& a) {
130+
unsigned i = IfcSchema::Type::GetAttributeIndex($self->type(), a);
131+
return std::pair<IfcUtil::ArgumentType,Argument*>($self->getArgumentType(i), $self->getArgument(i));
132+
}
251133
}
252134

253135
%extend IfcParse::IfcSpfHeader {

0 commit comments

Comments
 (0)