PythonInteropBoost
This is an example of one way to wrap an Eigen-based C++ class using Boost::Python for use with numpy.
Here is an overview of how it works:
- You write a wrapper function for any member functions that take
Eigen Matrices,Vectors,etc… This function will:
- take PyObjects (your numpy arrays) as inputs,
- use a macro to get at the underlying data,
- use Eigen’s Map functionality to “turn them into” Eigen arrays,
- and then call the wrapped function using these arrays.
- You write a bit more code to tell Boost about your class and the functions you are exposing to python.
- You build the module as a shared library
- You can either:
- import this directly in your code (you will crash hard if the
inputs are incorrect)
or - write some Python to do typechecking, bounds checking, etc… in __init__.py, exporting a “safe” version to the rest of your code. (the example does this)
- import this directly in your code (you will crash hard if the
inputs are incorrect)
All in all, this is definitely overkill for the included example, and hopefully some day there will be a much cleaner way of doing this, but this is a place to start, and may even be satisfactory if you are already familiar with Boost::Python.
Here is the most critical part, to give you so you have some idea what to expect:
#define WRAP_PYTHON 1
#if WRAP_PYTHON
#include <Python.h>
#include <boost/python.hpp>
#include <numpy/arrayobject.h>
#endif
#include <iostream>
using namespace std;
#include <Eigen/Core>
#include <Eigen/Array>
using namespace Eigen;
class FooClass
{
public:
FooClass( int new_m );
~FooClass();
template<typename Derived>
int foo(const MatrixBase<Derived>& barIn, MatrixBase<Derived>& barOut);
#if WRAP_PYTHON
int foo_python(PyObject* barIn, PyObject* barOut);
#endif
private:
int m;
};
FooClass::FooClass( int new_m ){
m = new_m;
}
FooClass::~FooClass(){
}
template<typename Derived>
int FooClass::foo(const MatrixBase<Derived>& barIn, MatrixBase<Derived>& barOut){
barOut = barIn*3.0; // Some trivial placeholder computation.
}
#if WRAP_PYTHON
int FooClass::foo_python(PyObject* barIn, PyObject* barOut){
Map<VectorXd> _barIn((double *) PyArray_DATA(barIn),m);
Map<VectorXd> _barOut((double *) PyArray_DATA(barOut),m);
return foo(_barIn, _barOut);
}
using namespace boost::python;
BOOST_PYTHON_MODULE(_FooClass)
{
class_<FooClass>("FooClass", init<int>(args("m")))
.def("foo", &FooClass::foo_python)
;
}
#endif