This project has moved and is read-only. For the latest updates, please go here.

au_mex.h

This is a collection of classes which greatly simplify the development of robust MEX files which smoothly handle a variety of input datatypes.

Reference

See au_mex_reference or https://awful.codeplex.com/SourceControl/latest#matlab/au_mex.h for a more detailed reference manual, or carry on here for a tutorial.

Tutorial

To see what au_mex.h offers,, take a look at this mexFunction to add two arrays:
#include "au_mex.h"

// Declare mlx_function (C++ version of mexFunction)
void mlx_function(mlx_inputs& in, mlx_outputs& out)
{
   mlx_array<double> A(in[0]); // Get input 0
   mlx_array<double> B(in[1]); // Get input 1

   mlx_make_array<double> sum(A.size); // Make output array

   // Perform the operation
   for(int i = 0; i < A.numel(); ++i)
     sum[i] = A[i] + B[i];

   out[0] = sum; // Assign to output
}
The key concepts here are the use of mlx_array to take an mxArray, and robustly extract a pointer to its data, having checked its datatype is double. mlx_make_array is a helper which makes a new array of the given size and datatype, which can be passed around just like an mxArray*, but more safely.

If you don't like the predeclared mexFunction (which just wraps its inputs and calls mlx_function), you can #define MLX_NO_MEXFUNCTION before including au_mex.h.

See the raw mexFunction to see the non-c++ version.

Better error checking

But don't stop there. Let's comprehensively bulletproof this code so any incorrect input generates an appropriate error, rather than a segfault. That means adding one line of code....

#include "au_mex.h"

void mlx_function(mlx_inputs& in, mlx_outputs& out)
{
   mlx_array<double> A(in[0]); // Get input 0
   mlx_array<double> B(in[1]); // Get input 1

   mlx_assert(A.size == B.size);// Check sizes match

   mlx_make_array<double> sum(A.size); // Make output array

   // Perform the operation
   for(int i = 0; i < A.numel(); ++i)
     sum[i] = A[i] + B[i];

   out[0] = sum; // Assign to output
}
Everything else was already checked: enough input arguments, enough output arguments, array bounds checking.... I know, you're hoping I'm not slowing you down by bounds-checking your arrays? Yes, operator[] does bounds check unless you #include "au_mex_unchecked.h". The other accessors get/put 0/1 never check.

Handling multiple datatypes

A joy of C++ is that we can easily lay down code for different datatypes using templates. MEX by default makes it rather fiddly to handle multiple datatypes, but au_mex makes it easy. Before looking at that, though let's introduce a few other features.

Having trouble remembering which Matlab datatype corresponds to which C++ one? We've typedeffed them handily for you:
typedef ... mlx_uint8;
typedef ... mlx_uint16;
typedef ... mlx_uint32;
typedef ... mlx_uint64;
typedef ... mlx_single; // etc
typedef ... mlx_double; // etc
And you can get the matlab class ID from a C++ type using e.g. mlx_class_id((double)*0)
Want to check if an array is a certain type? Try this:
if (mlx_isa<double>(a_ptr)) { // it is... }

And now let's see the full templated glory of the type dispatch example. First, we write a function template that does our operation
#include "au_mex.h"

template <class Real>
mlx_array<Real> Compute(mlx_array<Real> const& A, mlx_array<Real> const& B)
{
   mlx_assert(A.size == B.size);// Check sizes match
   
   mlx_make_array<Real> sum(A.size); // Make output array

   // Perform the operation
   for(int i = 0; i < A.numel(); ++i)
     sum[i] = A[i] + B[i];

   return sum;
}
So far so standard. Then we write a function which will take a pair of mxArrays, check sizes, array, and so on, to call the above. The key to this function is that it silently returns false if the mxArrays are the wrong type.
template <class Real>
bool try_cast(mxArray const* pA, mxArray const* pB, mlx_output* out)
{
   if (!(mlx_isa<Real>(pA) && mlx_isa<Real>(pB))) 
      return false;  // Return silently if types don't match.

   mlx_array<Real> A(pA);  
   mlx_array<Real> B(pB);
   
   *out = Compute(A, B);
   return true;
}   
Now, for each datatype I want to handle, I call try_cast, and return if it's successul:

void mlx_function(mlx_inputs& in, mlx_outputs& out)
{
   // Enumerate the types.  You really do have to do this, so that the 
   // C++ compiler can lay down different code for each case.
   // You could clean this up with a macro if you like that sort of thing.
   if (try_cast<mlx_double>(in[0], in[1], &out[0])) return;
   if (try_cast<mlx_single>(in[0], in[1], &out[0])) return;
   if (try_cast<mlx_uint8>(in[0], in[1], &out[0])) return;
 
   // If we get here, 
   mexErrMsgIdAndTxt("awful:types", "We don't handle this input type combo: %s, %s", 
           mxGetClassName(in[0]), mxGetClassName(in[1]));
}

Related work

Since this was published I have also been pointed to http://github.com/kuitang/mexcpp, and nr3matlab.

Last edited Aug 20, 2015 at 4:49 PM by awfidius, version 23

Comments

No comments yet.