pythonc++eigencppyy

Cppyy Setting a referenced value


I am attempting to use Eigen with cppyy and convert back and forth to numpy, but ran into a snag,

How would one set a value by reference, when you cannot assign to a function call in python?

Take this C++ code for example:

Eigen::Matrix4d matrix = Eigen::Matrix4d::Identity();
matrix(0, 0) = 2.0;

Now in python:

matrix = cppyy.gbl.Eigen.Matrix4d.Identity()
matrix(0, 0) = 2.0 # error, cannot assign to function call
matrix.row(0)[0] = 2.0  # error, setitem operator does not work correctly in cppyy here

Not just Eigen, any function that returns a non const reference to a standard type (double, float, etc) has this issue. eg:

class MyClass {
public:
    double& value() { return m_value; }
    const double& value() const { return m_value; }
private:
    float m_value{0.0};
};

What is the standard practice for this?


Solution

  • First off, the C++ and Python codes above are not equivalent. In C++, you end up having a Eigen::Matrix4d that is initialized from Identity(), whereas in Python, you are taking a reference to the result of Identity() which is a CwiseNullaryOp, not a matrix. The idea behind nullary expressions in Eigen is that you don't need to store things like an identity matrix, but rather have a functor that returns 1 if the indices are equal and 0 otherwise. Obviously, however, such a CwiseNullaryOp is a constant object and can not be modified.

    Instead, make the codes equivalent by doing:

    Matrix4d = cppyy.gbl.Eigen.Matrix4d
    matrix = Matrix4d(Matrix4d.Identity())
    

    This way, you now have a Eigen::Matrix4d in Python as well, and assignment can now happen.

    Next, yes, matrix(0, 0) = 2.0 is a syntax error in Python, so instead, use square brackets:

    matrix[0, 0] = 2.0
    

    The ticket here is that cppyy assumes that if a "function call" returns a non-const reference, that that is potentially a __setitem__ in disguise. Then if there is no operator[] defined, it will map operator() to both __call__ and __setitem__.

    As for MyClass, yes, that one can not be fully automatically bound and will need some pythonization with some C++ helpers (that can be JITed through Cling). The difference here is that cppyy will not recognize value() the same way it does operator().