Mex文件:如何返回已分配的matlab数组
我发现了一个非常棘手的问题,我似乎不容易解决。简而言之,我想从一个mex文件返回一个数组,该数组已作为mex函数输入传递。您可以简单地执行以下操作:Mex文件:如何返回已分配的matlab数组,matlab,memory-management,mex,Matlab,Memory Management,Mex,我发现了一个非常棘手的问题,我似乎不容易解决。简而言之,我想从一个mex文件返回一个数组,该数组已作为mex函数输入传递。您可以简单地执行以下操作: void mexFunction(int nargout, mxArray *pargout [ ], int nargin, const mxArray *pargin[]) { pargout[0] = pargin[0]; } 但这不是我需要的。我想从pargin[0]中获取原始指针,对其进行内部处理,并通过设置相应的数据指针返回新创
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;
}
似乎我必须走“未记录的MATLAB”之路,使用mxCreateSharedDataCopy
和mxUnshareArray
您应该使用的方法,这就是记录的方法:
#include "mex.h"
void mexFunction(int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[])
{
plhs[0] = mxDuplicateArray(prhs[0]);
}
虽然没有记录,但MEX API函数
mxCreateSharedDataCopy
ism现在显然被禁止创建mxArray
的共享数据副本。MathWorks甚至在其解决方案中提供了一个示例
如移除的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]
中看到更改,因为它引用了相同的数据缓冲区。另一方面,当显式生成共享副本(使用mxCreateSharedDataCopy
)时,有两个不同的mxArray
对象,对一个数组的数据进行更改将触发复制操作,从而生成两个完全独立的数组。还有,直接分配
修改的MathWorks示例
从使用上述MathWorks解决方案中修改的mxsharedcopy.c
的示例开始。第一个重要步骤是为mxCreateSharedDataCopy
函数提供原型:
>> 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
/*添加此声明,因为它不存在于“mex.h”标题中*/
外部mxArray*mxCreateSharedDataCopy(常量mxArray*pr);
正如注释所述,这不在mex.h
中,因此您必须自己声明
mxsharedcopy.c
的下一部分通过以下方式创建新的mxArray
s:
通过mxDuplicateArray
进行深度复制:
copy1 = mxDuplicateArray(prhs[0]);
通过mxCreateSharedDataCopy
共享副本:
copy2 = mxCreateSharedDataCopy(copy1);
我添加的mxArray*
的直接副本:
copy0 = prhs[0]; // OK, but don't modify copy0 inside mexFunction!
然后,它为每个mxArray
及其第一个值打印数据缓冲区的地址(pr
)。以下是针对x=one(1e3)修改的mxsharedcopy(x)
的输出代码>:
发生了什么:
正如所料,通过比较prhs[0]
和copy0
我们没有创建任何新的内容,除了指向相同mxArray
的另一个指针之外
比较prhs[0]
和copy1
,注意mxDuplicateArray
在地址721BF120
处创建了一个新的mxArray
,并将数据复制到19740060
处的新缓冲区中
copy2
与copy1
具有不同的地址(mxArray*
),这意味着它也是一个不同的mxArray
,不仅是由不同变量指向的同一个,而且它们在地址19740060
处共享相同的数据
问题归结为:在plhs[0]
中返回copy0
或copy2
(分别从简单指针复制或mxCreateSharedDataCopy
)是否安全,或者是否有必要使用实际复制数据的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
现在,对于临时输入(不带格式调试
):
有趣的是,如果我在没有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位
<强> MxSyrdCopy.CPP(修改C++版本):>P/>
#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]);
}
#包括“mex.h”
/*添加此声明,因为它在“mex.h”标题中不存在*/
外部“C”mxArray*mxCreateSharedDataCopy(常量mxArray*pr);
bool mxUnshareArray(常量mxArray*pr,常量bool noDeepCopy);//如果不成功,则为true
void MEX函数(int nlhs、mxArray*plhs[]、int nrhs、const mxArray*prhs[])
{
mxArray*copy1(空)、*copy2(空)、*copy0(空);
//(无效)plhs;/*未使用的参数*/
/*检查输入和输出参数的正确数量*/
如果(nrhs!=1)
mexErrMsgTxt(“需要一个输入参数”);
如果(nlhs>1)
mexErrMsgTxt(“太多的输出参数”);
copy0=const_cast(prhs[0]);//已添加
/*首先对输入数组进行常规深度复制*/
copy1=mxDuplicateArray(prhs[0]);
/*然后创建新阵列的共享副本*/
copy2=mxCreateSharedDataCopy(copy1);
/*打印有关阵列的一些信息*/
//mexPrintf(“创建共享数据副本和常规深度副本”);
mexPrintf(“prhs[0]
>> tempDup = mxsharedcopy(2*ones(1e3)) % no semi-colon
>> whos tempDup
>> tempDup(1)
Undefined function 'tempDup' for input arguments of type 'double'.
#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]);
}