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?
First off, the C++ and Python codes above are not equivalent. In C++, you end up having a
Eigen::Matrix4dthat is initialized fromIdentity(), whereas in Python, you are taking a reference to the result ofIdentity()which is aCwiseNullaryOp, 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 aCwiseNullaryOpis a constant object and can not be modified.Instead, make the codes equivalent by doing:
This way, you now have a
Eigen::Matrix4din Python as well, and assignment can now happen.Next, yes,
matrix(0, 0) = 2.0is a syntax error in Python, so instead, use square brackets: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 nooperator[]defined, it will mapoperator()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 recognizevalue()the same way it doesoperator().