Skip to main content


Tip: Writing mex files

In the final part of this talk, we go into additional detail on the use of mexFunctions, in particular the procedure for extracting information from the passed mxArrays, and putting information computed in C back into mxArrays for eventual use in MATLAB. This extended example computes the row averages of an m*n input matrix and returns them in an m*1 array to the user. Note: writing an averaging function of this sort is not really useful for anything outside of an exercise in learning to write mex files. In general, one should always prefer built-in MATLAB functions (in this case, 'mean') to perform basic tasks as they are always correct and nearly always as efficient or more than anything user-coded in C.

File: avg.c

#include <stdio.h>
#include "mex.h"
void mexFunction(int nlhs, mxArray *plhs[],
                 int nrhs, const mxArray *prhs[])
{
    double *input, *output;
    int i, j, n_rows, n_cols;
    double avg;

    if ((nrhs != 1) || (nlhs > 1)) {
        printf("Error! Expecting exactly 1 rhs and up to 1 lhs argument!\n");
        return;
    }
        
    input = mxGetPr(prhs[0]);
    n_rows = mxGetM(prhs[0]);
    n_cols = mxGetN(prhs[0]);

    if (nlhs == 1) {
        plhs[0] = mxCreateDoubleMatrix(n_rows, 1, mxREAL);
        output = mxGetPr(plhs[0]);
    }
        
    for(i=0; i<n_rows; i++) {
        avg = 0.;
        for(j=0; j<n_cols; j++)
            avg += input[(i*n_cols)+j];
        avg /= (double)n_cols;

        if (nlhs == 1)
            output[i] = avg;
    }
} /* end mexFunction */

As in the previous example, our mex function is named mexFunction and is defined in its own C file, in this case avg.c. Also as before, the mexFunction takes four arguments: nlhs, nrhs, plhs, and prhs, and returns void. The function begins by defining several variables which will be required in the computation of the averages. 'input' and 'output' are of type pointer-to-double and will eventually be used to point at the incoming m*n array and the resulting m*1 outgoing result array. 'i' and 'j' are loop variables, 'n_rows' and 'n_cols' are the number of rows and columns, respectively, of the incoming m*n array. Finally, 'avg' is a temporary variable which will be used inside the loop to accumulate row sums and temporarily store the average before it is moved to the output storage.

Next, we perform an essential task for any mex function: checking the input variables. Recall that because MATLAB functions can be called from the interpreter with any number of input (and output) variables, including zero, we must be able to handle unexpected argument combinations in our mex file gracefully. In this particular case, we can handle exactly 1 right-hand side argument (nrhs==1) and 0 or 1 left-hand side arguments: the user may catch the output or may not. Therefore, as shown we print an error message and return immediately if either of these conditions is not met. Accessing an undefined entry in plhs will almost certainly lead to a segmentation fault violation and crash the running MATLAB process, so this input-checking step is essential.

After checking the user input, we need to actually extract the data from the MATLAB mxArray objects for processing. The 'mxGetPr' function takes a pointer to an mxArray object and returns a pointer-to-double which is usable in C. This function, which stands for "get pointer to real," is defined, like all other MATLAB-specific functions, in the mex.h header file included at the beginning of the avg.c source. In addition to mxGetPr, MATLAB also provides the 'mxGetPi' function which returns a pointer to the complex-valued data (if any) held in an mxArray. We next call the 'mxGetM' and 'mxGetN' functions which return, respectively, the number of rows "M" and columns "N" which are represented by the passed mxArray data object. In each of the mxGetPr, mxGetM, and mxGetN calls we have passed 'prhs[0]', that is, a pointer to the zeroth location of the prhs array. We know this location is safe to access since we have previously checked that nrhs==1. (Recall that C is "zero-based" so array accesses go from 0 to nrhs-1 in contrast to MATLAB which is "one-based".)

Next we need to create storage for the return result, but only if the user has actually tried to "catch" the result, i.e. if nlhs==1. MATLAB's mex API provides a special function to allocate storage in mxArrays called mxCreateDoubleMatrix. The mxCreateDoubleMatrix function must be passed three arguments: the number of rows, the number of columns and the type to be stored in the matrix, in this case mxREAL. The return value of mxCreateDoubleMatrix is a pointer to an mxArray object, and in this example we store this returned pointer in plhs[0]. Once storage in the mxArray has been created, we still need to be able to access it from C, and for this purpose we will use the pointer-to-double 'output' declared earlier. We can assign 'output' to point at plhs[0] by once again utilizing the 'mxGetPr' function.

Now that the data arrays have been created and properly set up, we are ready to perform the actual averaging algorithm. The algorithm itself is rather simple, and uses the fact that the input array is stored in so-called "row-major" format. Row-major storage means that a logically two (or more) dimensional array is stored in a one-dimensional storage location, row by row. To access the (i,j) entry of the original two-dimensional data, you must compute the correct offset into the one-dimensional storage, which is given by 'i*n_cols + j', where n_cols is the number of columns in the two-dimensional array. The averaging algorithm first loops over each row in the input data and computes the sum of the row entries. This step is followed by division by the number of columns, note that we explicitly cast the integer variable 'n_cols' to a double before performing the division, to ensure that floating-point division is actually performed instead of integer division. Finally, if the user has requested to catch the output, we assign the newly-computed average to the i'th entry of the output vector. This concludes the mex function, and the file avg.c.

>> mex avg.c
>> y = avg(rand(3));
>> y
    0.6977
    0.7476
    0.4931

After compiling our function (by running 'mex avg.c' from the MATLAB interpreter) we can test out its function on for example a 3*3 array of random data given by rand(3). The reader should verify that MATLAB's built-in 'mean' function gives the same result as the hand-coded mex function when passed the same data array. Finally, let us also attempt an invalid calling sequence with our newly-created mex function. If we ask for two return values, say '[y,z]' instead of 1, the error message from avg.c should be triggered and the function should return immediately. Note that MATLAB also warns that one of the output arguments was not assigned during the call to avg: this additional error checking happens automatically for all mex functions without any additional input from the user. The same error message would be triggered if e.g. the user had called the 'avg' function without any rhs inputs.

>> [y,z] = avg(rand(3));
Error! Expecting exactly 1 rhs and up ...
    to 1 lhs argument!
??? One or more output arguments not ...
    assigned during call to "my_avg".

This example has only scratched the surface of what is possible in MATLAB mex file programming. For example, we have not discussed working with strings, scalar values, complex data, calling MATLAB functions from mex files, creating cell arrays in mex files, generating MATLAB errors from mex files, or writing FORTRAN-based mex files. Additional mex file examples can be found in the $matlab/extern/examples/mx directory of your MATLAB installation, and a number of useful tutorials can be found on the Internet. Finally, we note that GNU Octave also provides support for writing mex files and therefore you are not limited to using your mex functions only in the official MATLAB product.