I have integrated C++ into Matlab; in my example there are three simple files: a Matlab script main.m
, a Matlab class file myClass.m
and a C++ file myMex.cpp
.
In main.m
I call the class giving a string as input.
main.m
:myVar = myClass('test.bin');
myClass.m
:classdef myClass < handle
properties
bitStreamName;
cabac_handle;
end
methods
function obj = myClass(fn)
obj.bitStreamName = fn;
obj.cabac_handle = myMex('First', obj.bitStreamName);
myMex('Second', obj.cabac_handle);
end
end
end
myMex.cpp
:#include "mex.h"
#include <iostream>
using namespace std;
void _main();
class CABAC {
public:
CABAC() {};
~CABAC() {};
char* fn;
};
// the MEX interface function
void mexFunction(
int nlhs, // Number of expected output mxArrays
mxArray *plhs[], // Array of pointers to the expected output mxArrays
int nrhs, // Number of input mxArrays
const mxArray *prhs[] // Array of pointers to the input mxArrays
)
{
CABAC *c; // pointer to (new) instance of the CABAC class
char* fn = 0;
char cmd[64]; // temp char array to hold the command
// start parsing the input command
mxGetString(prhs[0], cmd, sizeof(cmd));
string inputCmd(cmd);
if (inputCmd == "First")
{
// get the filename string
fn = mxArrayToString(prhs[1]);
c = new CABAC;
uintptr_t c_value = (uintptr_t)c;
plhs[0] = mxCreateDoubleMatrix(1, 1, mxREAL);
*mxGetPr(plhs[0]) = c_value; // return pointer to matlab environment
c->fn = fn;
mexPrintf("Pointer: %p\n", c);
mexPrintf("Filename: %s\n", c->fn);
}
else if (inputCmd == "Second")
{
uintptr_t my_value = *mxGetPr(prhs[1]);
CABAC *my_pointer = (CABAC *)my_value;
mexPrintf("Pointer: %p\n", my_pointer);
mexPrintf("Filename: %s\n", my_pointer->fn);
}
}
The first time myMex.cpp
is called, I create an object of the class CABAC
and the string "test.bin"
is associated to its attribute fn
. Finally, the value of the pointer is returned to the Matlab environment.
The second time, I simply get the pointer to the previously instanced object, and here something weird happens. In particular, printing both the address of the pointer and the attribute fn
, the former is always correct (i.e., the address is the same of the object), but the latter is sometimes correct ("test.bin"
is printed) and sometimes totally and randomly wrong (strange strings appear, differently for each execution).
To execute my code, you can simply run main.m
after compiling myMex.cpp
with this instruction:
mex CXXFLAGS="\$CXXFLAGS -std=c++11" -g myMex.cpp
or
mex COMPFLAGS="\$CXXFLAGS -std=c++11" -g myMex.cpp
Can you help me to understand what happens to the filename?
EDIT: it seems that c
is deleted after the first myMex
call. How can I keep that pointer to the CABAC
object (with all its attributes) in memory?
There are many issues here. First of all, you are casting a pointer to a double
, which doesn't preserve its representation:
uintptr_t c_value = (uintptr_t)c;
plhs[0] = mxCreateDoubleMatrix(1, 1, mxREAL);
*mxGetPr(plhs[0]) = c_value; // return pointer to matlab environment
Instead, create a 64-bit integer array to save the pointer to:
plhs[0] = mxCreateNumericMatrix(1, 1, mxUINT64_CLASS, mxREAL);
*(uint64_t*)mxGetData(plhs[0]) = (uint64_t)c;
(Since MATLAB R2018a there are alternatives to mxGetData
for each type, in this case it would be mxGetUint64s
).
Second, the memory new
ed would likely be deleted when the MEX-file gets unloaded. This can happen at any time. To prevent this, lock the MEX-file in memory using mexLock
. To prevent memory leaks, you will need to include code that deletes the memory when you're done with it. Since your MATLAB-class is a handle class, this can be accomplished.
Third, it is very easy to call the MEX-file with a wrong pointer, which will likely crash all of MATLAB. There is no way around that with your current implementation.
Fourth, as pointed out by Navan, the following line creates a copy of the string, but in a memory buffer maintained by MATLAB:
fn = mxArrayToString(prhs[1]);
MATLAB will automatically delete all memory allocated by mxMalloc
and similar at the end of mexFunction
. Thus, fn
will point to freed memory. You need to manually create a copy here in memory that you manage. I suggest you copy the string into a std::string
instead:
class CABAC {
public:
std::string fn;
};
fn = mxArrayToString(prhs[1]); // Will copy the string to `fn`.
However, instead I would recommend a different approach:
NULL
pointer (e.g. for a deleted object or for an array element that hasn't been used yet).You should know that global variables will persist between calls to the MEX-file (as long as it is locked). Thus, you would need a global array (defined outside of mexFunction
) to store the pointers in. Alternatively, declare the array static
inside the mexFunction
.
This MEX-file implements the recommendation described here, though it is far from a trivial example, I hope it might be useful as a starting point. In this function, I'm using a std::map
instead of plain array to store the handles.