R zu
R zu

Reputation: 2074

Take ownership of referenced Eigen3 matrix

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

Answers (3)

R zu
R zu

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

ggael
ggael

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

Cris Luengo
Cris Luengo

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

Related Questions