虽然未经文件记录,MEX API函数
mxCreateSharedDataCopy
曾被MathWorks公司提供作为创建
mxArray
共享数据副本的解决方案
(由MathWorks提供),但现在似乎已被否认。MathWorks甚至在他们的解决方案中提供了一个示例{{link2:
mxsharedcopy.c
}}。
如同被删除的 MathWorks 解决方案(1-6NU359)中所描述的那样,该函数可用于克隆 mxArray 头文件。但是,使用
plhs[0] = prhs[0];
和
plhs[0] = mxCreateSharedDataCopy(prhs[0]);
的区别在于,第一个版本只复制 mxArray*(指针),因此不会创建新的 mxArray 容器(至少直到 mexFunction 返回并 MATLAB 进行其操作为止),这将增加两个 mxArray 中数据的引用计数。
为什么这可能成为一个问题?如果您使用
plhs[0] = prhs[0];
,并且在从
mexFunction
返回之前没有对
plhs[0]
进行进一步修改,则一切都很好,由于MATLAB的帮助,您将拥有共享数据副本。然而,如果在上述赋值之后,您在
MEX函数中修改了
plhs[0]
,则由于
prhs[0]
引用相同的数据缓冲区,因此该更改也会在
prhs[0]
中看到。另一方面,当显式生成共享副本(使用
mxCreateSharedDataCopy
)时,存在两个不同的
mxArray
对象,并且对一个数组数据的更改将触发复制操作,从而产生两个完全独立的数组。此外,直接赋值
有时会导致分段错误。
以修改后的
mxsharedcopy.c
为例。该文件来自于上述MathWorks解决方案。第一个重要的步骤是提供
mxCreateSharedDataCopy
函数的原型。
extern mxArray *mxCreateSharedDataCopy(const mxArray *pr);
正如注释所述,这不在
mex.h
中,因此您必须自己声明它。
mxsharedcopy.c
的下一部分以以下方式创建新的
mxArray
:
A deep copy via mxDuplicateArray
:
copy1 = mxDuplicateArray(prhs[0])
A shared copy via mxCreateSharedDataCopy
:
copy2 = mxCreateSharedDataCopy(copy1);
Direct copy of the mxArray*
, added by me:
copy0 = prhs[0]; // OK, but don't modify copy0 inside mexFunction!
然后它打印出每个 mxArray 的数据缓冲区(`pr`)的地址及其第一个值。以下是修改后的 `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
在地址为 721BF120
处创建了一个新的 mxArray
,并将数据复制到了地址为 19740060
的新缓冲区中。
copy2
与 copy1
有不同的地址(mxArray*
),这意味着它们是不同的 mxArray
,而不仅仅是由不同变量指向的同一对象,但它们在地址为 19740060
的相同数据。
这个问题可以简化为:在
plhs [0]
中返回简单指针复制或者
mxCreateSharedDataCopy
的
copy0
或
copy2
是否安全,还是必须使用实际复制数据的
mxDuplicateArray
?我们可以通过销毁
copy1
并验证
copy2
仍然有效来证明
mxCreateSharedDataCopy
是可行的。
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(修改的C ++版本):
#include "mex.h"
extern "C" mxArray *mxCreateSharedDataCopy(const mxArray *pr);
bool mxUnshareArray(const mxArray *pr, const bool noDeepCopy);
void mexFunction(int nlhs,mxArray *plhs[],int nrhs,const mxArray *prhs[])
{
mxArray *copy1(NULL), *copy2(NULL), *copy0(NULL);
if (nrhs != 1)
mexErrMsgTxt("One input argument required.");
if (nlhs > 1)
mexErrMsgTxt("Too many output arguments.");
copy0 = const_cast<mxArray*>(prhs[0]);
copy1 = mxDuplicateArray(prhs[0]);
copy2 = mxCreateSharedDataCopy(copy1);
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));
if (nlhs>0) plhs[0] = mxCreateSharedDataCopy(prhs[0]);
}
pargout[0] = pargin[0];
没有完成你想要的操作?如果这看起来不太靠谱,那么mxCreateSharedDataCopy
是否能通过共享相同的数据指针并处理引用计数,从而防止 MATLAB 崩溃,达到你的需求呢? - chappjctypecastx
示例是如何实现这一点的绝佳范例;它基本上调用了plhs[0] = mxCreateSharedDataCopy(prhs[0]);
而不是我在答案中写的mxDuplicateArray
。 - AmromxCreateSharedDataCopy
,这里有一份详细但有点过时的例子,以及James Tursa撰写的快速示例。此外,还有这个不错的列表,列出了(半)未记录的MEX API函数。 - chappjcpargin_to_pargout(a.field);
出现段错误是因为nargout
为0,而pargout[0]
是无效的。我在答案中添加了关于mxCreateSharedDataCopy
的讨论。也许会有用。 - chappjc