Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

Copying 2d array within a mex file

131 views
Skip to first unread message

Iain Styles

unread,
Feb 16, 2009, 12:23:01 PM2/16/09
to
Hi,

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);

}

James Tursa

unread,
Feb 16, 2009, 2:26:02 PM2/16/09
to

MATLAB array memory is column based, like Fortran. i.e., element (1,1) is first in memory, followed by (2,1), (3,1), etc. C array memory is row based, so [0][0] is first, followed by [0][1], then [0][2], etc. It looks like you are building a typical 2-dimensional row-based C matrix and then doing a simple memcpy to get it into a MATLAB 2-dimensional variable of the same size. That won't work. You need to transpose it either on the C side or on the MATLAB side. For example, the easiest thing to do would be to change this line:

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

James Tursa

unread,
Feb 16, 2009, 2:52:01 PM2/16/09
to

I would also point out that you are not doing your 2-dimensional matrix allocation correctly if you want to copy all of it at once like you do later on in the code. You are counting on three successive calls to mxMalloc returning three memory blocks that are contiguous to each other:

> 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

Iain Styles

unread,
Feb 17, 2009, 12:15:04 PM2/17/09
to
James - thanks for the advice. I've taken your comments on board and modified the code accordingly. I found your second comment about memory contiguity especially useful.

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

James Tursa

unread,
Feb 17, 2009, 2:26:01 PM2/17/09
to
"Iain Styles" <I.B.Styles...@cs.bham.ac.uk> wrote in message <gnerao$sat$1...@fred.mathworks.com>...

Change this:

memcpy(mxGetPr(tmp),os.z,12*sizeof(double));

to this:

memcpy(mxGetPr(tmp),os.z[0],12*sizeof(double));

James Tursa

Iain Styles

unread,
Feb 17, 2009, 3:07:01 PM2/17/09
to

"James Tursa" <aclassyguy...@hotmail.com> wrote in message
> Change this:
> memcpy(mxGetPr(tmp),os.z,12*sizeof(double));
> to this:
> memcpy(mxGetPr(tmp),os.z[0],12*sizeof(double));

Brilliant - that works just as I want. Many thanks for your help.

Iain Styles

0 new messages