Search code examples
c++c++11eigenboost-python

Overload resolution of Eigen's operator()() when wrapping it with Boost.Python


I am in the process of wrapping a C++ library using Boost.Python. Some of the functions therein return Eigen::MatrixXd objects (a dynamically sized double-precision matrix class). On the Python side I just need to access the dimensions of the matrices, this is easy, and retrieve some matrix elements with Eigen's overloaded operator()() method. Unfortunately there are 4 such overloaded methods, one has to pick the right one manually, i.e. giving Boost.Python a function pointer typedef with the correct signature, something along the lines

namespace bpy = boost::python;
bpy::class_<Eigen::MatrixXd>("MatrixXd",
        "Variable-size double-precision matrix class",
        bpy::init<const Eigen::MatrixXd&>()
    )
        .def("__call__", static_cast<parop_signature>(&Eigen::MatrixXd::operator()))
        // ...
;

The problem is that I cannot figure out what the correct signature of the function is. "Operationally" it should take two integer indices and return a double value. However,

typedef const double& (Eigen::MatrixXd::*parop_signature)(int, int) const;

results in the following compilation error (Mac OS X, clang++ in C++11 mode, Boost.Python V1.61):

address of overloaded function 'operator()' cannot be static_cast to type
      'const double &(MatrixXd::*)(int, int) const'
  ...static_cast<const double& (Eigen::MatrixXd::*)(int, int) const>(&Eigen::MatrixXd::operator())
     ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
/usr/local/eigen/current/Eigen/src/Core/DenseCoeffsBase.h:111:41: note: 
      candidate function
    EIGEN_STRONG_INLINE CoeffReturnType operator()(Index row, Index col) const
                                        ^
/usr/local/eigen/current/Eigen/src/Core/DenseCoeffsBase.h:171:5: note: 
      candidate function
    operator()(Index index) const
    ^
/usr/local/eigen/current/Eigen/src/Core/DenseCoeffsBase.h:334:5: note: 
      candidate function
    operator()(Index row, Index col)
    ^
/usr/local/eigen/current/Eigen/src/Core/DenseCoeffsBase.h:392:5: note: 
      candidate function
    operator()(Index index)

Fair enough, you'd say: but I cannot figure out how I can tell Boost.Python that CoeffReturnType is actually a double here (or maybe a const double&, who knows?), and that IndexType will resolve to a plain int at the end of the day. I have tried all sorts of combinations of typedef-s with or without const qualifiers.

Even tried to declare a C++11 -style function pointer like

auto eigen_indexfn = std::mem_fn<double(int,int)>(&Eigen::MatrixXd::operator());

, no success, I get

candidate template ignored: couldn't infer template argument '_Tp'
mem_fn(_Rp _Tp::* __pm)
^

Is there someone who has gone through this already and can provide me with the correct signature of what for all intents and purposes should be as simple as "double Eigen::MatrixXd::operator(int, int)" ? Any hints would be much appreciated.


Solution

  • It seems the error origins from the fact that Eigen::Index is not int but defaults to ptrdiff_t. Just because int can implicitly be cast to Eigen::Index does not mean that you can cast a function pointer which requires Eigen::Index to a function pointer which requires int. If that was possible, you would end up passing integers of the wrong size over the stack.

    Addendum: If you really prefer int over ptrdiff_t, you can define EIGEN_DEFAULT_DENSE_INDEX_TYPE to int before including Eigen, as documented here, be aware that this will break ABI compatibility.