Archivos mex: cómo devolver una matriz de matlab ya asignada

He encontrado un problema realmente complicado, que no puedo solucionar fácilmente. En resumen, me gustaría regresar de un archivo mex una matriz, que se ha pasado como entrada de función mex. Usted podría hacer esto trivialmente:

void mexFunction(int nargout, mxArray *pargout [ ], int nargin, const mxArray *pargin[]) { pargout[0] = pargin[0]; } 

Pero esto no es lo que necesito. Me gustaría obtener el puntero sin pargin[0] de pargin[0] , procesarlo internamente y devolver una matriz pargin[0] recién creada configurando el puntero de datos correspondiente. Como eso:

 #include  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; } 

No funciona Recibo una segfault, si no de inmediato, luego de algunas llamadas. Creo que no se dice nada sobre ese escenario en la documentación . El único requisito es que el puntero de data haya sido asignado usando mxCalloc , que obviamente tiene. Por lo tanto, asumiría que este código es legal.

Necesito hacer esto, porque estoy analizando una estructura complicada de MATLAB en mis estructuras internas de datos C. Proceso los datos, algunos de los datos se vuelven a asignar, otros no. Me gustaría devolver de forma transparente la estructura de salida, sin pensar cuándo tengo que copiar simplemente un mxArray (primer fragmento de código), y cuando realmente tengo que crearlo.

¡Por favor ayuda!

EDITAR

Después de mirar y discutir con Amro, parece que incluso mi primer fragmento de código no es compatible y puede causar lockings de MATLAB en ciertas situaciones, por ejemplo, al pasar campos de estructura o elementos de celda a dicha función mex:

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

Parece que tendré que pasar por la carretera ‘MATLAB indocumentada’ y usar mxCreateSharedDataCopy y mxUnshareArray .

    Deberías usar mxDuplicateArray , mxDuplicateArray es la forma documentada:

     #include "mex.h" void mexFunction(int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[]) { plhs[0] = mxDuplicateArray(prhs[0]); } 

    Mientras no está documentado, la función de la API MEX mxCreateSharedDataCopy fue dada como una solución por MathWorks , ahora aparentemente desautorizada, para crear una copia de datos compartidos de un mxArray . MathWorks incluso proporciona un ejemplo en su solución, mxsharedcopy.c .

    Como se describe en esa solución MathWorks eliminada (1-6NU359), la función se puede utilizar para clonar el encabezado mxArray . Sin embargo, la diferencia entre hacer plhs[0] = prhs[0]; y plhs[0] = mxCreateSharedDataCopy(prhs[0]); es que la primera versión solo copia el mxArray* (un puntero) y por lo tanto no crea un nuevo contenedor mxArray (al menos no hasta que la función mexFunction regrese y MATLAB funcione es mágico), lo que incrementaría el recuento de referencias de datos en ambos mxArray s.

    ¿Por qué podría ser esto un problema? Si usa plhs[0] = prhs[0]; y no haga más modificaciones a plhs[0] antes de regresar de mexFunction , todo está bien y tendrá una copia de datos compartida gracias a MATLAB. Sin embargo, si después de la asignación anterior usted modifica plhs[0] en la función MEX , el cambio también se verá en prhs[0] ya que se refiere al mismo buffer de datos. Por otro lado, cuando se genera explícitamente una copia compartida (con mxCreateSharedDataCopy ) hay dos objetos mxArray diferentes y un cambio en los datos de una matriz desencadenará una operación de copia que resultará en dos matrices completamente independientes. Además, la asignación directa puede causar fallas de segmentación en algunos casos .

    Ejemplo modificado de MathWorks

    Comience con un ejemplo utilizando mxsharedcopy.c modificado de la solución MathWorks mencionada anteriormente. El primer paso importante es proporcionar el prototipo para la función mxCreateSharedDataCopy :

     /* Add this declaration because it does not exist in the "mex.h" header */ extern mxArray *mxCreateSharedDataCopy(const mxArray *pr); 

    Como dice el comentario, esto no está en mex.h , así que tienes que declararlo tú mismo.

    La siguiente parte de mxsharedcopy.c crea nuevas mxArray de las siguientes maneras:

    1. Una copia profunda a través de mxDuplicateArray :

       copy1 = mxDuplicateArray(prhs[0]); 
    2. Una copia compartida a través de mxCreateSharedDataCopy :

       copy2 = mxCreateSharedDataCopy(copy1); 
    3. Copia directa de mxArray* , agregada por mí:

       copy0 = prhs[0]; // OK, but don't modify copy0 inside mexFunction! 

    Luego imprime la dirección del buffer de datos ( pr ) para cada mxArray y sus primeros valores. Aquí está el resultado de mxsharedcopy(x) modificado para x=ones(1e3); :

     prhs[0] = 72145590, mxGetPr = 18F90060, value = 1.000000 copy0 = 72145590, mxGetPr = 18F90060, value = 1.000000 copy1 = 721BF120, mxGetPr = 19740060, value = 1.000000 copy2 = 721BD4B0, mxGetPr = 19740060, value = 1.000000 

    Que pasó:

    1. Como era de esperar, comparando prhs[0] y copy0 no hemos creado nada nuevo, excepto otro puntero al mismo mxArray .
    2. Comparando prhs[0] y copy1 , observe que mxDuplicateArray creó un nuevo mxArray en la dirección 721BF120 , y copió los datos en un nuevo buffer en 19740060 .
    3. copy2 tiene una dirección diferente ( mxArray* ) de copy1 , lo que significa que también es una mxArray diferente, no solo la misma señalada por diferentes variables, pero ambas comparten los mismos datos en la dirección 19740060 .

    La pregunta se reduce a: ¿Es seguro devolver en plhs[0] de copy0 o copy2 (de una copia de puntero simple o mxCreateSharedDataCopy , respectivamente) o es necesario usar mxDuplicateArray , que realmente copia los datos? Podemos demostrar que mxCreateSharedDataCopy funcionaría destruyendo copy1 y verificando que copy2 sigue siendo válido:

     mxDestroyArray(copy1); copy2val0 = *mxGetPr(copy2); % no crash! 

    Aplicando Copia de Datos Compartidos a la Entrada

    De vuelta a la pregunta. Avance un paso más allá del ejemplo de MathWorks y devuelva una copia de datos compartidos de la entrada . Solo haz:

     if (nlhs>0) plhs[0] = mxCreateSharedDataCopy(prhs[0]); 

    ¡Contenga la respiración!

     >> format debug >> x=ones(1,2) x = Structure address = 9aff820 % mxArray* m = 1 n = 2 pr = 2bcc8500 % double* pi = 0 1 1 >> xDup = mxsharedcopy(x) xDup = Structure address = 9afe2b0 % mxArray* (different) m = 1 n = 2 pr = 2bcc8500 % double* (same) pi = 0 1 1 >> clear x >> xDup % hold your breath! xDup = Structure address = 9afe2b0 m = 1 n = 2 pr = 2bcc8500 % double* (still same!) pi = 0 1 1 

    Ahora para una entrada temporal (sin format debug ):

     >> tempDup = mxsharedcopy(2*ones(1e3)); >> tempDup(1) ans = 2 

    Curiosamente, si mxCreateSharedDataCopy sin mxCreateSharedDataCopy (es decir, con solo plhs[0] = prhs[0]; ), MATLAB no falla, pero la variable de salida nunca se materializa:

     >> tempDup = mxsharedcopy(2*ones(1e3)) % no semi-colon >> whos tempDup >> tempDup(1) Undefined function 'tempDup' for input arguments of type 'double'. 

    R2013b, Windows, 64 bits.

    mxsharedcopy.cpp (versión modificada de C ++):

     #include "mex.h" /* Add this declaration because it does not exist in the "mex.h" header */ extern "C" mxArray *mxCreateSharedDataCopy(const mxArray *pr); bool mxUnshareArray(const mxArray *pr, const bool noDeepCopy); // true if not successful void mexFunction(int nlhs,mxArray *plhs[],int nrhs,const mxArray *prhs[]) { mxArray *copy1(NULL), *copy2(NULL), *copy0(NULL); //(void) plhs; /* Unused parameter */ /* Check for proper number of input and output arguments */ if (nrhs != 1) mexErrMsgTxt("One input argument required."); if (nlhs > 1) mexErrMsgTxt("Too many output arguments."); copy0 = const_cast(prhs[0]); // ADDED /* First make a regular deep copy of the input array */ copy1 = mxDuplicateArray(prhs[0]); /* Then make a shared copy of the new array */ copy2 = mxCreateSharedDataCopy(copy1); /* Print some information about the arrays */ // mexPrintf("Created shared data copy, and regular deep copy\n"); mexPrintf("prhs[0] = %X, mxGetPr = %X, value = %lf\n",prhs[0],mxGetPr(prhs[0]),*mxGetPr(prhs[0])); mexPrintf("copy0 = %X, mxGetPr = %X, value = %lf\n",copy0,mxGetPr(copy0),*mxGetPr(copy0)); mexPrintf("copy1 = %X, mxGetPr = %X, value = %lf\n",copy1,mxGetPr(copy1),*mxGetPr(copy1)); mexPrintf("copy2 = %X, mxGetPr = %X, value = %lf\n",copy2,mxGetPr(copy2),*mxGetPr(copy2)); /* TEST: Destroy the first copy */ //mxDestroyArray(copy1); //copy1 = NULL; //mexPrintf("\nFreed copy1\n"); /* RESULT: copy2 will still be valid */ //mexPrintf("copy2 = %X, mxGetPr = %X, value = %lf\n",copy2,mxGetPr(copy2),*mxGetPr(copy2)); if (nlhs>0) plhs[0] = mxCreateSharedDataCopy(prhs[0]); //if (nlhs>0) plhs[0] = const_cast(prhs[0]); }