1

I am trying to initialize Eigen::SparseMatrix A by using malloc like this

A.valuePtr() = static_cast<T*>(std::malloc(nnz*sizeof(T)));
A.innerIndexPtr() = static_cast<int*>(std::malloc(nnz*sizeof(int)));

but I get the error

error: lvalue required as left operand of assignment

for both statements. In case it matters, the function containing the lines takes a Eigen::SparseMatrix<T, Eigen::RowMajor> by reference.

Can anyone help me with this? I'm using g++ 5.2.

EDIT: The function valuePtr of class SparseMatrix is

inline Scalar* valuePtr() { return &m_data.value(0); }

Scalar is a template paramter. m_data is a protected variable of type CompressedStorage whose method value(size_t i) returns a reference to the ith member of its internal data array.

 inline Scalar& value(size_t i) { return m_values[i]; }

So, I concluded that valuePtr() returns the address of the first element of an array. I should, then, be able to allocate space for that array by malloc.

In case someone is interested, I include a link for reference - please see lines after 84, and 131. Eigen3 SparseMatrix class

Aditya Kashi
  • 266
  • 2
  • 13
  • Never used Eigen, but I am fairly certain that this is not how one initializes Eigen matrices. You should read the docs. – Baum mit Augen Feb 26 '16 at 01:20
  • I agree with @BaummitAugen, however may i reccomend you avoid malloc entirely and use new and delete (or better, a C++11 shared_ptr, or unique_ptr. – John Bargman Feb 26 '16 at 01:44
  • @JohnBargman I used malloc because that's what they use internally in Eigen. I assumed it would be better for their destructor. – Aditya Kashi Feb 26 '16 at 02:45
  • @BaummitAugen I know this is non-standard. I need this because I have my own sparse matrix implementation that I want to use with Eigen's sparse LU decomposition routines. – Aditya Kashi Feb 26 '16 at 02:46
  • It's very unlikely (and I mean very) they would need you to allocate data to be used internally by their class (this pretty much violates memory-ownership and object-orientation semantics). Are you are supposed to be doing what you're doing? – John Bargman Feb 26 '16 at 02:47
  • @AdityaKashi - with all due respect, I think you might be going about this incorrectly - Can't you create a new class that inherits from the Eigen one, then overload it's methods / constructor? If not, the very best you can hope for is a hack, and one that will no-doubt be very buggy. – John Bargman Feb 26 '16 at 02:49
  • 2
    @AdityaKashi I think you may actually want this : http://eigen.tuxfamily.org/dox/classEigen_1_1MappedSparseMatrix.html – John Bargman Feb 26 '16 at 03:07
  • ITT: Stop messing around with library internals, use the documented interfaces, in this case construct the matrices with the appropriate constructors. – Baum mit Augen Feb 26 '16 at 03:09
  • @JohnBargman Thanks for your time; I'll take a look. – Aditya Kashi Feb 26 '16 at 03:16
  • @AdityaKashi I'm really glad I was able to help you! I've updated my answer to help you understand why what you did didn't work. – John Bargman Feb 27 '16 at 01:15

2 Answers2

1

The problem is most likely due to you trying to assign a object to a method!

You lines equate to: A.valuePtr() = Sometype_Pointer;, A.valuePTR() will Call the Eigen::SparseMatrix::valuePTR method(function) of object A.

If this method returns a pointer (T* A::valuePtr()) then the pointer returned isn't what you want! the method would be returning a rvalue; an rvalue is not a lvalue that isn't the pointer contained in A, rather it's a temporary copy of the pointer.
You can't assign anything directly to it, just like you can't touch the people on a television screen.

I'm making a massive assumption that your trying to do what I described above, and that would be impossible unless that method looked like this:

T ** Eigen::SparseMatrix::valuePTR()
{
    return &T;
}
*A.valuePtr() = static_cast<T*>(std::malloc(nnz*sizeof(T)));

However, note that using malloc is considered very, very out-dated for c++ objects, something like this line would be more correct *A.valuePtr() = static_cast(new T[nnz]));

[edit/]

Honestly I'm trying very hard to understand what your code is supposed to accomplish. And most people (including myself) will not be familair with this "Eigen" class. Though I think you may very-much misunderstand how it's supposed to be used.

Could you possibly provide a link to documentation, or an example of how your creating / using this class?

[Edit 2/]

After some research I came across This article as well as This Question, could it be possible you need to use a MappedSparseMatrix

[edit 3/]

inline Scalar* valuePtr() { return &m_data.value(0); } will return a rvalue pointer. This isn't the object held in m_data, nor is it the pointer used internally. It's a temporary copy of a pointer to an existing object, trying to say "make this temporary copy point to something else" doesn't make sense.

inline Scalar& value(size_t i) { return m_values[i]; } In this case, the method returns a reference to an object, a reference is not a pointer - (though the & often confuses people).
A reference can be understood as the original object, you don't need to dereference a reference. Consider the following.

int A;
int * A_p = &A;
int & A_r = A;
int & A_r_2 = *A_p;

//de-reference the pointer (to create a reference)
*A_p = 10;  //A == 10
//Assign directly to A (via the reference)
A_r = 12; // A == 12
//assign directy to A (via the reference to A, that was taken from A_p
A_r_2 = 15; // A == 15
//attempt to de-reference a reference
*A_r = 10; //ERROR, A_r is not a pointer.

In short, The reference being returned isn't a pointer to the object (that already exists), rather it is the object. This would allow you to set the object to something, or call methods on the object using the . operator, however you cannot re-allocate something that already exists, and as such is an error.

Please consider reading this article on the difference between rvale and lvalue's in C++ understanding rvalue and lvalue

Community
  • 1
  • 1
John Bargman
  • 1,267
  • 9
  • 19
  • The right approach is indeed to use a `MappedSparseMatrix` (with 3.2), or better a `Map >` with Eigen 3.3. – ggael Feb 26 '16 at 21:41
  • @John Bargman Thanks, MappedSparseMatrix works - it has a convenient constructor to set the arrays. But I still don't understand why my earlier method does not work. As I explained in the edit, I should get an lvalue when I call valuePtr(). – Aditya Kashi Feb 26 '16 at 22:25
  • @AdityaKashi, what I said holes true for valueptr, it returns a pointer, however that pointer is a 'rvalue' copy of the original - assigning to it doesn't make sense as it won't effect the original. – John Bargman Feb 27 '16 at 01:02
  • @AdityaKashi, as for the value(int) method, that returns a reference to an object - but the object is the actual object. and int& is not the same as int*. I'll update my answer shortly. – John Bargman Feb 27 '16 at 01:04
0

After @John Bargman told me that the way I think about rvalues and lvalues is wrong, I thought about it and tried this:

class A
{
    char* str;
public:

    // This function returns a literal (constant) of type char* 
    // (just an address), not a pointer variable
    char* getstr() {
        return str;
    }

    char getchar(const int i) const {
        return str[i];
    }
};

int main()
{
    A a;

    // the RHS can't be assigned to a literal, we need a variable in the LHS
    a.getstr() = static_cast<char*>(malloc(10*sizeof(char)));   // ILLEGAL!

    // this assigns the address contained in str to mstr.
    char* mstr = a.getstr();

    // after this, mstr will have nothing to do with A::str; the former 
    // will have been re-assigned to a different address
    mstr = static_cast<char*>(malloc(10*sizeof(char)));

    for(int i = 0; i < 9; i++)
        mstr[i] = '0';
    mstr[9] = '\0';

    // The following line segfaults as no storage has been allocated to A::str
    cout << a.getchar(1) << endl;   // runtime error!

    free(mstr);
    return 0;
}

Now I understand that the function SparseMatrix::valuePtr(), like A::getstr(), returns a literal of some address type; it does not return a lvalue. Trying to assign something (in this case, the address of the start of a block of memory returned by malloc) to a literal (in this case, some address like 0x233ff554) is meaningless. I think I understand now how it works. Thanks @John Bargman!

Aditya Kashi
  • 266
  • 2
  • 13