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:

  1. 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.
  2. You write a bit more code to tell Boost about your class and the functions you are exposing to python.
  3. You build the module as a shared library
  4. 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)

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