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;
}

うまくいきません。すぐにではないにしても、数回呼び出した後、セグメンテーション違反が発生します。ドキュメントでは、そのようなシナリオについては何も言われていないと思います。唯一の要件は、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」の道をたどって and を使用する必要があるようmxCreateSharedDataCopyですmxUnshareArray

4

2 に答える 2

9

を使用する必要がありますmxDuplicateArray。これは文書化された方法です。

#include "mex.h"

void mexFunction(int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[])
{
    plhs[0] = mxDuplicateArray(prhs[0]);
}
于 2013-11-06T13:50:58.930 に答える
6

文書化されていませんが、MEX API 関数mxCreateSharedDataCopy は、の共有データ コピーを作成するためのソリューションとしてMathWorks によって提供されましたが、現在は明らかに否定されていmxArrayます。MathWorks は、ソリューションの例も提供していますmxsharedcopy.c

削除された MathWorks ソリューション (1-6NU359) で説明されているように、この関数を使用してmxArrayヘッダーを複製できます。plhs[0] = prhs[0];ただし、実行と実行の違いplhs[0] = mxCreateSharedDataCopy(prhs[0]);は、最初のバージョンはmxArray*(a ポインター) をコピーするだけであり、新しいコンテナーを作成しないことですmxArray(少なくとも、mexFunction返されて MATLAB が魔法のように機能するまで)、両方でデータの参照カウントをインクリメントします。mxArray秒。

なぜこれが問題になるのでしょうか? から戻る前にを使用plhs[0] = prhs[0];し、それ以上変更しない場合は、すべて問題なく、MATLAB のおかげで共有データ コピーが作成されます。ただし、上記の割り当ての後にMEX 関数で変更すると、同じデータ バッファーを参照するため、変更も見られます。一方、( を使用して) 共有コピーを明示的に生成すると、2 つの異なるオブジェクトがあり、1 つの配列のデータを変更するとコピー操作がトリガーされ、2 つの完全に独立した配列が作成されます。また、直接代入は、場合によってはセグメンテーション違反を引き起こす可能性があります。plhs[0]mexFunctionplhs[0] prhs[0]mxCreateSharedDataCopymxArray

変更された 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新しい を作成します。mxArray

  1. 経由のディープコピーmxDuplicateArray:

    copy1 = mxDuplicateArray(prhs[0]);
    
  2. 経由の共有コピーmxCreateSharedDataCopy:

    copy2 = mxCreateSharedDataCopy(copy1);
    
  3. mxArray*私が追加したの直接コピー:

    copy0 = prhs[0]; // OK, but don't modify copy0 inside mexFunction!
    

次に、それぞれの最初の値のデータ バッファー ( pr)のアドレスを出力します。変更されたformxArrayの出力は次のとおりです。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

どうしたの:

  1. 予想どおり、 を比較するprhs[0]copy0、同じ への別のポインターを除いて、新しいものは何も作成されていませんmxArray
  2. と を比較するprhs[0]と、で新しいアドレスが作成され、 データが で新しいバッファにコピーされてcopy1いることに注意してください。mxDuplicateArraymxArray721BF12019740060
  3. copy2は とは異なるアドレス ( mxArray*) を持っていますcopy1。つまり、異なる変数が指している同じものだけでなく、アドレス同じデータを共有しています。mxArray19740060

質問は次のようになります:またはplhs[0]のいずれcopy0copy2(単純なポインターのコピー または から) を返すのは安全ですか、それとも実際にデータをコピーするmxCreateSharedDataCopyを使用する必要がありますか? それがまだ有効であることを破棄して検証することで、それが機能するmxDuplicateArrayことを示すことができます。mxCreateSharedDataCopycopy1copy2

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"

/* 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]);
}
于 2013-11-08T02:42:21.267 に答える