Назначение массива MATLAB завершается ошибкой после вызова MEX

Я работал с MEX и обнаружил странное поведение, которое я выделил для следующей очень простой программы:

#include "mex.h"
#include <stdio.h>

void mexFunction(int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[])
{
  double *A;
  int i;

  if (    nrhs != 1
       || nlhs > 1
       || !mxIsDouble(prhs[0])
       || mxIsComplex(prhs[0])
       || mxGetM(prhs[0])!=1
    ) mexErrMsgTxt("internal error: dtimes2: input error");

  A = mxGetPr(prhs[0]);
  for (i=0; i<3; i++) A[i] *= 2;
  return; }

Итак, проблема заключается в следующем: в сеансе работы с MATLAB

B=[3.2,5.6,9.4]; dtimes2(B); B

и MATLAB говорит: B = 6,4000 11,2000 18,8000

Все идет нормально. Но сейчас:

B=[3.2,5.6,9.4]

и MATLAB говорит: B = 6,4000 11,2000 18,8000

но когда я говорю

B=[-34.5,-57.6,-28.9]

тогда MATLAB говорит: B = -34,5000 -57,6000 -28,9000

Вы видите посередине, я не могу переназначить B, если номера такие же, как и раньше. Итак, проверка реальностью:

A=[1,2,3]; A=2*A; A=[1,2,3]

работает, конечно: MATLAB говорит, что A = [1,2,3] в конце.

MEX предупреждает, что мой компилятор '6.2.1-2', но поддерживается '4.7.x', но для этой простой программы я вряд ли ожидал проблемы. Что здесь не так?


person user2747939    schedule 16.12.2016    source источник
comment
Я не очень понимаю ваше объяснение проблемы. Каковы точные входные данные, которые вы пытаетесь ввести, каковы результаты и чем они отличаются от ожидаемых?   -  person excaza    schedule 16.12.2016


Ответы (2)


Вы не должны изменять какие-либо входные данные, поступающие в вашу оболочку MEX. Это неопределенное поведение, и именно это происходит в вашей MEX-функции. MEX рекомендует возвращать выходные данные вместо изменения входных данных. Вы можете прочитать больше об этом в блоге Яира Альтмана Undocumented MATLAB, если вы действительно хотите изменить входные данные или то, что он называет редактированием на месте. Однако, когда вы начинаете работать с MEX, я не поощряю такое поведение. Хотя бывают ситуации, когда это необходимо, если вы не знаете, что делаете, по возможности избегайте этого: http://undocumentedmatlab.com/blog/matlab-mex-in-place-editing.

Что касается отказа от изменения входных данных, это четко определено в объявлении функции вашего mexFunction шлюза:

void mexFunction(int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[])
                                                      ^^^^^^^^^^^^^^^^^^^^^

Модификатор const гарантирует, что ни один из указателей ввода не будет изменен, и это нормально... но это не мешает вам фактически изменять содержимое, на которое ссылаются указатели на память, или делать это на месте. Вообще говоря, память, которую вы создали для любой переменной MATLAB в рабочей области MATLAB, может использоваться совместно с другими переменными MATLAB из-за поведения «ленивого копирования». Следовательно, значения, которые вы видите в матрице, векторе или одиночном значении, могут быть связаны с другими матрицами, векторами или отдельными значениями других переменных. Когда вы меняете память на месте, вы также изменили бы и другие переменные, и это может быть причиной непоследовательного поведения.

Всегда возвращайте фактический желаемый результат, а не изменяйте входные данные, поскольку вы гарантированно получите правильные результаты. Я изменил ваш код ниже, чтобы он выводил новую матрицу, а не выполнял вычисления на месте.

#include "mex.h"
#include <stdio.h>

void mexFunction(int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[])
{
  double *A;
  double *B; // Change - For the output
  int i;

  if (    nrhs != 1
       || nlhs > 1
       || !mxIsDouble(prhs[0])
       || mxIsComplex(prhs[0])
       || mxGetM(prhs[0])!=1
    ) mexErrMsgTxt("internal error: dtimes2: input error");

  A = mxGetPr(prhs[0]);

  // Change - Create output memory
  mwSize rows = mxGetM(prhs[0]);
  mwSize cols = mxGetN(prhs[0]); 
  plhs[0] = mxCreateDoubleMatrix(rows, cols, mxREAL);

  // Change - get a pointer to the output memory
  B = mxGetPr(plhs[0]);

  for (i=0; i<3; i++) B[i] = 2*A[i]; // Change - Write to output memory
  return;
}

Вместо этого вы бы сделали это:

B = dtimes2(B);
person rayryeng    schedule 16.12.2016
comment
ой. в порядке. константа да. я никогда не буду делать это снова. Большое спасибо! - person user2747939; 16.12.2016

Я просто думал о том, что произошло и как это могло произойти, и решил сделать заметку, чтобы подчеркнуть опасность изменения неизменяемых объектов MATLAB из-под MATLAB, на случай, если это кому-нибудь поможет e Given A=[1,2,3] , MATLAB должен был вычислить хэш для [1,2,3], скажем, «бу», и сохранить [1,2,3] в корзине «бу». В файле MEX он с радостью отказался от указателя на [1,2,3], и большой сильный код c превратил вектор по этому адресу в [2,4,6]. Затем по оператору A=[1,2,3] MATLAB снова вычислил хэш, и он оказался таким же, а именно «бу». Поскольку сеанс был простым, в этой корзине был только один объект. Ну, если был только один объект, он уже должен был быть [1,2,3], так зачем перезаписывать это в память? Так что это не так. Но памяти было не [1,2,3], а [2,4,6], чего никогда не должно было быть в корзине "бу". После нарушения получился такой разговор:

Я: MATLAB, пожалуйста, установите A=[1,2,3].

MATLAB: Сэр, да сэр! Сделал это! А=[2,4,6]!

Я: Нет, правда, установите A=[1,2,3].

MATLAB: Сэр, да сэр! А=[2,4,6]!

Я: Хорошо. Ммм, установите A=[3,4,5].

MATLAB: Сэр, да сэр! А=[3,4,5].

Я: Итак, ммм, ладно, теперь поставь A=[1,2,3]

MATLAB: Сэр, да сэр! А=[2,4,6]!

Каждый раз, когда выполнялся неверный MEX-файл, результирующий вектор оказывался в неправильном хеш-бине. Случайным образом в некоторых корзинах оказался ровно один неправильный объект. Это вызвало крайнее замешательство.

person user2747939    schedule 16.12.2016