首页 文章

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

提问于
浏览
5

我发现了一个非常棘手的问题,我似乎无法轻易解决这个问题 . 简而言之,我想从一个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 (第一个代码片段),以及何时我必须创建它 .

请帮忙!

EDIT

在进一步查看和讨论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

看来我必须沿着'undocumented MATLAB'路走下去并使用 mxCreateSharedDataCopymxUnshareArray .

2 回答

  • 6

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

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

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

    如删除的MathWorks解决方案(1-6NU359)中所述,该函数可用于克隆 mxArray 标头 . 但是,执行 plhs[0] = prhs[0];plhs[0] = mxCreateSharedDataCopy(prhs[0]); 之间的区别在于第一个版本只是复制 mxArray* (一个指针),因此不会创建一个新的 mxArray 容器(至少在 mexFunction 返回之前并且MATLAB在 mxArray 中使用's magic), which would increment the data'的引用计数秒 .

    为什么这可能是一个问题?如果您使用 plhs[0] = prhs[0]; 并且在从 mexFunction 返回之前不对 plhs[0] 进行进一步修改,那么一切都很好,并且由于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

    • 通过 mxDuplicateArray 进行深层复制:
    copy1 = mxDuplicateArray(prhs[0]);
    
    • 通过 mxCreateSharedDataCopy 共享副本:
    copy2 = mxCreateSharedDataCopy(copy1);
    
    • mxArray* 的直接副本,由我添加:
    copy0 = prhs[0]; // OK, but don't modify copy0 inside mexFunction!
    

    然后它为每个 mxArray 打印数据缓冲区的地址( pr )及其第一个值 . 以下是 x=ones(1e3); 修改 mxsharedcopy(x) 的输出:

    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
    

    发生了什么:

    • 正如预期的那样,比较 prhs[0]copy0 我们没有创建任何新的东西,除了指向同一个 mxArray 的另一个指针 .

    • 比较 prhs[0]copy1 ,注意 mxDuplicateArray 在地址 721BF120 处创建了新的 mxArray ,并将数据复制到 19740060 的新缓冲区中 .

    • copy2copy1 具有不同的地址( mxArray* ),这意味着它也是一个不同的 mxArray ,而不仅仅是不同变量指向的相同地址,而 they both share the same data 在地址 19740060 处 .

    问题简化为:在 copy0copy2 (分别来自简单指针复制或 mxCreateSharedDataCopy )的 plhs[0] 中返回是否安全,或者是否必须使用实际复制数据的 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]; ),MATLAB不会崩溃,但输出变量永远不会实现:

    >> 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 (modified C++ version):

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

相关问题