简体   繁体   中英

MATLAB crashes when it reaches mxSetPr in a mex file

I am writing a function within in a mex file which calls a MATLAB function. Unfortunately, when it comes to mxSetPr , MATLAB crashes and does not proceed further. Could someone kindly tell me how I can fix this?

void myconv2( double * Ain  , double *Aout, 
              int AnRows    , int AnCols, 
              double* kernel, int kernelnRows, int kernelnCols )
{

    mxArray *rhs[3], *lhs[1];

    rhs[0] = mxCreateNumericMatrix( 0, 0, mxDOUBLE_CLASS, mxREAL );
    rhs[1] = mxCreateNumericMatrix( 0, 0, mxDOUBLE_CLASS, mxREAL );
    rhs[2] = mxCreateString       ( "same" );

    mxSetPr( rhs[0], Ain    );
    mxSetM ( rhs[0], AnRows );
    mxSetN ( rhs[0], AnCols );


    mxSetPr( rhs[1], kernel      );
    mxSetM ( rhs[1], kernelnRows );
    mxSetN ( rhs[1], kernelnCols );


    mexCallMATLAB(1, lhs, 3, rhs, "conv2");
    Aout = mxGetPr( lhs[0] );

}

The memory passed to mxSetPr must be allocated with mxMalloc or mxCalloc , NOT with malloc , new , etc. From the mxSetPr docs page :

void mxSetPr(mxArray *pm, double *pr);
...
pr

Pointer to the first element of an array. Each element in the array contains the real component of a value. The array must be in dynamic memory; call mxCalloc to allocate this memory. Do not use the ANSI® C calloc function, which can cause memory alignment issues leading to program termination . If pr points to static memory, memory leaks and other memory errors can result.

Another issues is with your declaration for myconv2 , where the output argument is double *Aout . Inside the function, Aout itself is modified, not whatever Aout originally points to, if anything. To modify the pointer, either pass by reference ( double *&Aout ), or pass a double pointer ( double **Aout ) and change your calling convention. Its easiest to use a reference if you are compiling as C++.

This will get you past the mxSetPr commands and allow you to run mexCallMATLAB , but the next issue is that the mxArray pointed to by plhs[1] is created inside myconv2 , which means it will be deallocated by the MATLAB memory manager whenever it likes after the program termination. You should either copy the data out of lhs[1] and into Aout (I think this is what you want since you pass the pointer by value) or allocate Aout outside of myconv2 with mxMalloc or mxCalloc .

Another fun surprise with having mxSetPr in a different function from the one that created the mxArray* being assigned is that when myconv2 exists, MATLAB will also try to destroy each mxArray in rhs[] . Assuming you want the caller to be responsible for these arrays, then set the pointers to NULL before myconv2 exits. I've never had come across this situation before, but this might be a valid approach.


Copying the data inside myconv

#include "mex.h"

void myconv2(double * Ain, double *Aout,
    int AnRows, int AnCols,
    double* kern, int kernelnRows, int kernelnCols)
{
    mxArray *rhs[3], *lhs[1];

    rhs[0] = mxCreateNumericMatrix(0, 0, mxDOUBLE_CLASS, mxREAL);
    rhs[1] = mxCreateNumericMatrix(0, 0, mxDOUBLE_CLASS, mxREAL);
    rhs[2] = mxCreateString("same");

    mxSetPr(rhs[0], Ain);
    mxSetM(rhs[0], AnRows);
    mxSetN(rhs[0], AnCols);

    mxSetPr(rhs[1], kern);
    mxSetM(rhs[1], kernelnRows);
    mxSetN(rhs[1], kernelnCols);

    mexCallMATLAB(1, lhs, 3, rhs, "conv2");

    // prevent `Ain` and `kern` from being destroyed on `myconv2` return
    mxSetPr(rhs[0], NULL); mxSetPr(rhs[1], NULL);

    // copy each element of output
    double *d = mxGetPr(lhs[0]);
    for (int i = 0; i < AnRows*AnCols; ++i)
        Aout[i] = d[i];

}

void mexFunction(int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[])
{
    int nrows = 256, ncols = 256;
    double *Aout = new double[nrows * ncols];

    // allocate with `mxMalloc`
    double *Ain = (double*)mxMalloc(nrows * ncols * sizeof(double));
    double *kern = (double*)mxMalloc(5 * 5 * sizeof(double));

    myconv2(Ain, Aout, nrows, ncols, kern, 5, 5);

    // free here, not in `myconv2`
    mxFree(Ain);
    mxFree(kern);

    // do something with Aout
    mexPrintf("%p\n", Aout);

    delete[] Aout;
}

Creating lhs outside of myconv2 to avoid any copies

void myconv2(double *Ain, mxArray *&lhs, int AnRows, int AnCols,
    double *kern, int kernelnRows, int kernelnCols)
{
    mxArray *rhs[3];

    rhs[0] = mxCreateNumericMatrix(0, 0, mxDOUBLE_CLASS, mxREAL);
    rhs[1] = mxCreateNumericMatrix(0, 0, mxDOUBLE_CLASS, mxREAL);
    rhs[2] = mxCreateString("same");

    mxSetPr(rhs[0], Ain);
    mxSetM(rhs[0], AnRows);
    mxSetN(rhs[0], AnCols);

    mxSetPr(rhs[1], kern);
    mxSetM(rhs[1], kernelnRows);
    mxSetN(rhs[1], kernelnCols);

    mexCallMATLAB(1, &lhs, 3, rhs, "conv2");

    mxSetPr(rhs[0], NULL); mxSetPr(rhs[1], NULL);
}

void mexFunction(int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[])
{
    mxArray *AoutMX;

    int nrows = 256, ncols = 256;
    double *Ain = (double*)mxMalloc(nrows * ncols * sizeof(double));
    double *kern = (double*)mxMalloc(5 * 5 * sizeof(double));

    myconv2(Ain, AoutMX, nrows, ncols, kern, 5, 5);

    mxFree(Ain); mxFree(kern);

    // do something with AoutMX
    mexPrintf("%x\n", mxGetPr(AoutMX));    
}

Although, it's important to note that when you call conv2 , it always creates a new mxArray , even if you use the same mxArray* .


For completeness, it is worth noting that there are low-level hacks that let you assign any pointer (not just those created with mxMalloc and mxCalloc ), but these involve guessing the structure of the opaque type mxArray and doing something like ths:

// effectively, mxSetPr(mxmat, Pr + n); 
((mxArray_type_guess*)(mxmat))->data.number_array.pdata = (Pr + n);

See the InplaceArray FEX submission for more info. The mxArray struct guesses are probably badly out of date though. Supposedly you can determine the right structure with this code .

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM