hrl
hrl

Reputation: 1

Returning a Eigen::Map from a lambda

Returning a Eigen::Map from a lambda function gives wrong output:

#include<iostream>
#include<eigen3/Eigen/Dense>
using namespace std;
using namespace Eigen;
int main(){
    auto doIt = [](){
        MatrixXd a = MatrixXd::Random(2,3);
        Map<MatrixXd> m(a.data(),2,3);
        cout << "a:\n" << a << endl;
        cout << "m:\n" << m << endl;
        return m;
    };
    MatrixXd o(2,3);
    o = doIt();
    cout << "o:\n" << o << endl;
   return 0;
}

The output (using Eigen 3.2.9-1):

a:
 0.680375  0.566198  0.823295
-0.211234   0.59688 -0.604897
m:
 0.680375  0.566198  0.823295
-0.211234   0.59688 -0.604897
o:
5.15038e-317     0.566198     0.823295
-0.211234      0.59688    -0.604897

I further get a heap-use-after-free-error if compiled with clang++ -std=c++11 -fsanitize=address. (clang version 3.7.1)

If I convert the lambda to a function returning a Eigen::MatrixXd, it works -- possibly because of additional copying. Is there any way to have it working inside the lambda, without using an additional MatrixXd and copying the content of the map to it?

(In my use case, the lambda has only access to the Map m and not to the MatrixXd a itself).

Upvotes: 0

Views: 812

Answers (3)

davidhigh
davidhigh

Reputation: 15488

I'm just elaborating on the comment of @GuillaumeRacicot, who already gave the answer in his comment.

Map<MatrixXd> m(a.data(),2,3); takes as input a pointer to the data in matrix m which is stored contiguously in memory. When you copy mthis pointer is copied as well.

Now the matrix a is defined only in the scope of doIt() and is allocated on the stack. Once doIt() is done, a is destroyed, and any pointer to it becomes invalid.

And this is exactly what happens here: when o tries to access the data, it is already gone and the regions in memory could have been used for something different in the meantime. This is why you see that some elements are still the same, but others have changed.

Upvotes: 1

Teivaz
Teivaz

Reputation: 5675

Map does not own the data, it only wraps existing data. If the data was freed or modified Map will not be aware. And that precisely what happens here:

auto doIt = [](){
    // Here MatrixXd dynamically allocates memory for its content
    MatrixXd a = MatrixXd::Random(2,3);

    // Now you save pointer to this allocated memory
    Map<MatrixXd> m(a.data(),2,3);
    cout << "a:\n" << a << endl;
    cout << "m:\n" << m << endl;

    return m;
    // When leaving the scope via return or simple end of block
    // all local variables will be destroyed.
    // When destorying, Matrix will also free
    // what it owns thus Map will point to invalid memory

};

So basically you are accessing a freed memory. You need to return the object that owns resources:

auto doIt = [](){
    return MatrixXd::Random(2,3);
};
auto a = doIt();
Map<MatrixXd> m(a.data(),2,3);
cout << "a:\n" << a << endl;
cout << "m:\n" << m << endl;
MatrixXd o(2,3);
cout << "o:\n" << o << endl;

Upvotes: 2

stryku
stryku

Reputation: 750

I think that move will help

#include<iostream>
#include<eigen3/Eigen/Dense>
using namespace std;
using namespace Eigen;
int main(){
    auto&& doIt = [](){
        MatrixXd a = MatrixXd::Random(2,3);
        Map<MatrixXd> m(a.data(),2,3);
        cout << "a:\n" << a << endl;
        cout << "m:\n" << m << endl;
        return std::move(m);
    };
    MatrixXd o(2,3);
    o = doIt();
    cout << "o:\n" << o << endl;
   return 0;
}

Can't test it because I don't have eigen locally

Upvotes: -1

Related Questions