Numpy array interface to SWIG wrapped C++ Class with --builtin
Tobias Winchen <[email protected]>
| Newsgroups | gmane.comp.programming.swig |
|---|---|
| Message-ID | <1990648.nfdQr6AkM3@guymontag> |
Dear SWIG Users and Developers,
I am trying to generate a python interface to a C++ class with SWIG that is
conform with the numpy array interface. For SWIG without --builtin option I
succeeded with the approach described below. How can I generate the correct
wrappers when using swig with the --builtin option?
The following class is part of a larger C++ code with python wrappers using
SWIG. Unessential methods have been removed to create a minimum example. File
vector3.h:
template<typename T>
class Vector3
{
public:
union {
struct
{
T x;
T y;
T z;
};
T data[3];
};
// avoid creation of default non-conversion constructor
Vector3(const Vector3 &v) : data{v.data[0], v.data[1], v.data[2]} {
}
// Provides implicit conversion
template<typename U>
Vector3(const Vector3<U> &v) {
data[0] = v.x;
data[1] = v.y;
data[2] = v.z;
}
~Vector3()
{
}
explicit Vector3(const double *v) {
data[0] = v[0];
data[1] = v[1];
data[2] = v[2];
}
explicit Vector3(const float *v) {
data[0] = v[0];
data[1] = v[1];
data[2] = v[2];
}
explicit Vector3(const T &X, const T &Y, const T &Z) : data{X, Y, Z} {
}
explicit Vector3(T t) : data{t, t, t} {
}
};
I can create a python wrapper with the swig file vector3.i:
%module(directors="1", threads="1", allprotected="1") vector3
%init %{
import_array();
import_ufunc();
%}
%{
/* Include numpy array interface, if available */
#define NPY_NO_DEPRECATED_API NPY_1_7_API_VERSION
#include "numpy/arrayobject.h"
#include "numpy/ufuncobject.h"
#include "vector3.h"
%}
%feature("python:slot", "sq_length", functype="lenfunc") Vector3::__len__;
%feature("python:slot", "mp_subscript", functype="binaryfunc")
Vector3::__getitem__;
%feature("python:slot", "mp_ass_subscript", functype="objobjargproc")
Vector3::__setitem__;
%include "vector3.h"
%extend Vector3
{
size_t __len__()
{
return 3;
}
size_t getdataPointer()
{
return (size_t) $self->data;
}
double __getitem__(size_t i)
{
return $self->data[i];
}
int __setitem__(size_t i, T value)
{
$self->data[i] = value;
return 0;
}
%pythoncode %{
def __getArrayInterface(obj):
return {'shape':(3,), 'data': (obj.getdataPointer(), False), 'typestr':
'f8'}
__swig_getmethods__["__array_interface__"] = __getArrayInterface
%template(Vector3d) Vector3<double>;
swig -c++ -python -I. -dirprot -o vector3_wrap.cxx vector3.i
g++ -std=c++11 -g -fPIC -c vector3_wrap.cxx -I/usr/include/python2.7 -I.
g++ -std=c++11 -g -shared vector3_wrap.o -o _vector3.so
Adding the array_interface dict above gives the desired functionality, e.g.
v = Vector3d(0,2,3)
print "Np sum:", np.sum(v)
print "Np array", np.array(v)
Instead of adding an array_interface dictionary I extended the vector3 in
swig with an array method:
Also in vector3.i:
extend Vector3{
PyObject* __array__()
{
npy_intp shape[1];
shape[0] = 3;
PyObject *ro = Py_None;
if (sizeof($self->data[0]) == NPY_SIZEOF_FLOAT)
{
ro = PyArray_SimpleNewFromData(1, shape, NPY_FLOAT, $self->data);
}
else if (sizeof($self->data[0]) == NPY_SIZEOF_DOUBLE)
{
ro = PyArray_SimpleNewFromData(1, shape, NPY_DOUBLE, $self->data);
}
return Py_None;
}
return ro;
}
correctly. The interface may segfault because (I think) the vector3 may be
deleted while the array `view' on the data still exists. I tried adding a base
PyArray_SetBaseObject((PyArrayObject *) ro, SWIG_This());
without success. But also the approach to add a method that returns a python
object directly via extend seems to be not correct. I already asked this
question Any suggestions are appreciated. Please note that I first asked this
question on stackoverflow [1] without receiving an answer yet - just in case
you prefer to answer there.
Bests,
Tobi
[1] https://stackoverflow.com/questions/60207293/numpy-array-interface-to-swig-wrapped-c-class-with-builtin