Почему значение указателя неожиданно меняется? - PullRequest
0 голосов
/ 07 января 2019

Я интегрировал C ++ в Matlab; в моем примере есть три простых файла: скрипт Matlab main.m, файл класса Matlab myClass.m и файл C ++ myMex.cpp.

В main.m я вызываю класс, в качестве входного значения которого используется строка.

main.m:

myVar = myClass('test.bin');

myClass.m:

classdef myClass < handle    
    properties
        bitStreamName;
        cabac_handle;
    end
    methods
        function obj = myClass(fn)      
            obj.bitStreamName = fn;
            obj.cabac_handle = myMex('First', obj.bitStreamName);
            myMex('Second', obj.cabac_handle);
        end
    end
end

myMex.cpp

#include "mex.h"
#include <iostream>
using namespace std;

void _main();

class CABAC {
public:
    CABAC() {};
    ~CABAC() {};
    char* fn;
};

// the MEX interface function
void mexFunction(
    int nlhs, // Number of expected output mxArrays
    mxArray *plhs[], // Array of pointers to the expected output mxArrays
    int nrhs, // Number of input mxArrays
    const mxArray *prhs[] // Array of pointers to the input mxArrays
    )

{
    CABAC *c; // pointer to (new) instance of the CABAC class
    char* fn = 0;
    char cmd[64]; // temp char array to hold the command

    // start parsing the input command
    mxGetString(prhs[0], cmd, sizeof(cmd));
    string inputCmd(cmd);

    if (inputCmd == "First")
    {
        // get the filename string
        fn = mxArrayToString(prhs[1]);
        c = new CABAC;
        uintptr_t c_value = (uintptr_t)c;
        plhs[0] = mxCreateDoubleMatrix(1, 1, mxREAL);
        *mxGetPr(plhs[0]) = c_value; // return pointer to matlab environment
        c->fn = fn;
        mexPrintf("Pointer: %p\n", c);
        mexPrintf("Filename: %s\n", c->fn);
    }
    else if (inputCmd == "Second")
    {
        uintptr_t my_value = *mxGetPr(prhs[1]);
        CABAC *my_pointer =  (CABAC *)my_value;
        mexPrintf("Pointer: %p\n", my_pointer);
        mexPrintf("Filename: %s\n", my_pointer->fn);
    }
}

При первом вызове myMex.cpp я создаю объект класса CABAC, а строка "test.bin" связана с его атрибутом fn. Наконец, значение указателя возвращается в среду Matlab.

Во второй раз я просто получаю указатель на ранее созданный экземпляр объекта, и тут происходит нечто странное. В частности, при печати адреса указателя и атрибута fn первый всегда является правильным (т. Е. Адрес совпадает с адресом объекта), но последний иногда является правильным (печатается "test.bin"), а иногда совершенно и случайно неправильно (появляются странные строки, по-разному для каждого выполнения).

Чтобы выполнить мой код, вы можете просто запустить main.m после компиляции myMex.cpp с помощью этой инструкции:

mex CXXFLAGS="\$CXXFLAGS -std=c++11" -g myMex.cpp

или

mex COMPFLAGS="\$CXXFLAGS -std=c++11" -g myMex.cpp

Можете ли вы помочь мне понять, что происходит с именем файла?

РЕДАКТИРОВАТЬ: кажется, что c удаляется после первого myMex вызова. Как мне сохранить этот указатель на объект CABAC (со всеми его атрибутами) в памяти?

1 Ответ

0 голосов
/ 07 января 2019

Здесь много вопросов. Прежде всего, вы приводите указатель на double, который не сохраняет его представление:

uintptr_t c_value = (uintptr_t)c;
plhs[0] = mxCreateDoubleMatrix(1, 1, mxREAL);
*mxGetPr(plhs[0]) = c_value; // return pointer to matlab environment

Вместо этого создайте 64-битный целочисленный массив для сохранения указателя на:

plhs[0] = mxCreateNumericMatrix(1, 1, mxUINT64_CLASS, mxREAL);
*(uint64_t*)mxGetData(plhs[0]) = (uint64_t)c;

(Поскольку у MATLAB R2018a есть альтернатив mxGetData для каждого типа , в этом случае это будет mxGetUint64s).

Во-вторых, память new ed, вероятно, будет удалена при выгрузке MEX-файла. Это может произойти в любое время. Чтобы предотвратить это, заблокируйте MEX-файл в памяти, используя mexLock. Чтобы предотвратить утечки памяти, вам нужно будет включить код, который удаляет память, когда вы закончите с ней. Поскольку ваш MATLAB-класс является классом-дескриптором, это может быть достигнуто.

В-третьих, очень просто вызвать MEX-файл с неверным указателем, что, вероятно, приведет к краху всей MATLAB. С вашей текущей реализацией это никак не обойтись.

В-четвертых, как указано Navan , следующая строка создает копию строки, но в буфере памяти, поддерживаемом MATLAB:

fn = mxArrayToString(prhs[1]);

MATLAB автоматически удалит всю память, выделенную на mxMalloc и аналогичную в конце mexFunction. Таким образом, fn будет указывать на свободную память. Вам нужно вручную создать копию здесь, в памяти, которой вы управляете. Я предлагаю вам скопировать строку в std::string вместо:

class CABAC {
  public:
  std::string fn;
};

fn = mxArrayToString(prhs[1]); // Will copy the string to `fn`.

Однако вместо этого я бы рекомендовал другой подход:

  • В вашем MEX-файле храните массив указателей на выделенные объекты.
  • MEX-файл не возвращает указатель, а возвращает индекс массива.
  • MEX-файл теперь может проверять входной «дескриптор», чтобы он находился в пределах допустимых границ массива, а также, содержит ли этот массив действительный указатель или указатель NULL (например, для удаленного объекта или для элемента массива, который еще не использовался).
  • Заблокировать MEX-файл, но разрешить команде разблокировать его. Разблокировка MEX-файла также приведет к удалению всех выделенных объектов.
  • Вы можете включить логику для автоматической разблокировки MEX-файла, когда последний из выделенных объектов был удален.

Вы должны знать, что глобальные переменные будут сохраняться между обращениями к MEX-файлу (пока он заблокирован). Таким образом, вам понадобится глобальный массив (определенный вне mexFunction) для хранения указателей. В качестве альтернативы, объявите массив static внутри mexFunction.

Этот MEX-файл реализует рекомендацию, описанную здесь, хотя это далеко не тривиальный пример, я надеюсь, что он может быть полезен в качестве отправной точки. В этой функции я использую std::map вместо обычного массива для хранения дескрипторов.

...