Why is the Eigen matrix of a matrix converted to a C array giving garbage values ​​for the first two indices?

I have an Eigen matrix that needs to be converted to a C array. I can reproduce the problem in the following example.

#include <iostream>
#include <Eigen/Core>

int *test()
{
    Eigen::MatrixXi arr = Eigen::MatrixXi::Ones(6,1);
    // just to check
    arr(4)=3;
    arr(5)=19;
    return arr.data();
}

int main()
{
    int *c_arr;
    c_arr = test();

    for (int i=0; i<6;++i)
    {
        std::cout << c_arr[i] << std::endl;
    }

    return 0;
}

      

Output:

0
0
1
1
3
19

      

Now if I print the converted C array values ​​from the function test

, the values ​​are correct. However, if I print the values ​​from main

(as shown above), the first two indices are always garbage. So I'm wondering what's going on in the function call? I've tried this with various Eigen matrices (types, sizes) and I get the same result.

+3


source to share


1 answer


To begin with, I'm not 100% familiar with the Eigen library (I just downloaded it to look at it out of curiosity) and the documentation is a bit lacking, but your problem is a fundamental C problem that can be fixed in several ways.

First, let's start by explaining what's going on in your code to assign values ​​to garbage:

int *test()
{
    /* create an auto scoped variable on the stack;
       this variable is only "visible" to this function
       and any references to it or it underlying data
       outside the scope of this function will result
       in "undefined behaviour" */
    Eigen::MatrixXi arr = Eigen::MatrixXi::Ones(6,1);
    arr(4)=3;
    arr(5)=19;
    /* arr.data() is defined as returning a pointer to the scalar underlying type (or
    a C-style array in other words). Regardless of the type being returned, it is pointer based
    and you are returning a pointer to a location in memory, not the actual data being held in
    the memory. */
    return arr.data();
} /* the variable arr is destroyed here since we left function scope and the return value (the pointer location)
is put in the return register and "program flow" is returned back to the main function where the pointer being
returned now points to "invalid" memory */

int main()
{
    int *c_arr; // create a pointer type that can reference int types
    c_arr = test(); // point it to the result of the test function (see notes above)
    /* c_arr now points to a memory location returned from test, but since the
    arr variable no longer exists here, when you go through and print the values pointed
    to at those memory locations you will get what is at those locations and could be "anything"
    except a valid reference to the original arr variable and it underlying data. */

    for (int i=0; i<6;++i)
    {
        std::cout << c_arr[i] << std::endl;
    }

    return 0;
}

      

So why , how to fix this, there are several ways to solve your problem; one is to pass the returned array as a variable to your function test

(for example void test(int*& val)

), then you can choose to allocate new memory for the variable in the test function, or assume the user has already done this and also must assume that the user clears up after himself and calls delete[]

( not just delete

because you are working with arrays of data).



But it has a lot of caveats that you need to know how much space to allocate and be sure to free up when it's done. I'm not sure why you only need a C-style array, but since you are using C ++ it might make more sense if you use some of the STL and container functions available to you to help you, for example:

#include <iostream>
#include <vector>
#include <Eigen/Core>

std::vector<int> test()
{
    Eigen::MatrixXi arr = Eigen::MatrixXi::Ones(6,1);
    arr(4)=3;
    arr(5)=19;
    // we need the size so we know how big of a container to allocate
    std::size_t sz = arr.innerSize() * arr.outerSize();
    std::vector<int> ret(sz);
    // get a temporary C array pointer so we can reference the data
    int* tmp = arr.data();
    // copy from tmp[0] to tmp[sz] and insert the data into the first element of ret
    std::copy(tmp, tmp+sz, ret.begin());
    // return the (copied) data
    return ret;
}

int main()
{
    std::vector<int> c_arr = test();
    // c_arr now points to valid data it holds and can be iterated on
    for (std::size_t i = 0; i < c_arr.size(); ++i) {
        std::cout << c_arr[i] << std::endl;
    }
    // if you need a C-style array from here, you can easily copy the data
    // from the vector to your C-array
    return 0;
}

      

I looked into using cast()

a class function, but couldn't figure out the syntax to make it less painful than just copying it above, as it looks like you need to call the function cast

for a differnt type Eigen

and then cast back from there, but you know there is a function cast

and other methods to get the underlying class data MatrixX

if you need access to it.

Hope this helps.

+1


source







All Articles