Reputation: 2074
How to pass an eigen matrix A into a function by reference, and then steal ownership of A?
I am writing a C++ extension to Python that takes in two std::vector<Eigen::Ref<Mat> >
and returns a std::vector<Mat>
. Each element of the returned vector can be a new matrix or an old matrix referenced in the input vectors.
The example from pybind11 mentioned pass-by-reference between C++ and Python with Eigen::Ref
here (pybind11 doc).
I try a modification of an example (from an old bug report). But the source is not moved to the target. This is because the source matrix is not empty at the end.
Test:
#include <iostream>
#include <eigen3/Eigen/Dense>
typedef Eigen::MatrixXd Mat;
Mat func(Eigen::Ref<Mat> ref) {
Mat target(std::move(ref));
return target;
}
int main() {
const std::size_t n = 2;
Mat source = Mat::Zero(n, n);
Mat target = func(source);
std::cout << "source\n" << source << "\n";
std::cout << "target\n" << target << "\n";
return 0;
}
Result:
source
0 0
0 0
target
0 0
0 0
Upvotes: 2
Views: 389
Reputation: 2074
This works in C++:
Test:
#include <iostream>
#include <eigen3/Eigen/Dense>
#include <vector>
typedef Eigen::MatrixXd Mat;
void print_mats(std::string const& s, const std::vector<Mat>& v) {
std::cout << s << "\n";
for (int i = 0; i < v.size(); ++i) {
std::cout << "matrix #" << i << "\n";
std::cout << v[i] << "\n";
}
}
std::vector<Mat> func(std::vector<Mat>& source) {
std::vector<Mat> target;
target.emplace_back(std::move(source[0]));
return target;
}
int main() {
const std::size_t n = 2;
std::vector<Mat> source;
// can't move Mat::Zero(n, n), which is a const expression.
// no need for source.emplace_back(std::move(Mat::Zero(n, n)));
source.emplace_back(Mat::Zero(n, n));
print_mats("source before", source);
std::vector<Mat> target = func(source);
print_mats("source after", source);
print_mats("target", target);
return 0;
}
Result:
source before
matrix #0
0 0
0 0
source after
matrix #0
target
matrix #0
0 0
0 0
Upvotes: 0
Reputation: 29225
This is not possible because the memory layout of Ref<MatrixXd>
is more general than MatrixXd
and it does not even own the data that it references! So the only solution for you is to pass a MatrixXd&
.
Upvotes: 2
Reputation: 60761
ref
inside your function is a local variable. You can move it, that's OK. But you cannot steal ownership because it's passed by value, you don't have access to the object as it exists in the caller's workspace. Also, you are moving the reference to a constructor of Mat
, which will simply create a new matrix by copy (I presume, since you cannot move an object of one type to an object of a different type).
What you see happening is because ref
shares the data with source
in the caller's workspace. These are two different objects that point to the same data.
If you were to return the reference object (rather than create a new matrix initialized with the reference object as you do), then the reference could outlive the original object referenced, and cause trouble.
Upvotes: 1