MEX文件:如何返回一个已分配的MATLAB阵列MEX文件:如何返回一个已分配的MATLAB阵列(M

2019-05-12 03:12发布

我发现一个非常棘手的问题,我似乎无法轻易解决。 总之,我想从一个MEX文件的阵列,其已经作为MEX函数输入传递返回。 你可以平凡做到这一点:

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

但是,这不是我所需要的。 我想获得从原始指针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;
}

它不工作。 我得到一个段错误,如果不立即,再经过几个电话。 我相信没有提到这样的情况在文档中 。 唯一的要求是帽子的data指针已使用分配mxCalloc ,它显然有。 因此,我会承担这个代码是合法的。

我需要这样做,因为我解析复杂的MATLAB结构到我C的内置数据结构。 我处理这些数据,有些数据被重新分配,有些则没有。 我想透明返回输出结构,无需时候我就来简单复制思考mxArray (第一个代码段),当我实际上是创建它。

请帮忙!

编辑

经过进一步寻找与荷银讨论,似乎连我的第一个代码段是不支持的,并可能导致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

看来我必须下井“无证MATLAB”路线,并使用mxCreateSharedDataCopymxUnshareArray

Answer 1:

您应该使用mxDuplicateArray ,多数民众赞成在记录方式:

#include "mex.h"

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


Answer 2:

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

如该删除MathWorks的解决方案(1-6NU359)中描述,该函数可被用于克隆mxArray报头。 然而,这样做的区别plhs[0] = prhs[0];plhs[0] = mxCreateSharedDataCopy(prhs[0]); 是第一个版本刚复制mxArray* (指针),因此不会创建一个新的mxArray容器(至少直到mexFunction回报和MATLAB工作它的魔力),这将增加数据的引用计数在这两个mxArray秒。

为什么这可能是一个问题吗? 如果使用plhs[0] = prhs[0]; 并且不再做进一步的修改plhs[0]从返回之前mexFunction ,一切都很好,你将不得不感谢一个共享的数据复制到MATLAB。 然而,如果在上述分配之后修改plhs[0] 在MEX函数 ,改变来在可见prhs[0]以及因为它指的是相同的数据缓冲器。 在另一方面,明确地生成一个共享的副本(具有当mxCreateSharedDataCopy )有两种不同的mxArray对象和改变到一个阵列的数据将触发导致两个完全独立的阵列的复制操作。 此外,直接分配可能会导致在某些情况下分割故障 。

改性MathWorks的实施例

使用改进的一个例子开始mxsharedcopy.c从上面引用的MathWorks的溶液。 第一个重要步骤是提供原型为mxCreateSharedDataCopy功能:

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

作为评论的状态,这是不是在mex.h ,所以你要这个宣布自己。

在接下来的部分mxsharedcopy.c创造了新的mxArray通过以下方式S:

  1. 通过深副本mxDuplicateArray

     copy1 = mxDuplicateArray(prhs[0]); 
  2. 经由共享拷贝mxCreateSharedDataCopy

     copy2 = mxCreateSharedDataCopy(copy1); 
  3. 在直接拷贝mxArray* ,由我补充说:

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

然后它打印数据缓冲器(的地址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

发生了什么:

  1. 正如预期的那样,比较prhs[0]copy0我们还没有创建任何新的东西,除了另一个指针相同的mxArray
  2. 比较prhs[0]copy1 ,注意mxDuplicateArray创建了一个新mxArray在地址721BF120 ,和复制的数据到一个新的缓冲液中在19740060
  3. copy2有一个不同的地址( mxArray* FROM) copy1 ,这意味着它有不同的mxArray不一样的一个不同的变量指向的,但他们都有着相同的数据在地址19740060

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

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

应用共享数据复制到输入

回到问题。 借此比MathWorks的例子的一步, 返回输入的共享数据副本 。 做就是了:

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

屏住呼吸!

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

现在的临时输入(无format debug ):

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

有趣的是,如果我测试而不mxCreateSharedDataCopy (即,与只是plhs[0] = prhs[0];不会崩溃,但输出变量从未物化:

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

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


文章来源: Mex files: how to return an already allocated matlab array