Search code examples
c++matlabpointersmemorymex

Why does the value of a pointer unexpectedly change?


I have integrated C++ into Matlab; in my example there are three simple files: a Matlab script main.m, a Matlab class file myClass.m and a C++ file myMex.cpp.

In main.m I call the class giving a string as input.

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

The first time myMex.cpp is called, I create an object of the class CABAC and the string "test.bin" is associated to its attribute fn. Finally, the value of the pointer is returned to the Matlab environment.

The second time, I simply get the pointer to the previously instanced object, and here something weird happens. In particular, printing both the address of the pointer and the attribute fn, the former is always correct (i.e., the address is the same of the object), but the latter is sometimes correct ("test.bin" is printed) and sometimes totally and randomly wrong (strange strings appear, differently for each execution).

To execute my code, you can simply run main.m after compiling myMex.cpp with this instruction:

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

or

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

Can you help me to understand what happens to the filename?

EDIT: it seems that c is deleted after the first myMex call. How can I keep that pointer to the CABAC object (with all its attributes) in memory?


Solution

  • There are many issues here. First of all, you are casting a pointer to a double, which doesn't preserve its representation:

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

    Instead, create a 64-bit integer array to save the pointer to:

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

    (Since MATLAB R2018a there are alternatives to mxGetData for each type, in this case it would be mxGetUint64s).

    Second, the memory newed would likely be deleted when the MEX-file gets unloaded. This can happen at any time. To prevent this, lock the MEX-file in memory using mexLock. To prevent memory leaks, you will need to include code that deletes the memory when you're done with it. Since your MATLAB-class is a handle class, this can be accomplished.

    Third, it is very easy to call the MEX-file with a wrong pointer, which will likely crash all of MATLAB. There is no way around that with your current implementation.

    Fourth, as pointed out by Navan, the following line creates a copy of the string, but in a memory buffer maintained by MATLAB:

    fn = mxArrayToString(prhs[1]);
    

    MATLAB will automatically delete all memory allocated by mxMalloc and similar at the end of mexFunction. Thus, fn will point to freed memory. You need to manually create a copy here in memory that you manage. I suggest you copy the string into a std::string instead:

    class CABAC {
      public:
      std::string fn;
    };
    
    fn = mxArrayToString(prhs[1]); // Will copy the string to `fn`.
    

    However, instead I would recommend a different approach:

    • In your MEX-file, keep an array of pointers to allocated objects.
    • The MEX-file doesn't return a pointer, instead it returns the array index.
    • The MEX-file can now test the input "handle" to be within valid bounds of the array, and whether that array contains a valid pointer or a NULL pointer (e.g. for a deleted object or for an array element that hasn't been used yet).
    • Lock the MEX-file, but allow a command to unlock it. Unlocking the MEX-file would also delete all allocated objects.
    • You could include logic to automatically unlock the MEX-file when the last of the allocated objects has been deleted.

    You should know that global variables will persist between calls to the MEX-file (as long as it is locked). Thus, you would need a global array (defined outside of mexFunction) to store the pointers in. Alternatively, declare the array static inside the mexFunction.

    This MEX-file implements the recommendation described here, though it is far from a trivial example, I hope it might be useful as a starting point. In this function, I'm using a std::map instead of plain array to store the handles.