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::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()
.