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.

This commit is contained in:
Thomas Krijnen
2015-06-17 11:27:02 +00:00
parent f8c45b2a99
commit 48676a733f
10 changed files with 454 additions and 176 deletions
+159
View File
@@ -0,0 +1,159 @@
/********************************************************************************
* *
* This file is part of IfcOpenShell. *
* *
* IfcOpenShell is free software: you can redistribute it and/or modify *
* it under the terms of the Lesser GNU General Public License as published by *
* the Free Software Foundation, either version 3.0 of the License, or *
* (at your option) any later version. *
* *
* IfcOpenShell is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* Lesser GNU General Public License for more details. *
* *
* You should have received a copy of the Lesser GNU General Public License *
* along with this program. If not, see <http://www.gnu.org/licenses/>. *
* *
********************************************************************************/
// Conversion functions and checks to convert Python objects into STL vectors
%{
template <typename T> void* get_python_type();
template <> void* get_python_type<double>() { return &PyFloat_Type; }
#if PY_VERSION_HEX >= 0x03000000
template <> void* get_python_type<int>() { return &PyLong_Type; }
template <> void* get_python_type<std::string>() { return &PyUnicode_Type; }
#else
template <> void* get_python_type<int>() { return &PyInt_Type; }
template <> void* get_python_type<std::string>() { return &PyString_Type; }
#endif
bool check_aggregate_of_type(PyObject* aggregate, void* type_obj) {
if (!PySequence_Check(aggregate)) return false;
for(Py_ssize_t i = 0; i < PySequence_Size(aggregate); ++i) {
PyObject* element = PySequence_GetItem(aggregate, i);
// This is equivalent to the PyFloat_CheckExact macro. This means
// that direct instances of int, float, str, etc. need to be used.
if (element->ob_type != type_obj) return false;
}
return true;
}
bool check_aggregate_of_aggregate_of_type(PyObject* aggregate, void* type_obj) {
if (!PySequence_Check(aggregate)) return false;
for(Py_ssize_t i = 0; i < PySequence_Size(aggregate); ++i) {
PyObject* element = PySequence_GetItem(aggregate, i);
if (!check_aggregate_of_type(element, type_obj)) {
return false;
}
}
return true;
}
template <typename T>
T cast_pyobject(PyObject* element);
template <>
int cast_pyobject(PyObject* element) {
return static_cast<int>(PyInt_AsLong(element));
}
template <>
double cast_pyobject(PyObject* element) {
return PyFloat_AsDouble(element);
}
template <>
std::string cast_pyobject(PyObject* element) {
char* str_data = SWIG_Python_str_AsChar(element);
std::string str = str_data;
SWIG_Python_str_DelForPy3(str_data);
return str;
}
template <>
IfcParse::IfcLateBoundEntity* cast_pyobject(PyObject* element) {
void *arg = 0;
int res = SWIG_ConvertPtr(element, &arg, SWIGTYPE_p_IfcParse__IfcLateBoundEntity, 0);
if (!res) return static_cast<IfcParse::IfcLateBoundEntity*>(0);
return reinterpret_cast<IfcParse::IfcLateBoundEntity*>(arg);
}
template <typename T>
std::vector<T> python_sequence_as_vector(PyObject* aggregate) {
std::vector<T> result_vector;
result_vector.reserve(PySequence_Size(aggregate));
for(Py_ssize_t i = 0; i < PySequence_Size(aggregate); ++i) {
PyObject* element = PySequence_GetItem(aggregate, i);
T t = cast_pyobject<T>(element);
result_vector.push_back(t);
}
return result_vector;
}
template <typename T>
std::vector< std::vector<T> > python_sequence_as_vector_of_vector(PyObject* aggregate) {
std::vector< std::vector<T> > result_vector;
result_vector.reserve(PySequence_Size(aggregate));
for(Py_ssize_t i = 0; i < PySequence_Size(aggregate); ++i) {
PyObject* element = PySequence_GetItem(aggregate, i);
std::vector<T> t = python_sequence_as_vector<T>(element);
result_vector.push_back(t);
}
return result_vector;
}
%}
// Conversion functions to convert STL vectors into Python objects
%{
PyObject* pythonize(const int& t) { return PyInt_FromLong(t); }
PyObject* pythonize(const bool& t) { return PyBool_FromLong(t); }
PyObject* pythonize(const double& t) { return PyFloat_FromDouble(t); }
PyObject* pythonize(const std::string& t) { return PyString_FromString(t.c_str()); }
PyObject* pythonize(const IfcUtil::IfcBaseClass* t) { return SWIG_NewPointerObj(SWIG_as_voidptr(t), SWIGTYPE_p_IfcParse__IfcLateBoundEntity, 0); }
PyObject* pythonize(const boost::dynamic_bitset<>& t) {
std::string bitstring;
boost::to_string(t, bitstring);
return pythonize(bitstring);
}
PyObject* pythonize(const IfcEntityList::ptr& t) {
unsigned int i = 0;
PyObject* pyobj = PyTuple_New(t->size());
for (IfcEntityList::it it = t->begin(); it != t->end(); ++it, ++i) {
PyTuple_SetItem(pyobj, i, pythonize(*it));
}
return pyobj;
}
template <typename T>
PyObject* pythonize_vector(const std::vector<T>& v) {
const unsigned size = v.size();
PyObject* pyobj = PyTuple_New(size);
for (unsigned int i = 0; i < size; ++i) {
PyTuple_SetItem(pyobj, i, pythonize(v[i]));
}
return pyobj;
}
template <typename T>
PyObject* pythonize_vector2(const std::vector< std::vector<T> >& v) {
const unsigned size = v.size();
PyObject* pyobj = PyTuple_New(size);
for (unsigned int i = 0; i < size; ++i) {
PyTuple_SetItem(pyobj, i, pythonize_vector(v[i]));
}
return pyobj;
}
PyObject* pythonize(const IfcEntityListList::ptr& t) {
unsigned int i = 0;
PyObject* pyobj = PyTuple_New(t->size());
for (IfcEntityListList::outer_it it = t->begin(); it != t->end(); ++it, ++i) {
PyTuple_SetItem(pyobj, i, pythonize_vector(*it));
}
return pyobj;
}
%}
+111
View File
@@ -0,0 +1,111 @@
/********************************************************************************
* *
* This file is part of IfcOpenShell. *
* *
* IfcOpenShell is free software: you can redistribute it and/or modify *
* it under the terms of the Lesser GNU General Public License as published by *
* the Free Software Foundation, either version 3.0 of the License, or *
* (at your option) any later version. *
* *
* IfcOpenShell is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* Lesser GNU General Public License for more details. *
* *
* You should have received a copy of the Lesser GNU General Public License *
* along with this program. If not, see <http://www.gnu.org/licenses/>. *
* *
********************************************************************************/
%define CREATE_VECTOR_TYPEMAP_IN(template_type, express_name, python_name)
%typemap(in) std::vector<template_type> {
if (!check_aggregate_of_type($input, get_python_type<template_type>())) {
SWIG_exception(SWIG_TypeError, "Attribute of type AGGREGATE OF " #express_name " needs a python sequence of " #python_name "s");
}
$1 = python_sequence_as_vector<template_type>($input);
}
%typemap(arginit) const std::vector<template_type>& {
$1 = new std::vector<template_type>();
}
%typemap(in) const std::vector<template_type>& {
if (!check_aggregate_of_type($input, get_python_type<template_type>())) {
SWIG_exception(SWIG_TypeError, "Attribute of type AGGREGATE OF " #express_name " needs a python sequence of " #python_name "s");
}
*$1 = python_sequence_as_vector<template_type>($input);
}
%typemap(freearg) const std::vector<template_type>& {
delete $1;
}
%typemap(in) std::vector< std::vector<template_type> > {
if (!check_aggregate_of_aggregate_of_type($input, get_python_type<template_type>())) {
SWIG_exception(SWIG_TypeError, "Attribute of type AGGREGATE OF AGGREGATE OF " #express_name " needs a python sequence of sequence of " #python_name "s");
}
$1 = python_sequence_as_vector_of_vector<template_type>($input);
}
%typemap(arginit) const std::vector< std::vector<template_type> >& {
$1 = new std::vector< std::vector<template_type> >();
}
%typemap(in) const std::vector< std::vector<template_type> >& {
if (!check_aggregate_of_aggregate_of_type($input, get_python_type<template_type>())) {
SWIG_exception(SWIG_TypeError, "Attribute of type AGGREGATE OF AGGREGATE OF " #express_name " needs a python sequence of sequence of " #python_name "s");
}
*$1 = python_sequence_as_vector_of_vector<template_type>($input);
}
%typemap(freearg) const std::vector< std::vector<template_type> >& {
delete $1;
}
%enddef
CREATE_VECTOR_TYPEMAP_IN(int, INTEGER, int)
CREATE_VECTOR_TYPEMAP_IN(double, REAL, float)
CREATE_VECTOR_TYPEMAP_IN(std::string, STRING, str)
%typemap(in) IfcEntityList::ptr {
if (PySequence_Check($input)) {
$1 = IfcEntityList::ptr(new IfcEntityList());
for(Py_ssize_t i = 0; i < PySequence_Size($input); ++i) {
PyObject* element = PySequence_GetItem($input, i);
IfcParse::IfcLateBoundEntity* inst = cast_pyobject<IfcParse::IfcLateBoundEntity*>(element);
if (inst) {
$1->push(inst);
} else {
SWIG_exception(SWIG_TypeError, "Attribute of type AGGREGATE OF ENTITY INSTANCE needs a python sequence of entity instances");
}
}
} else {
SWIG_exception(SWIG_TypeError, "Attribute of type AGGREGATE OF ENTITY INSTANCE needs a python sequence of entity instances");
}
}
%typemap(in) IfcEntityListList::ptr {
if (PySequence_Check($input)) {
$1 = IfcEntityListList::ptr(new IfcEntityListList());
for(Py_ssize_t i = 0; i < PySequence_Size($input); ++i) {
PyObject* element = PySequence_GetItem($input, i);
if (PySequence_Check(element)) {
std::vector<IfcUtil::IfcBaseClass*> vector;
vector.reserve(PySequence_Size(element));
for(Py_ssize_t j = 0; j < PySequence_Size(element); ++j) {
PyObject* element_element = PySequence_GetItem(element, j);
IfcParse::IfcLateBoundEntity* inst = cast_pyobject<IfcParse::IfcLateBoundEntity*>(element_element);
if (inst) {
vector.push_back(cast_pyobject<IfcParse::IfcLateBoundEntity*>(element_element));
} else {
SWIG_exception(SWIG_TypeError, "Attribute of type AGGREGATE OF AGGREGATE OF ENTITY INSTANCE needs a python sequence of sequence of entity instances");
}
}
$1->push(vector);
} else {
SWIG_exception(SWIG_TypeError, "Attribute of type AGGREGATE OF AGGREGATE OF ENTITY INSTANCE needs a python sequence of sequence of entity instances");
break;
}
}
} else {
SWIG_exception(SWIG_TypeError, "Attribute of type AGGREGATE OF AGGREGATE OF ENTITY INSTANCE needs a python sequence of sequence of entity instances");
}
}
+92
View File
@@ -0,0 +1,92 @@
%typemap(out) IfcEntityList::ptr {
const unsigned size = $1 ? $1->size() : 0;
$result = PyTuple_New(size);
for (unsigned i = 0; i < size; ++i) {
PyTuple_SetItem($result, i, pythonize((*$1)[i]));
}
}
%typemap(out) IfcUtil::ArgumentType {
$result = SWIG_Python_str_FromChar(IfcUtil::ArgumentTypeToString($1));
}
%typemap(out) std::pair<IfcUtil::ArgumentType, Argument*> {
// 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;
if (arg.isNull() || type == IfcUtil::Argument_DERIVED) {
Py_INCREF(Py_None);
$result = Py_None;
} else {
switch(type) {
case IfcUtil::Argument_INT:
$result = pythonize((int) arg);
break;
case IfcUtil::Argument_BOOL:
$result = pythonize((bool) arg);
break;
case IfcUtil::Argument_DOUBLE:
$result = pythonize((double) arg);
break;
case IfcUtil::Argument_ENUMERATION:
case IfcUtil::Argument_STRING:
$result = pythonize((std::string) arg);
break;
case IfcUtil::Argument_BINARY:
$result = pythonize((boost::dynamic_bitset<>) arg);
break;
case IfcUtil::Argument_AGGREGATE_OF_INT:
$result = pythonize_vector((std::vector<int>) arg);
break;
case IfcUtil::Argument_AGGREGATE_OF_DOUBLE:
$result = pythonize_vector((std::vector<double>) arg);
break;
case IfcUtil::Argument_AGGREGATE_OF_STRING:
$result = pythonize_vector((std::vector<std::string>) arg);
break;
case IfcUtil::Argument_ENTITY_INSTANCE:
$result = pythonize((IfcUtil::IfcBaseClass*) arg);
break;
case IfcUtil::Argument_AGGREGATE_OF_ENTITY_INSTANCE:
$result = pythonize((IfcEntityList::ptr) arg);
break;
case IfcUtil::Argument_AGGREGATE_OF_BINARY:
$result = pythonize_vector((std::vector< boost::dynamic_bitset<> >) arg);
break;
case IfcUtil::Argument_AGGREGATE_OF_AGGREGATE_OF_INT:
$result = pythonize_vector2((std::vector< std::vector<int> >) arg);
break;
case IfcUtil::Argument_AGGREGATE_OF_AGGREGATE_OF_DOUBLE:
$result = pythonize_vector2((std::vector< std::vector<double> >) arg);
break;
case IfcUtil::Argument_AGGREGATE_OF_AGGREGATE_OF_ENTITY_INSTANCE:
$result = pythonize((IfcEntityListList::ptr) arg);
break;
case IfcUtil::Argument_UNKNOWN:
default:
SWIG_exception(SWIG_RuntimeError,"Unknown attribute type");
break;
}
}
} catch(IfcParse::IfcException& e) {
SWIG_exception(SWIG_RuntimeError, e.what());
} catch(...) {
SWIG_exception(SWIG_RuntimeError, "An unknown error occurred");
}
}
%define CREATE_VECTOR_TYPEMAP_OUT(template_type)
%typemap(out) std::vector<template_type> {
$result = pythonize_vector<template_type>($1);
}
%typemap(out) const std::vector<template_type>& {
$result = pythonize_vector<template_type>($1);
}
%enddef
CREATE_VECTOR_TYPEMAP_OUT(int)
CREATE_VECTOR_TYPEMAP_OUT(double)
CREATE_VECTOR_TYPEMAP_OUT(std::string)