How can I store function_handle objects in MEX code?
1 Ansicht (letzte 30 Tage)
Ältere Kommentare anzeigen
Damien LEFEVRE
am 3 Jan. 2017
Kommentiert: Damien LEFEVRE
am 4 Jan. 2017
Hi,
I'm writing a MEX file to create a Qt application instance in order to get an event loop to be able to use QIODevice modules depending on it.
I can create an delete the QApplication instance. Now I would like to store a function_handle object in my mex file in order use it to trigger callbacks in Matlab.
Below are snippets of the interesting parts.
messageHandler is a static method getting called when a message comes in the system. From this method, I'm trying to use FEVAL, passing as first argument the function_handle I store from matlab, the message type as double, and the message string.
I stepped through the code and already checked instance->m_messageHandlerCallback is the same pointer as when called in the mexFunction. In mexFunction mxIsClass succeeds, but fails in CoreLibrary::messageHandler, so obviously I'm doing something illegal.
C++
void CoreLibrary::messageHandler(QtMsgType type, const QMessageLogContext &context, const QString &msg)
{
CoreLibrary* instance = CoreLibrary::instance();
const char* localMsg = msg.toLatin1().constData();
mxArray *prhs[3];
if (!mxIsClass(static_cast<const mxArray*>(instance->m_messageHandlerCallback), "function_handle"))
{
mexErrMsgTxt("Third input argument is not a function handle.");
return;
}
prhs[0] = instance->m_messageHandlerCallback;
prhs[1] = convertInt32ToMat(static_cast<int>(type));
prhs[2] = mxCreateString(msg.toLatin1().constData());
mexCallMATLAB(0, nullptr, 3, prhs, "feval");
if (type == QtFatalMsg)
{
abort();
}
mxDestroyArray(prhs[1]);
mxDestroyArray(prhs[2]);
}
void mexFunction(int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[])
{
// Get the command string
char cmd[64];
mxGetString(prhs[(nrhs == 1) ? 0 : 1], cmd, sizeof(cmd));
// New
if (!strcmp("new", cmd))
{
if (nrhs != 1)
{
mexErrMsgTxt("new: 1 inputs expected");
return;
}
if (nlhs != 1)
{
mexErrMsgTxt("new: 1 output expected");
return;
}
CoreLibrary::inc();
CoreLibrary* module = CoreLibrary::instance();
plhs[0] = convertPtr2Mat<CoreLibrary>(module);
qInstallMessageHandler(&CoreLibrary::messageHandler);
return;
}
// setMessageHandlerCallback
if (!strcmp("setMessageHandlerCallback", cmd))
{
CHK_MEX_ARGS("setMessageHandlerCallback", 3, 0);
const mxArray* functionHandle = prhs[2];
if (!mxIsClass(functionHandle, "function_handle"))
{
mexErrMsgTxt("Third input argument is not a function handle.");
return;
}
module->setMessageHandlerCallback(const_cast<mxArray*>(functionHandle));
}
// setMessageHandlerCallback
if (!strcmp("test", cmd))
{
CHK_MEX_ARGS("setMessageHandlerCallback", 3, 0);
char message[1024] = "";
mxGetString(prhs[2], message, 1024);
qDebug() << message;
}
// Delete
if (!strcmp("delete", cmd))
{
CHK_MEX_ARGS("delete", 2, 0);
CoreLibrary::dec();
return;
}
}
Matlab
classdef Core < handle
properties (SetAccess = private, Hidden = true)
objectHandle; % Handle to the underlying C++ class instance
end
methods
function self = Core(folder, appName)
self.objectHandle = CoreMex('new');
self.setMessageHandler(@self.defaultMessageHandler);
%...
self.defaultMessageHandler(4, 'Application starts');
end
function delete(self)
fclose(self.file);
CoreMex(self.objectHandle, 'delete');
end
function setMessageHandler(self, messageHandler)
self.messageHandler = messageHandler;
CoreMex(self.objectHandle, 'setMessageHandlerCallback', messageHandler);
end
function test(self)
CoreMex(self.objectHandle, 'test', 'Hello');
end
function defaultMessageHandler(self, type, message)
% ...
end
end
end
Test code
g = Core();
g.test();
Does anyone know how these function_handle can be stored on the MEX side?
Thanks
0 Kommentare
Akzeptierte Antwort
James Tursa
am 3 Jan. 2017
Not sure I follow everything that is going on with your code, but here are my observations:
self.setMessageHandler(@self.defaultMessageHandler);
In the above line of code, it appears you are passing in a function handle, and then using it in your mex routine as follows:
const mxArray* functionHandle = prhs[2];
:
module->setMessageHandlerCallback(const_cast<mxArray*>(functionHandle));
The problem with this approach is that you create the function handle in your m-code on the fly as a function argument. That means that this variable is a temporary variable that gets destroyed immediately after the self.setMessageHandler function call. So the address of this function handle variable is no longer the address of a valid mxArray after this line. But that is the mxArray address you store for downstream use in your callback. When the callback tries to subsequently access this destroyed variable bad things happen. I am actually surprised that you didn't crash MATLAB with a seg fault because of illegal memory access. (Maybe there happened to be another valid MATLAB variable stored at that address at the time of your msIsClass check)
To fix this, you need to ensure that the function handle you are storing for you callback is persistent, and not a variable that can be destroyed in any way at the m-file level. You could make the function handle at the m-file level a persistent variable instead of a temporary variable, but that leaves your mex routine vulnerable to a crash if this variable ever got inadvertently cleared at the m-file level. So maybe the best thing to do is to create a duplicate of the prhs[2], make it persistent inside the mex routine, and then have some clean-up code (e.g., mexAtExit) that will destroy it at the appropriate time (e.g., when the mex routine gets cleared or when another function handle gets passed in etc.). Something like:
const mxArray* functionHandle = mxDuplicateArray(prhs[2]);
mexMakeArrayPersistent(functionHandle);
with other added code for destroying this functionHandle per my above comments.
Weitere Antworten (0)
Siehe auch
Kategorien
Mehr zu MATLAB Compiler finden Sie in Help Center und File Exchange
Produkte
Community Treasure Hunt
Find the treasures in MATLAB Central and discover how the community can help you!
Start Hunting!