Getting Started with Eigen and MEX Files

Here, we consider how to process MATLAB data with the Eigen linear algebra library. For an introduction to Eigen itself, please refer to the excellent and extensive Eigen documentation. Furthermore, for more information about MATLAB's C/C++ interface, see C/C++ Matrix Library API.

The Nonlinear Estimation Toolbox offers, among others, the two major C++ wrapper classes to easily work with MATLAB data

  • Mex::ConstMatrix to access MATLAB input matrices in C++

  • Mex::OutputMatrix to return matrices back to MATLAB

Both require three template parameters

  1. The scalar matrix data type (e.g., double or int32_t). It must coincide with the data type of the MATLAB matrix passed to the MEX function (e.g., 'double' or 'int32'). By default, all MATLAB matrices are of the type 'double'. Hence, the C++ scalar type is most times double like in the following examples.

  2. The number of matrix rows (e.g., 3). If the number of rows is not known at compile time, use Eigen::Dynamic instead.

  3. The number of matrix columns (e.g., 5). If the number of columns is not known at compile time, use Eigen::Dynamic instead.

This is identical to the concepts of the Eigen library. So let's start with a first short MEX file example:

simpleMexFile.cpp

A first C++ example.
#include <Mex/Mex.h>

void mexFunction(int numOutputs, mxArray *outputs[],
                 int numInputs, const mxArray *inputs[])
{
    try {
        /* Check for proper number of arguments */
        if (numInputs != 2) {
            throw std::invalid_argument("Two inputs are required.");
        }
        
        if (numOutputs != 1) {
            throw std::invalid_argument("One output is required.");
        }
        
        /* Extract first input parameter
         * Here, we require that the first input parameter is a 3xN matrix.
         *
         * The Mex::ConstMatrix class is designed to make the readonly
         * input mxArrays accessible for the Eigen framework. As we now that
         * the data is readonly, compilation will fail if we (accidental)
         * try to modify its content. Moreover, as we only now the number
         * of rows at compile time, the number of columns template parameter
         * is set to accept any possible value.
         */
        Mex::ConstMatrix<double, 3, Eigen::Dynamic> mat(inputs[0]);
        
        /* Extract second input parameter
         * Here, we require the second input parameter to be a 3D vector.
         *
         * As we know at compile time that the second input is a 3D vector
         * we totally avoid Eigen::Dynamic for performance reasons and
         * allow static code analysis for code correctness during compilation.
         */
        Mex::ConstMatrix<double, 3, 1> vec(inputs[1]);
        
        /* Compute the result
         *
         * The Mex::OutputMatrix is designed to return the compuation result
         * of the MEX file. That is, it doesn't free its internal memory to
         * make it accessible to MATLAB after the MEX file execution.
         *
         * Do something useful with the powerful Eigen library.
         * Here, we subtract the second input (the 3D vector) column-wise
         * from the first input (the 3xN matrix), and finally multiply
         * the resulting matrix by 3.
         */
        Mex::OutputMatrix<double, 3, Eigen::Dynamic> output = mat.colwise() - vec;
        
        output *= 3;
        
        /* Return the computed result back to MATLAB
         *
         * The Mex::OutputMatrix is capable of casting an instance
         * to its internal mxArray pointer. Here, we use this functionality
         * to pass our computation result back to MATLAB.
         */
        outputs[0] = output;
    } catch (std::exception& ex) {
        /* In case of any exception, issue a MATLAB exception with
         * the text of the C++ exception. This terminates the MEX
         * file without returning any data back to MATLAB.
         */
        mexErrMsgTxt(ex.what());
    }
}

Compile the source code to a MEX file with

>> compileMex('simpleMexFile.cpp')
Building with 'g++'.
MEX completed successfully.

and execute the MEX function for example with

>> result = simpleMexFile(3 * ones(3, 10), -2 * ones(3, 1))

result =

    15    15    15    15    15    15    15    15    15    15
    15    15    15    15    15    15    15    15    15    15
    15    15    15    15    15    15    15    15    15    15

It is important to note that there is no unnecessary coping of input matrix data. The raw MATLAB matrix data will only be mapped to plain C array in order to make it accessible for the Eigen library. Hence, passing a matrix of any size will only cause a slight runtime overhead.

When passing a matrix with a wrong number of rows will issue an error like this

>> result = simpleMexFile(3 * ones(2, 10), -2 * ones(3, 1))
Error using simpleMexFile
Mismatch between given (2) and expected (3) number of rows.

Also when trying to pass a matrix of wrong data type will be catched

>> result = simpleMexFile(3 * ones(3, 10), 'string')
Error using simpleMexFile
MX array of invalid type.
  
>> result = simpleMexFile(3 * ones(3, 10), -2 * ones(3, 1, 'int32'))
Error using simpleMexFile
MX array of invalid type.

That's all! You have successfully wrote your first MEX file that processed matrix data with Eigen and returned the result back to MATLAB. Now, it's time to write your first system model in C++.

You can split your source code in several files (e.g., for better reusability) and simply pass all file names to compileMex() to compile the MEX file from those files. For example, compileMex('file1.cpp', 'file2.cpp').

You can specify any parameters/options for the actual mex compiler by passing them to compileMex() in addition to the source files. For example, compileMex('file1.cpp', 'file2.cpp', '-Ifoo/bar', '-output', 'MexFile', '-v') adds the include directory foo/bar, names the resulting MEX file "MexFile", and shows verbose compile information.

Every read or write operation in a memory region where you do not have the permissions (e.g., out of range array accesses) will likely cause a segmentation fault. MATLAB reacts on such situations with an abrupt termination of the entire MATLAB program. Every unsaved file or variable in your workspace will be lost! Hence, make sure that you never exceed the dimensions of a matrix. This can happen for example when a smaller matrix than expected is passed to your MEX file and you do not check the inputs properly.