I've been writing a Mex wrapper for some existing C code and have come across a problem. The routine I want to interface to returns a struct, and I would like to preserve this (ie. have the mex gateway routine return a struct)
The structs in the original contains scalars and arrays of 2,3 and 4 dimensions. To summarise what I'm doing (full code follows later), I first create the output structure using:
plhs[0] = mxCreateStructArray(1,odims,3,fnames);
(where odims = {1,1})
I then populate the struct using (with mxArray *tmp;)
/* scalar */
tmp = mxCreateDoubleScalar(os.x);
mxSetFieldByNumber(plhs[0],0,0,tmp);
/* 1d double array of length 5 */
tmp = mxCreateDoubleMatrix(1,5,mxREAL);
memcpy(mxGetPr(tmp),os.y,5*sizeof(double));
mxSetFieldByNumber(plhs[0],0,1,tmp);
/* 2d double array of size 3x4 */
tmp = mxCreateDoubleMatrix(3,4,mxREAL);
memcpy(mxGetPr(tmp),os.z,12*sizeof(double));
mxSetFieldByNumber(plhs[0],0,2,tmp);
On running the code, the scalars and 1d arrays are correct in the output struct, but the 2d (and 3/4d) arrays are not (dimensions are correct, contents are not). The appended code contains an example of this behaviour.
I'd be very grateful if any could help me figure out what's going wrong. I suspect it's very simple, but I've not written any mex code before and I've been staring at it for too long now.
Many thanks,
Iain Styles
------------------------------------------------------
#include"mex.h"
typedef struct
{
double x;
double *y;
double **z;
} ostruct;
void mexFunction(int nlhs, mxArray *plhs[],
int nrhs, const mxArray *prhs[])
{
int i,j;
ostruct os;
mxArray *tmp;
mwSize odims[2];
const char *fnames[] = {"x","y","z"};
/* No inputs, one output */
if( nrhs != 0 )
mexErrMsgTxt("Incorrect number of inputs - must have 0");
else if( nlhs != 1 )
mexErrMsgTxt("Incorrect number of outputs - must have 1");
/* Set up outputs */
odims[0] = 1;
odims[1] = 1;
plhs[0] = mxCreateStructArray(1,odims,3,fnames);
/* Allocate and populate the data */
os.x = 3.0;
os.y = mxMalloc(5*sizeof(double));
os.z = mxMalloc(3*sizeof(double*));
for(i=0;i<3;i++)
os.z[i] = mxMalloc(4*sizeof(double));
for(i=0;i<5;i++)
os.y[i] = 3.0*i;
for(i=0;i<3;i++)
for(j=0;j<4;j++)
os.z[i][j] = (double)i+(double)j;
/* Print the outputs so I can see what's going on */
mexPrintf("os.x = %g\n",os.x);
mexPrintf("os.y = ");
for(i=0;i<5;i++)
mexPrintf("%g ",os.y[i]);
mexPrintf("\n os.z = \n");
for(i=0;i<3;i++){
for(j=0;j<4;j++){
mexPrintf("%g ",os.z[i][j]);
}
mexPrintf("\n");
}
/* Copy outputs to plhs */
/* x */
tmp = mxCreateDoubleScalar(os.x);
mxSetFieldByNumber(plhs[0],0,0,tmp);
/* y */
tmp = mxCreateDoubleMatrix(1,5,mxREAL);
memcpy(mxGetPr(tmp),os.y,5*sizeof(double));
mxSetFieldByNumber(plhs[0],0,1,tmp);
/* x */
tmp = mxCreateDoubleMatrix(3,4,mxREAL);
memcpy(mxGetPr(tmp),os.z,12*sizeof(double));
mxSetFieldByNumber(plhs[0],0,2,tmp);
mxFree(os.y);
for(i=0;i<3;i++)
mxFree(os.z[i]);
mxFree(os.z);
}
tmp = mxCreateDoubleMatrix(3,4,mxREAL);
to this:
tmp = mxCreateDoubleMatrix(4,3,mxREAL);
and then once you get it into MATLAB you will have the transpose of what you want. Then you can just transpose it on the MATLAB side.
James Tursa
> os.z = mxMalloc(3*sizeof(double*));
> for(i=0;i<3;i++)
> os.z[i] = mxMalloc(4*sizeof(double));
i.e., you assume that they are contiguous as fact when you copy the entire memory block at once later on:
> memcpy(mxGetPr(tmp),os.z,12*sizeof(double));
But successive calls to mxMalloc are *not* guaranteed to give you contiguous blocks in memory. Moreover, even if you did check to make sure they were contiguous, it is, strictly speaking, against the rules to copy or read beyond the end of an allocated memory block. You would need to copy each 4-element row individually to have your code correct.
A better way instead would be allocating the entire block at once and then setting your pointers appropriately, like this:
os.z = mxMalloc(3*sizeof(double*));
os.z[0] = mxMalloc(12*sizeof(double));
os.z[1] = os.z[0] + 4;
os.z[2] = os.z[1] + 4;
That allows you to legally copy the entire block at once. And then when you free the memory, only free the first pointer, of course. So this:
> for(i=0;i<3;i++)
> mxFree(os.z[i]);
> mxFree(os.z);
would becomes this:
mxFree(os.z[0]);
mxFree(os.z);
James Tursa
However, it still hasn't fixed the problem: the 2d array returned in the structure is the right size, but contains garbage. If you have any other ideas I'd be grateful. My fallback position is to linearise the arrays for output and reshape them in Matlab, but I would rather avoid this if at all possible.
Many thanks.
Iain Styles
Change this:
memcpy(mxGetPr(tmp),os.z,12*sizeof(double));
to this:
memcpy(mxGetPr(tmp),os.z[0],12*sizeof(double));
James Tursa
Brilliant - that works just as I want. Many thanks for your help.
Iain Styles