1

I am working on code for a library that uses Eigen extensively, and frequently maps Eigen::Matrix objects with an NSObject subclass of my own design (vMAT_Array). Working with the library often requires marshaling matrices into vMAT_Array instances to they can be passed around, etc.

I have a vMAT_cast template function which handles this:

template <typename EigenObjectType>
vMAT_Array * vMAT_cast(EigenObjectType matrix)
{
    return Map<EigenObjectType>(matrix).matA;
}

The problem with this function is that it does not interact correctly with Eigen's lazy evaluation semantics. Take for example the unit test code below:

vMAT_Array * matM = vMAT_cast(VectorXd::LinSpaced(40, 1.0, 40.0).eval());
[matM reshape:vMAT_MakeSize(5, 8)];
Mat<double> M = matM;
Array<bool, Dynamic, Dynamic> sel = M.unaryExpr([](double elt) { return (int)elt % 3 == 0; }).cast<bool>();
vMAT_Array * vecN = vMAT_pick(matM, vMAT_cast(sel));
NSLog(@"%@", vecN.dump);
vMAT_Array * vecNv = vMAT_cast(VectorXd::LinSpaced(13, 3.0, 39.0).eval());
STAssertEqualObjects(vecN, vecNv, @"Logical indexing broken");

Notice the explicit .eval() calls on most of the arguments to vMAT_cast. These are necessary because the template function tries (at compile time) to expand into code using one of Eigen's lazy expression templates, which generates lovely error messages like this one:

/Users/Shared/Source/vMAT/vMATTests/EigenTests.mm:25:35: note: in instantiation of member function 'Eigen::DenseBase<Eigen::Matrix<double, -1, 1, 0, -1, 1> >::LinSpaced' requested here
    vMAT_Array * matM = vMAT_cast(VectorXd::LinSpaced(40, 1.0, 40.0));
                                  ^
In file included from /Users/Shared/Source/vMAT/vMATTests/EigenTests.mm:11:
In file included from /Users/Shared/Source/vMAT/vMAT/vMAT.h:51:
/Users/Shared/Source/vMAT/vMAT/vMAT_Array.h:122:82: error: no member named 'data' in 'Eigen::CwiseNullaryOp<Eigen::internal::linspaced_op<double, true>, Eigen::Matrix<double, -1, 1, 0, -1, 1> >'
                                        data:[NSMutableData dataWithBytes:matrix.data()
                                                                          ~~~~~~ ^

I suspect there is template-fu that would "force" the MatrixBase::eval to happen, but I lack knowledge of it. Can anyone enlighten me?

Kaelin Colclasure
  • 3,925
  • 1
  • 26
  • 36

1 Answers1

2

You can find this kind of template kung-fu in eigen/unsupported/Eigen/OpenGLSupport module. Here you will find wrappers to OpenGL functions taking vectors and matrices through raw C pointers. The key is in this test where we check whether the expression type is OpenGL compatible:

bool IsGLCompatible =  bool(XprType::Flags&LinearAccessBit)                                              
                    && bool(XprType::Flags&DirectAccessBit)                                               
                    && (XprType::IsVectorAtCompileTime || (XprType::Flags&RowMajorBit)==0)

LinearAccessBit means there is no "stride", DirectAccessBit means there is a .data() available. There other two are obvious and maybe not relevant in your case.

Another option for you, probably much simpler, is to use the new Ref<> class of the devel branch. I refer to the doc for the details of this approach.

ggael
  • 28,425
  • 2
  • 65
  • 71
  • Ah, I *think* I see how the expression works… It is evaluated at compile-time, and if `IsGLCompatible` is `false` there is a specialization that "fixes" the argument before calling the function. I'm not seeing how I might use the `Ref` template class instead though. – Kaelin Colclasure Apr 19 '13 at 13:05
  • Let's say you can only map sequentially stored column-major matrices, then you declare your `vMAT_cast` function as `vMAT_cast(Ref > matrix);` Any expression that are not "similar" to a `MatrixXd` will be evaluated. If `vMAT_Array` supports strides, then declare it as: `vMAT_cast(Ref matrix)`. – ggael Apr 19 '13 at 15:34
  • Thanks for clarifying… My difficulty is that I don't know ahead of time what type of matrix a library user might want to cast. My **Objective-C** library is trying to use `vMAT_Array *`'s as the "ref" type you can easily pass to and return from functions, methods, etc. So maybe I need to dig into the source for the **Eigen** `Ref` stuff at some point? But since I can (mostly) understand how the first approach you outlined works, I think I will try that first. (For the moment I am still sprinkling `.eval()` everywhere…) – Kaelin Colclasure Apr 20 '13 at 03:49