kyamagu / mexplus

C++ Matlab MEX development kit.
Other
99 stars 49 forks source link

Getting matrix data out of a struct #17

Closed patrikhuber closed 7 years ago

patrikhuber commented 7 years ago

Hi!

I'm trying to pass a struct from Matlab to C++. The struct contains several matrices:

mesh = 
  struct with fields:

     vertices: [845×4 double]
          tvi: [1610×3 int32]

I'm having trouble reading this back into my C++ class.

template<>
void MxArray::to(const mxArray* in_array, eos::core::Mesh* mesh)
{
    MxArray array(in_array);
    // Ideally I'd like to do this:
    mesh.vertices = array.at<std::vector<glm::vec4>>("vertices");
    // (where Mesh::vertices is a std::vector<glm::vec4> obviously).

    // ... some more code, omitted ...
}

This fails to compile with a few errors like:

Error C2039 'assign': is not a member of 'glm::tvec4<float,0>' Error C2039 'resize': is not a member of 'glm::tvec4<float,0>'

Of course I have defined a template specialisation to read this:

template<>
void MxArray::to(const mxArray* in_array, std::vector<glm::vec4>* vec)
{
    MxArray arr(in_array);
    // ... implementation ...
};

But it doesn't seem to get used. Also, defining a ::to method for just glm::vec4 doesn't fix it and results in the same compile error.

I'm a bit confused by this as when I go the opposite way, from a C++ Mesh to a Matlab struct, everything works great - I've defined a ::from method for Mesh which calls out_array.set("vertices", mesh.vertices);, which in turns calls the ::from specialisation for std::vector<glm::tvec4<float>> that I have defined.

It seems to me like the only way to read a matrix from a Matlab struct in C++ is through vector; if I do this inside the Mesh's ::to method, it compiles:

auto vertices = array.at<std::vector<double>>("vertices");

But this is quite cumbersome and repetitive I then need to convert manually from this vector<double> to my types (reshape, etc.), and I think it defies the whole purpose of the very nice concept of these converters with the to and from methods.

Am I missing something, or what is the best way to accomplish this?

Thanks!

kyamagu commented 7 years ago

@patrikhuber Is there any difference between glm::tvec4<float,0> and glm::vec4<float>?

Usually it is a matter of defining ::to for glm::vec4<float>*. But vec4 seems to be also a template, and because of that, template type inference might be failing in the compiler side.

patrikhuber commented 7 years ago

Is there any difference between glm::tvec4<float,0> and glm::vec4?

No, 0 (it's glm::precision::packed_highp) is the default. But just to be sure, I played around more with it, simplifying it a bit: leaving out the std::vector, and specifying all template arguments explicitly, and it still gives the same error.

However I think I got closer to the issue: I think this is an issue with how the ::to and ::from instantiation works in input.get<T>(arg_nb); (from InputArguments) versus how it works in array.at<T>(...) (from MxArray).

For example this works fine, and calls the ::to method for glm::vec4*:

const auto vec = input.get<glm::vec4>(0);

In contrast, array.at<glm::vec4>("vertices"); doesn't compile - I don't think this tries to go via the ::to methods at all? It tries to use its own methods with a different mechanism, which use value->assign(...) and value->resize(...).

I can call ::to manually on the element of the struct, but it's really ugly and cumbersome:

const mxArray* element = mxGetField(array.get(), 0, "vertices");
MxArray vtx_sub_ele(element);
glm::vec4 vec;
mexplus::MxArray::to(vtx_sub_ele.get(), &vec);

Am I correct that these are two different overload mechanisms happening here, because it works in the first case, from InputArguments, but not in the second case? Do you have any suggestions as to a solution?

kyamagu commented 7 years ago

@patrikhuber There could be a missing API in MxArray for at() support. Just to check, does the following work?

array.at("vertices", &mesh.vertices);
patrikhuber commented 7 years ago

array.at("vertices", &mesh.vertices);

It produces the same compile error than the other at function overload that I used:

Error C2039 'assign': is not a member of 'glm::tvec4<float,0>' Error C2039 'resize': is not a member of 'glm::tvec4<float,0>'

kyamagu commented 7 years ago

@patrikhuber I see. This can be marked a bug, as the internal implementation of at is not able to instantiate the correct function.

The temporary workaround is perhaps to use to as you've found.

MxArray vertices(array.at("vertices"));
vertices.to(&mesh.vertices);
patrikhuber commented 7 years ago

MxArray vertices(array.at("vertices")); vertices.to(&mesh.vertices);

Actually, this produces above compile errors too.

So we've got the following:

glm::vec4 vec;
MxArray vertices(array.at("vertices"));
vertices.to(&vec); // produces above compile errors
mexplus::MxArray::to(vertices.get(), &vec); // works
kyamagu commented 7 years ago

@patrikhuber Alright, this is yet another bug. Will check.

patrikhuber commented 7 years ago

Thank you very much!

patrikhuber commented 7 years ago

I think I know why it works in the case of InputArguments:

InputArguments::get(...) explicitly calls ::to: MxArray::to<T>(get(index), value);

While in the other case, probably some function overload "magic" happens.

kyamagu commented 7 years ago

@patrikhuber Yes, the thing is that at and to are using different internal conversion logic, and that's why the behavior is inconsistent. I am thinking about unifying some of the internal implementation. At least cell and struct access can rely on to conversion inside.

patrikhuber commented 7 years ago

Okay! That makes sense and explains my observation. I'm manually calling MxArray::to right now in many places. Looking forward to the improvements! :-)

Thanks for your time and this great library!

kyamagu commented 7 years ago

@patrikhuber PR #21 should solve this specific issue. Good luck

patrikhuber commented 7 years ago

I can confirm this fixes the issue, and array.at("vertices", &mesh->vertices); now finds the correct overload!

Thanks very much for your time and effort! :-)