我发现了一个非常棘手的问题,我似乎无法轻易解决这个问题。简而言之,我想从一个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'走下去并使用mxCreateSharedDataCopy
和mxUnshareArray
。
答案 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解决方案中的修改后的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!
然后它打印每个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
发生了什么:
prhs[0]
和copy0
我们没有创建任何新内容,除了另一个指向同一mxArray
的指针。prhs[0]
和copy1
,请注意mxDuplicateArray
在地址mxArray
处创建了新的721BF120
,并将数据复制到{{1}处的新缓冲区中1}}。19740060
来自copy2
的地址(mxArray*
)不同,这意味着也一个不同的copy1
,而不仅仅是同一个mxArray
通过不同的变量,但他们在地址19740060
共享相同的数据。问题简化为:以plhs[0]
copy0
或copy2
(分别来自简单指针副本或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}}