Using @kazemakase and @jagerman (the latter via the pybind11 forum ) I figured this out. I changed the class signature a bit so that it can copy data from a pointer:
template <class T> class Matrix3D { public: std::vector<T> data; std::vector<size_t> shape; std::vector<size_t> strides; Matrix3D<T>(); Matrix3D<T>(std::vector<size_t>, const T *data=NULL); Matrix3D<T>(const Matrix3D<T>&); T& operator() (int,int,int); };
The constructor of the class is as follows:
template <class T> Matrix3D<T>::Matrix3D (std::vector<size_t> shape_, const T *data_ ) { int size; while ( shape.size()<3 ) shape.push_back(1); while ( strides.size()<3 ) strides.push_back(1); for ( int i=0 ; i<3 ; i++ ) shape[i] = shape_[i]; size = shape_[0]*shape_[1]*shape_[2]; strides[0] = shape[2]*shape[1]; strides[1] = shape[2]; strides[2] = 1; while ( data.size()<size ) data.push_back((T)0); if ( data_!=NULL ) for ( int i=0 ; i<size ; i++ ) data[i] = data_[i]; }
Directly wrap the function of the following signature:
Matrix3D<double> func ( const Matrix3D<double>& );
the following shell code is needed
#include <pybind11/pybind11.h> #include <pybind11/stl.h> #include <pybind11/numpy.h> namespace py = pybind11; namespace pybind11 { namespace detail { template <typename T> struct type_caster<Matrix3D<T>> { public: PYBIND11_TYPE_CASTER(Matrix3D<T>, _("Matrix3D<T>")); // Conversion part 1 (Python -> C++) bool load(py::handle src, bool convert) { if (!convert && !py::array_t<T>::check_(src)) return false; auto buf = py::array_t<T, py::array::c_style | py::array::forcecast>::ensure(src); if (!buf) return false; auto dims = buf.ndim(); if (dims != 3 ) return false; std::vector<size_t> shape(3); for ( int i=0 ; i<3 ; i++ ) shape[i] = buf.shape()[i]; value = Matrix3D<T>(shape,buf.data()); return true; } //Conversion part 2 (C++ -> Python) static py::handle cast(const Matrix3D<T>& src, py::return_value_policy policy, py::handle parent) { std::vector<size_t> shape (3); std::vector<size_t> strides(3); for ( int i=0 ; i<3 ; i++ ) { shape [i] = src.shape [i]; strides[i] = src.strides[i]*sizeof(T); } py::array a(std::move(shape), std::move(strides), src.data.data() ); return a.release(); } }; }} // namespace pybind11::detail PYBIND11_PLUGIN(example) { py::module m("example", "Module description"); m.def("func", &func, "Function description" ); return m.ptr(); }
Please note that function overloading is now possible. For example, if an overloaded function existed with the following signature:
Matrix3D<int > func ( const Matrix3D<int >& ); Matrix3D<double> func ( const Matrix3D<double>& );
The following shell function definition is required:
m.def("func", py::overload_cast<Matrix3D<int >&>(&func), "Function description" ); m.def("func", py::overload_cast<Matrix3D<double>&>(&func), "Function description" );