Mex文件:如何返回已经分配的matlab数组

ang*_*nor 5 matlab memory-management mex

我发现了一个非常棘手的问题,我似乎无法轻易解决这个问题.简而言之,我想从一个mex文件返回一个数组,该数组已作为mex函数输入传递.你可以琐碎地做到这一点:

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

但这不是我需要的.我希望得到原始指针pargin[0],在内部处理它,并通过设置相应的数据指针返回一个新创建的mex数组.像那样:

#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;
}
Run Code Online (Sandbox Code Playgroud)

它不起作用.我得到一个段错误,如果没有立即,然后几个电话后.我相信文档中没有关于这种情况的说法.唯一的要求是data指针已被分配使用mxCalloc,显然它有.因此,我认为这段代码是合法的.

我需要这样做,因为我正在将一个复杂的MATLAB结构解析为我的内部C数据结构.我处理数据,一些数据被重新分配,有些则没有.我想透明地返回输出结构,而不必考虑何时只需复制mxArray(第一个代码片段),以及何时我必须创建它.

请帮忙!

编辑

在进一步查看和讨论Amro后,似乎我的第一个代码片段不受支持,并且在某些情况下可能导致MATLAB崩溃,例如,在将结构字段或单元格元素传递给此类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
Run Code Online (Sandbox Code Playgroud)

看来我将不得不沿着"未记载的MATLAB"道路前进并使用mxCreateSharedDataCopymxUnshareArray.

Amr*_*mro 9

您应该使用mxDuplicateArray,这是记录的方式:

#include "mex.h"

void mexFunction(int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[])
{
    plhs[0] = mxDuplicateArray(prhs[0]);
}
Run Code Online (Sandbox Code Playgroud)

  • 它崩溃的原因是两个`mxArray`结构指向内存中的相同数据,但MATLAB不知道这两个变量是"链接的"; 所以稍后当一个被释放时,另一个被置于一个悬浮指针的不一致状态......内部MATLAB使用`mxArray_tag`结构中的未记录标志来跟踪这种事情(想想引用计数).当您为常规MATLAB矩阵编写`A = B;`时,可以看到这种数据共享行为.有关详细信息,请参阅该帖子评论中发布的链接. (3认同)
  • 这将是危险的,可能导致MATLAB崩溃.有没有文档分享数据的方法,请参阅这篇文章:http://stackoverflow.com/a/18849127/97160 (2认同)

cha*_*pjc 6

而无证,所述MEX API函数mxCreateSharedDataCopy 给定为一个溶液通过MathWorks公司,现在显然否认,用于创建的一个共享的数据拷贝mxArray.MathWorks甚至在其解决方案中提供了一个示例mxsharedcopy.c.

如删除的MathWorks解决方案(1-6NU359)中所述,该函数可用于克隆mxArray标头.然而,do plhs[0] = prhs[0];和之间的区别在于plhs[0] = mxCreateSharedDataCopy(prhs[0]);第一个版本只复制mxArray*(一个指针),因此不会创建一个新的mxArray容器(至少在mexFunction返回和MATLAB工作之前不会产生魔力),这会增加两个数据的引用计数mxArray秒.

为什么这可能是一个问题?如果您在返回之前使用plhs[0] = prhs[0];并且不做进一步修改,那么一切都很好,并且由于MATLAB,您将拥有共享数据副本.但是,如果在MEX函数中修改了上述赋值后,也可以看到更改,因为它引用了相同的数据缓冲区.另一方面,当显式生成共享副本(with )时,有两个不同的对象,对一个数组的数据的更改将触发复制操作,从而产生两个完全独立的数组.此外,在某些情况下,直接分配会导致分段错误.plhs[0]mexFunctionplhs[0] prhs[0]mxCreateSharedDataCopymxArray

修改过的MathWorks示例

从使用mxsharedcopy.c上面引用的MathWorks解决方案中修改过的示例开始.第一个重要步骤是为mxCreateSharedDataCopy函数提供原型:

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

正如评论所述,这不是mex.h,所以你必须自己声明.

下一部分通过以下方式mxsharedcopy.c创建新mxArray的:

  1. 深刻的副本mxDuplicateArray:

    copy1 = mxDuplicateArray(prhs[0]);
    
    Run Code Online (Sandbox Code Playgroud)
  2. 共享副本通过mxCreateSharedDataCopy:

    copy2 = mxCreateSharedDataCopy(copy1);
    
    Run Code Online (Sandbox Code Playgroud)
  3. 直接复制的mxArray*,由我添加:

    copy0 = prhs[0]; // OK, but don't modify copy0 inside mexFunction!
    
    Run Code Online (Sandbox Code Playgroud)

然后它pr为每个mxArray及其第一个值打印数据缓冲区()的地址.下面是修改后的输出mxsharedcopy(x)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
Run Code Online (Sandbox Code Playgroud)

发生了什么:

  1. 正如所料,比较prhs[0]copy0我们没有创建任何新的东西,除了另一个指向相同的指针mxArray.
  2. 比较prhs[0]copy1注意mxDuplicateArray创建一个新的mxArrayat地址721BF120,并将数据复制到新的缓冲区中19740060.
  3. copy2有一个不同的地址(mxArray*)copy1,意味着它也是一个不同的mxArray不仅是不同变量指向的相同,但它们都在地址共享相同的数据19740060.

现在的问题简化为:它是安全的返回plhs[0]任一copy0copy2(从简单的指针复制或mxCreateSharedDataCopy分别),或者是有必要使用mxDuplicateArray,这实际上将数据复制?我们可以证明,mxCreateSharedDataCopy通过销毁copy1和验证copy2仍然有效:

mxDestroyArray(copy1);
copy2val0 = *mxGetPr(copy2); % no crash!
Run Code Online (Sandbox Code Playgroud)

将共享数据复制应用于输入

回到问题.比MathWorks示例更进一步,返回输入的共享数据副本.做就是了:

if (nlhs>0) plhs[0] = mxCreateSharedDataCopy(prhs[0]);
Run Code Online (Sandbox Code Playgroud)

屏住呼吸!

>> 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
Run Code Online (Sandbox Code Playgroud)

现在进行临时输入(不format debug):

>> tempDup = mxsharedcopy(2*ones(1e3));
>> tempDup(1)
ans =
     2
Run Code Online (Sandbox Code Playgroud)

有趣的是,如果我没有测试mxCreateSharedDataCopy(即只测试plhs[0] = prhs[0];),MATLAB不会崩溃,但输出变量永远不会实现:

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

R2013b,Windows,64位.

mxsharedcopy.cpp(修改后的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<mxArray*>(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<mxArray*>(prhs[0]);
}
Run Code Online (Sandbox Code Playgroud)

  • @BastianEbeling我看到在R2014a中,`mxCreateSharedDataCopy`现在被导出为一个装饰的C++函数(mangling依赖于编译器); 未修饰的名称是`struct mxArray_tag*matrix :: detail :: noninlined :: mx_array_api :: mxCreateSharedDataCopy(struct mxArray_tag const*`.似乎MathWorks已经完成了一些代码重构,包括将MX API函数组织到各种名称空间中.无论如何,它仍然虽然注意到`mex`现在有不同的C和C++配置.你有没有得到链接器错误? (2认同)