虽然没有记录,但 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
s。
为什么这可能是一个问题?如果您在从 中返回之前使用plhs[0] = prhs[0];
并且没有进行进一步修改,那么一切都很好,并且由于 MATLAB,您将拥有一个共享的数据副本。但是,如果在上述分配之后您在 MEX 函数中进行了修改,则也可以看到更改,因为它引用了相同的数据缓冲区。另一方面,当显式生成共享副本(使用)时,有两个不同的对象,并且对一个数组数据的更改将触发复制操作,从而产生两个完全独立的数组。此外,在某些情况下,直接分配可能会导致分段错误。plhs[0]
mexFunction
plhs[0]
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
创建新的s: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)
for 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
的 at 地址721BF120
,并将数据复制到了一个新的缓冲区 at 19740060
。
copy2
与有mxArray*
不同copy1
的地址(_ _ mxArray
_19740060
问题归结为:返回or (分别从简单的指针副本 or )返回是否安全,plhs[0]
或者是否有必要使用实际复制数据的 ?我们可以通过销毁并验证它仍然有效来证明这将起作用:copy0
copy2
mxCreateSharedDataCopy
mxDuplicateArray
mxCreateSharedDataCopy
copy1
copy2
mxDestroyArray(copy1);
copy2val0 = *mxGetPr(copy2); % no crash!
将共享数据副本应用于输入
回到问题。比 MathWorks 示例更进一步,并返回 input 的共享数据副本。做就是了:
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(修改后的 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]);
}