I would like to introduce guest blogger Pavel Holoborodko, the developer of the Advanpix Multiprecision Computing Toolbox for MATLAB. Pavel has already posted here in the past as a guest blogger about undocumented Matlab MEX functions. Today he will discuss another little-known aspect of advanced MEX programming with Matlab.
MEX API provides two functions for proper handling of erroneous situations: the legacy mexErrMsgTxt
and the newer mexErrMsgIdAndTxt
. Both show error message, interrupt execution of a MEX module and return control to Matlab immediately.
Under the hood, these functions are implemented through C++ exceptions. The reason for this design choice is unclear. Throwing C++ exceptions across module boundary (e.g. from dynamic library to host process) is unsafe and generally considered as bad practice in software design. Exceptions are part of C++ run-time library and different versions of it might have incompatible implementations.
This restricts MEX to use only the same version of C++ run-time and GCC which were used to build that particular version of Matlab itself. This is one of the reasons why TMW distributes its own version of GCC/libstdc++ along with every release of Matlab and pushes developers to use it for MEX compilation.
Such unfortunate design decision have two unpleasant consequences:
- developers must use some old version of GCC (forget all the fancy stuff from C++11, C++14, etc.)
- compiled MEX modules most likely will not work on older/newer versions of MATLAB (re-compilation is required).
The good news is that both issues are solvable and I will write more about this in the future (it is actually possible to create single binary MEX module, which will work on any GNU Linux flavor regardless of the versions of libc/libstc++/gcc installed in the system or used in Matlab).
Here I propose just first step towards freedom – avoid direct usage of mexErrMsg**
functions. Use a simple wrapper instead:
void mxShowCriticalErrorMessage(const char *msg) { mxArray *arg; arg = mxCreateString(msg); mexCallMATLAB(0,0,1,&arg,"error"); } |
The mxShowCriticalErrorMessage
function calls Matlab’s built-in error function via the interpreter, with the error message as input parameter.
In addition to being safe, this approach potentially gives us better control over what additional information is shown together with error messages. Instead of a string, we can use an errorStruct
as input argument to Matlab’s error function, with its fields tuned to our requirements (not shown here as I want to keep the example simple).
Even without tuning, output of mxShowCriticalErrorMessage
is much more informative and user-friendly:
- Error message from Matlab’s built-in functionality:
>> A = magic(3); >> A(0) Subscript indices must either be real positive integers or logicals.
Nice one-line message without any distracting information.
- Error message from MEX using
mexErrMsgTxt
/mexErrMsgIdAndTxt
:
>> A = mp(magic(3)); % convert matrix to arbitrary precision type, provided by our toolbox >> A(0) % subsref is called from toolbox, it is implemented in mpimpl.mex Error using mpimpl Subscript indices must either be real positive integers or logicals. Error in mp/subsref (line 860) [varargout{1:nargout}] = mpimpl(170, varargin{:});
Intimidating four lines with actual error message lost in the middle. All the additional information is meaningless for end-user and actually misleading.
The worst thing is that such error message is very different from what user get used to (see above one-liner), which leads to confusion if MEX plugin is properly working at all.
- Error message from MEX using
mxShowCriticalErrorMessage
:
>> A = magic(3); >> A(0) Error using mp/subsref (line 860) Subscript indices must either be real positive integers or logicals.
Now the message is clear and short, with error description in the last line where the user focus is.
A very interesting post. Is this problem persists for compiling MEX with
c
orFortran
, or is there another behaviour altogether?I guess it will come down to compatibility of
setjmp/longjmp
used in LIBC, that you compile your MEX against.(C++ exceptions are implemented through these low-level functions with all the additional things for calling class destructors, etc.)
Cannot say about Fortran though – but probably the situation is similar there.