I have found a really tricky problem, which I can not seem to fix easily. In short, I would like to return from a mex file an array, which has been passed as mex function input. You could trivially do this:
void mexFunction(int nargout, mxArray *pargout [ ], int nargin, const mxArray *pargin[])
{
pargout[0] = pargin[0];
}
But this is not what I need. I would like to get the raw pointer from pargin[0]
, process it internally, and return a freshly created mex array by setting the corresponding data pointer. Like that:
#include <mex.h>
void mexFunction(int nargout, mxArray *pargout [ ], int nargin, const mxArray *pargin[])
{
mxArray *outp;
double *data;
int m, n;
/* get input array */
data = mxGetData(pargin[0]);
m = mxGetM(pargin[0]);
n = mxGetN(pargin[0]);
/* copy pointer to output array */
outp = mxCreateNumericMatrix(0,0,mxDOUBLE_CLASS,mxREAL);
mxSetM(outp, m);
mxSetN(outp, n);
mxSetData(outp, data);
/* segfaults with or without the below line */
mexMakeMemoryPersistent(data);
pargout[0] = outp;
}
It doesn't work. I get a segfault, if not immediately, then after a few calls. I believe nothing is said about such scenario in the documentation. The only requirement is hat the data
pointer has been allocated using mxCalloc
, which it obviously has. Hence, I would assume this code is legal.
I need to do this, because I am parsing a complicated MATLAB structure into my internal C data structures. I process the data, some of the data gets re-allocated, some doesn't. I would like to transparently return the output structure, without thinking when I have to simply copy an mxArray
(first code snippet), and when I actually have to create it.
Please help!
EDIT
After further looking and discussing with Amro, it seems that even my first code snippet is unsupported and can cause MATLAB crashes in certain situations, e.g., when passing structure fields or cell elements to such mex function:
>> a.field = [1 2 3];
>> b = pargin_to_pargout(a.field); % ok - works and assigns [1 2 3] to b
>> pargin_to_pargout(a.field); % bad - segfault
It seems I will have to go down the 'undocumented MATLAB' road and use mxCreateSharedDataCopy
and mxUnshareArray
.
While undocumented, the MEX API function
mxCreateSharedDataCopy
iswas given as a solution by MathWorks, now apparently disavowed, for creating a shared-data copy of anmxArray
. MathWorks even provides an example in their solution,mxsharedcopy.c
.As described in that removed MathWorks Solution (1-6NU359), the function can be used to clone the
mxArray
header. However, the difference between doingplhs[0] = prhs[0];
andplhs[0] = mxCreateSharedDataCopy(prhs[0]);
is that the first version just copies themxArray*
(a pointer) and hence does not create a newmxArray
container (at least not until themexFunction
returns and MATLAB works it's magic), which would increment the data's reference count in bothmxArray
s.Why might this be a problem? If you use
plhs[0] = prhs[0];
and make no further modification toplhs[0]
before returning frommexFunction
, all is well and you will have a shared data copy thanks to MATLAB. However, if after the above assignment you modifyplhs[0]
in the MEX function, the change be seen inprhs[0]
as well since it refers to the same data buffer. On the other hand, when explicitly generating a shared copy (withmxCreateSharedDataCopy
) there are two differentmxArray
objects and a change to one array's data will trigger a copy operation resulting in two completely independent arrays. Also, direct assignment can cause segmentation faults in some cases.Modified MathWorks Example
Start with an example using a modified
mxsharedcopy.c
from the MathWorks solution referenced above. The first important step is to provide the prototype for themxCreateSharedDataCopy
function:As the comment states, this is not in
mex.h
, so you have to declare this yourself.The next part of the
mxsharedcopy.c
creates newmxArray
s in the following ways:A deep copy via
mxDuplicateArray
:A shared copy via
mxCreateSharedDataCopy
:Direct copy of the
mxArray*
, added by me:Then it prints the address of the data buffer (
pr
) for eachmxArray
and their first values. Here is the output of the modifiedmxsharedcopy(x)
forx=ones(1e3);
:What happened:
prhs[0]
andcopy0
we have not created anything new except another pointer to the samemxArray
.prhs[0]
andcopy1
, notice thatmxDuplicateArray
created a newmxArray
at address721BF120
, and copied the data into a new buffer at19740060
.copy2
has a different address (mxArray*
) fromcopy1
, meaning it is also a differentmxArray
not just the same one pointed to by different variables, but they both share the same data at address19740060
.The question reduces to: Is it safe to return in
plhs[0]
either ofcopy0
orcopy2
(from simple pointer copy ormxCreateSharedDataCopy
, respectively) or is it necessary to usemxDuplicateArray
, which actually copies the data? We can show thatmxCreateSharedDataCopy
would work by destroyingcopy1
and verifying thatcopy2
is still valid:Applying Shared-Data Copy to Input
Back to the question. Take this a step further than the MathWorks example and return a share-data copy of the input. Just do:
Hold your breath!
Now for a temporary input (without
format debug
):Interestingly, if I test without
mxCreateSharedDataCopy
(i.e. with justplhs[0] = prhs[0];
), MATLAB doesn't crash but the output variable never materializes:R2013b, Windows, 64-bit.
mxsharedcopy.cpp (modified C++ version):
You should use
mxDuplicateArray
, thats the documented way: