Should have been simple to read a class instance from .mat via C++, but wasn't

4 vues (au cours des 30 derniers jours)
Hello:
I'm looking to write a class instance via Matlab and read it back from a C++ program. Writing the file from Matlab, opening it in C++, and identifying the type of class all work for me. Reading the class properties does not work.
The screenshot says it all:
In Matlab (editor and command window shown at far left):
- I defined a class simple with three scalar properties
- I instantiated the class and wrote it to file
In VS2013 (source editor at center right, debugger console window at top right):
- I opened the file via C++ console application using matOpen (not visible in the source editor, but did succeed)
- I called matGetDir to enumerate the variable names
- I looped over the variables, calling matGetVariable on each one
- I checked the name to confirm that we found an instance of "simple", visible in the DOS window at upper right in the screenshot
- I find zero elements, zero fields, and 2 dimensions. This doesn't sound good.
- mxIsClass verifies that the mxArray holds an instance of simple.
- mxIsEmpty returns true. This doesn't sound good either.
- I start looking for properties via mxGetProperty, checking variants that might work. None are found.
Somewhere here I've got a fundamental misconception. Does anyone have a suggestion?
Many thanks
Matthew

Réponse acceptée

Matthew Britton
Matthew Britton le 28 Oct 2014
SK:
You seem to have identified the root cause: no Matlab support for user-defined class I/O. Naturally I'm disappointed, but appreciate your stick-to-it-iveness.
I will check out the library that you have suggested.
Many thanks Matthew

Plus de réponses (10)

SK
SK le 25 Oct 2014
Modifié(e) : SK le 25 Oct 2014
Can't see any error in your code.
Perhaps you could make one change. Try using matGetNextVariable(), instead of matGetVariable().
Also I notice you are stopped in the debugger. Did you check the value of "num"? Is it 1 or greater than than 1.
  1 commentaire
Matthew Britton
Matthew Britton le 27 Oct 2014
Thank you for replying.
A call to matGetNextVariable returns null, as shown in the attached screenshot.
The value of num returned by matGetDir is 1.
Many thanks Ma
tthew

Connectez-vous pour commenter.


SK
SK le 27 Oct 2014
Modifié(e) : SK le 27 Oct 2014
OK, you are the victim of indifferent documentation.
If you check the example matdgns.c file in your matlab installation in:
[MATLAB]\extern\examples\eng_mat
You will find the required gem of information:
/*
* get directory of MAT-file
*/
dir = (const char **)matGetDir(pmat, &ndir);
if (dir == NULL) {
printf("Error reading directory of file %s\n", file);
return(1);
} else {
printf("Directory of %s:\n", file);
for (i=0; i < ndir; i++)
printf("%s\n",dir[i]);
}
mxFree(dir);
/* In order to use matGetNextXXX correctly, reopen file to read in headers. */
if (matClose(pmat) != 0) {
printf("Error closing file %s\n",file);
return(1);
}
pmat = matOpen(file, "r");
if (pmat == NULL) {
printf("Error reopening file %s\n", file);
return(1);
}
So after using matGetDir() you have to close the file and reopen it in order to read the variables from the file. Presumably the read pointer is in the wrong place after matGetDir().
Regards.

Matthew Britton
Matthew Britton le 27 Oct 2014
SK:
Thank you again for your response. I wish that matlab had provided an example to open a class instance...
In following your suggestion, I :
- modified the code to close and reopen the file after matGetDir
- added a call to matGetNextVariableInfo to follow the matdgns.c example
- closed and reopened the file again
- called matGetNextVariable to retrieve the class instance
- experienced the same failure to find any properties as I originally reported.
The screenshot is below.
Many thanks
Matthew

SK
SK le 27 Oct 2014
Modifié(e) : SK le 27 Oct 2014
The following works on my system (matlab 2014a) compiled with VS 2013. All error checking omitted to avoid excess typing.
In ReadMatFile.cpp
#include "mex.h"
#include "mat.h"
void mexFunction(int nlhs, mxArray* plhs[], int nrhs, const mxArray* prhs[])
{
const char* pmatfilename = mxArrayToString(prhs[0]);
MATFile* mfp = matOpen(pmatfilename, "r");
int numvars = 0;
char** pmatfileinfo = matGetDir(mfp, &numvars); //Dummy to test open/close
matClose(mfp);
mfp = matOpen(pmatfilename, "r");
const char* name = 0;
mxArray* pa = matGetNextVariable(mfp, &name);
const mxArray* pProp = 0;
const char* pStr = 0;
while (pa != NULL)
{
mexPrintf(name);
pProp = mxGetProperty(pa, 0, "prop_1");
pStr = mxArrayToString(pProp);
mexPrintf(pStr);
pProp = mxGetProperty(pa, 0, "prop_2");
pStr = mxArrayToString(pProp);
mexPrintf(pStr);
mxDestroyArray(pa);
pa = matGetNextVariable(mfp, &name);
}
mxFree(pmatfileinfo);
matClose(mfp);
}
Better to copy and paste your code into the text box rather than provide a screenshot otherwise it is a pain to type it all out.
The matlab class was:
classdef Test
properties
prop_1;
prop_2;
end
methods (Access = 'public')
function This = Test(p1, p2)
This.prop_1 = p1;
This.prop_2 = p2;
end
end
end
>> txx = Test('12345', 'sdfadfdf');
>> save('test.mat', 'txx');
>> mex ReadMatFile.cpp
>> ReadMatFile('test.mat');
txx12345sdfadfdf
  2 commentaires
James Tursa
James Tursa le 28 Oct 2014
Does this work from a cold start? I.e., start MATLAB and then run the mex routine ReadMatFile immediately without calling Test first?
SK
SK le 28 Oct 2014
Yes, works fine on cold start.
Regards.

Connectez-vous pour commenter.


Matthew Britton
Matthew Britton le 27 Oct 2014
SK:
Thank you for the code snippet. I successfully replicated your results via Matlab command window. The same code snippet does not successfully find the class properties when compiled into a console application and executed via the debugger. I'm getting the same behavior as in my previous posts: mxGetProperty returns NULL.
I see that Matlab understands how to compile the code snippet through the Matlab command window, as I get the message:
>> mex ReadMatFile.cpp Building with 'Microsoft Visual C++ 2013 Professional'. MEX completed successfully.
From VS, I linked in the matlab include files and libs in order to declare/define the library symbols (e.g. matOpen.) Is this an unsupported methodology, or is there some documentation that tells me how to successfully link? (The Matlab command window appears to know how this is done.)
Many thanks Matthew
  1 commentaire
SK
SK le 28 Oct 2014
Modifié(e) : SK le 28 Oct 2014
Should work. I've compiled dll's (mexw64's) using VS. The only issue is how to pass in Matlab data which is done via mexFunction. But in your case I assume you are not passing in any data from Matlab so as far as I know there is no need for a mexFunction. Are you linking in the correct matlab libraries
[MATLAB}extern\lib\win32\microsoft\
or [MATLAB}extern\lib\win64\microsoft\
as per your system.
Another thing you could try is use:
mex -v ReadMatFile.cpp
which produces verbose output from mex. This will tell you the visual C++ compiler options that mex is using. Make sure that your VS compiler options are compatible. [You dont need /export:mexFunction in the linker flags and also don't need the MATLAB_MEX_FILE preprocessor definition and the target will be .exe rather than mexw64.]

Connectez-vous pour commenter.


Matthew Britton
Matthew Britton le 28 Oct 2014
SK:
I tried the following code, slightly modified from your example above to provide main(), plus a small amount of error checking:
#include "mex.h"
#include "mat.h"
//void mexFunction(int nlhs, mxArray* plhs[], int nrhs, const mxArray* prhs[])
int main(int argc, char * argv[])
{
const char* pmatfilename = "C:\\Users\\mbritton\\Desktop\\test.mat";
MATFile* mfp = matOpen(pmatfilename, "r");
int numvars = 0;
char** pmatfileinfo = matGetDir(mfp, &numvars); //Dummy to test open/close
matClose(mfp);
mfp = matOpen(pmatfilename, "r");
const char* name = 0;
mxArray* pa = matGetNextVariable(mfp, &name);
const mxArray* pProp = 0;
const char* pStr = 0;
while (pa != NULL)
{
mexPrintf(name);
mexPrintf("\n");
pProp = mxGetProperty(pa, 0, "prop_1");
if (pProp == NULL) mexPrintf("NULL\n");
else {
pStr = mxArrayToString(pProp);
mexPrintf(pStr);
}
pProp = mxGetProperty(pa, 0, "prop_2");
if (pProp == NULL) mexPrintf("NULL\n");
else {
pStr = mxArrayToString(pProp);
mexPrintf(pStr);
}
mxDestroyArray(pa);
pa = matGetNextVariable(mfp, &name);
}
mxFree(pmatfileinfo);
matClose(mfp);
}
Here are the property pages showing the compile and link lines, which incorporate the R2014a includes and win64 lib directories.
The runtime outcome is the same:
I've attached the diary file containing output from
mex -v Source1.cpp
(Source1.cpp is a copy of the ReadMatFile.cpp, which I inadvertently renamed.)
There are differences in the compile/link lines.
To try to replicate your success with DLLs, I placed this code into a DLL with declaration
void mexFunction(int nlhs, mxArray* plhs[], int nrhs, const mxArray* prhs[])
rather than
int main(int argc, char * argv[])
and invoked this function from main via the debugger. I got the same NULL results.
It seems there's something specific that Matlab is doing during compilation/link, which I have not succeeded in replicating under VS.
Many thanks
Matthew

SK
SK le 28 Oct 2014
Modifié(e) : SK le 28 Oct 2014
Actually, I have only used the mex and mx API in the dll not the matXXX API. I'll test your code out later, but one thing I noticed in the diary file:
COMPFLAGS : /Zp8
Now 8 byte packing is the default for the microsoft compiler according to the Docs but perhaps you could try adding that flag explicity. Also try adding the MX_COMPAT_32 preprocessor flag which is present in the diary file:
/DMX_COMPAT_32

Matthew Britton
Matthew Britton le 28 Oct 2014
SK:
Thank you for the suggestions. Not to preempt your trying out the code, but I went ahead and added /Zp8 and /DMX_COMPAT_32, as shown below.
Outcome was the same: mxGetProperty returns null.
Many thanks Matthew

SK
SK le 28 Oct 2014
Modifié(e) : SK le 28 Oct 2014
On the other hand the bad news on This page is:
"The MAT-File Interface Library does not support MATLAB objects created by user-defined classes."
So officially reading user defined objects is not supported. Perhaps it worked with mex by fluke. Maybe you can check this Library that avoids the mat-File API altogether and relies on the published mat file format.

SK
SK le 28 Oct 2014
I guess one has to learn the hard way sometimes - but you always end up learning something - so the effort is not entirely wasted. Perhaps you could mark the above as an answer. It may help some poor fellow who has the same problem.
Good luck.
Regards.

Catégories

En savoir plus sur Write C Functions Callable from MATLAB (MEX Files) dans Help Center et File Exchange

Tags

Community Treasure Hunt

Find the treasures in MATLAB Central and discover how the community can help you!

Start Hunting!

Translated by