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

时间:2013-11-06 13:44:33

标签: matlab memory-management mex

我发现了一个非常棘手的问题,我似乎无法轻易解决这个问题。简而言之,我想从一个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;
}

它不起作用。我得到一个段错误,如果没有立即,然后几个电话后。我相信这种情况没有任何说法in the documentation。唯一的要求是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

似乎我必须沿着'未记录的MATLAB'走下去并使用mxCreateSharedDataCopymxUnshareArray

2 个答案:

答案 0 :(得分:8)

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

#include "mex.h"

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

答案 1 :(得分:6)

虽然没有记录,但MEX API函数mxCreateSharedDataCopy given as a solution by MathWorks,显然已被拒绝,用于创建mxArray的共享数据副本。 MathWorks甚至在其解决方案中提供了一个示例mxsharedcopy.c

如删除的MathWorks Solution(1-6NU359)中所述,该函数可用于克隆mxArray标头。但是,执行plhs[0] = prhs[0];plhs[0] = mxCreateSharedDataCopy(prhs[0]);之间的区别在于第一个版本只复制mxArray*(指针),因此不会创建新的mxArray容器(至少直到mexFunction返回并且MATLAB才能正常工作,这会增加mxArray s中数据的引用计数。

为什么这可能是个问题?如果您使用plhs[0] = prhs[0];并且在从plhs[0]返回之前不对mexFunction进行进一步修改,那么一切都很好,并且由于MATLAB,您将拥有共享数据副本。但是,如果在上述分配之后修改了MEX函数中的plhs[0] ,则可以在prhs[0]中看到更改,因为它引用了相同的数据缓冲区。另一方面,当显式生成共享副本(使用mxCreateSharedDataCopy)时,有两个不同的mxArray对象,对一个数组的数据的更改将触发复制操作,从而产生两个完全独立的数组。另外,直接分配can cause segmentation faults in some cases

修改过的MathWorks示例

首先使用上面引用的MathWorks解决方案中的修改后的mxsharedcopy.c示例。第一个重要步骤是为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

  1. 通过mxDuplicateArray进行深层复制:

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

    copy2 = mxCreateSharedDataCopy(copy1);
    
  3. 我添加的mxArray*的直接副本:

    copy0 = prhs[0]; // OK, but don't modify copy0 inside mexFunction!
    
  4. 然后它打印每个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,并将数据复制到{{1}处的新缓冲区中1}}。
    3. 19740060来自copy2的地址(mxArray*)不同,这意味着一个不同的copy1,而不仅仅是同一个mxArray通过不同的变量,但他们在地址19740060共享相同的数据
    4. 问题简化为:以plhs[0] copy0copy2(分别来自简单指针副本或mxCreateSharedDataCopy)返回mxDuplicateArray是否安全,或者是否有必要使用mxCreateSharedDataCopy,实际上复制数据?我们可以通过销毁copy1并验证copy2仍然有效来证明mxDestroyArray(copy1); copy2val0 = *mxGetPr(copy2); % no crash! 有效:

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

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

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

      >> 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'. ),MATLAB不会崩溃,但输出变量永远不会实现:

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

      R2013b,Windows,64位。

      mxsharedcopy.cpp(已修改的C ++版本):

      {{1}}