    // Copyright (C) 2012 Massachusetts Institute of Technology, Lincoln Laboratory
    // License: Boost Software License   See LICENSE.txt for the full license.
    // Authors: Davis E. King (davis@dlib.net)
    /*
                                   READ THIS FIRST
                                       ######
                                       ######
                                       ######
                                       ######
                                       ######
                                       ######
                                       ######
                                       ######
                                       ######
                                       ######
                                       ######
                                       ######
                                       ######
                                   \############/
                                    \##########/
                                     \########/
                                      \######/
                                       \####/
                                        \##/
                                         \/

        !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!

        See example_mex_function.cpp for a discussion of how to use the mex wrapper.

        !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!

                                         /\
                                        /##\
                                       /####\
                                      /######\
                                     /########\
                                    /##########\
                                   /############\
                                       ######
                                       ######
                                       ######
                                       ######
                                       ######
                                       ######
                                       ######
                                       ######
                                       ######
                                       ######
                                       ######
                                       ######
                                       ######
                                   READ THIS FIRST
    */

    // Copyright (C) 2012 Massachusetts Institute of Technology, Lincoln Laboratory
    // License: Boost Software License   See LICENSE.txt for the full license.
    // Authors: Davis E. King (davis@dlib.net)

    // ----------------------------------------------------------------------------------------
    // ----------------------------------------------------------------------------------------
    //                            BEGIN IMPLEMENTATION DETAILS
    // ----------------------------------------------------------------------------------------
    // ----------------------------------------------------------------------------------------

    #include "../matrix.h"
    #include "../array2d.h"
    #include "../array.h"
    #include "../image_transforms.h"
    #include "../is_kind.h"
    #include "../any.h" // for sig_traits

    #if defined(_MSC_VER)
    #define DLL_EXPORT_SYM __declspec(dllexport)
    #endif
    #include "mex.h"
    #include <sstream>
    #include "call_matlab.h"

    // ----------------------------------------------------------------------------------------

    #ifdef ARG_1_DEFAULT
    #define ELSE_ASSIGN_ARG_1 else A1 = ARG_1_DEFAULT;
    #else
    #define ELSE_ASSIGN_ARG_1
    #endif

    #ifdef ARG_2_DEFAULT
    #define ELSE_ASSIGN_ARG_2 else A2 = ARG_2_DEFAULT;
    #else
    #define ELSE_ASSIGN_ARG_2
    #endif

    #ifdef ARG_3_DEFAULT
    #define ELSE_ASSIGN_ARG_3 else A3 = ARG_3_DEFAULT;
    #else
    #define ELSE_ASSIGN_ARG_3
    #endif

    #ifdef ARG_4_DEFAULT
    #define ELSE_ASSIGN_ARG_4 else A4 = ARG_4_DEFAULT;
    #else
    #define ELSE_ASSIGN_ARG_4
    #endif

    #ifdef ARG_5_DEFAULT
    #define ELSE_ASSIGN_ARG_5 else A5 = ARG_5_DEFAULT;
    #else
    #define ELSE_ASSIGN_ARG_5
    #endif

    #ifdef ARG_6_DEFAULT
    #define ELSE_ASSIGN_ARG_6 else A6 = ARG_6_DEFAULT;
    #else
    #define ELSE_ASSIGN_ARG_6
    #endif

    #ifdef ARG_7_DEFAULT
    #define ELSE_ASSIGN_ARG_7 else A7 = ARG_7_DEFAULT;
    #else
    #define ELSE_ASSIGN_ARG_7
    #endif

    #ifdef ARG_8_DEFAULT
    #define ELSE_ASSIGN_ARG_8 else A8 = ARG_8_DEFAULT;
    #else
    #define ELSE_ASSIGN_ARG_8
    #endif

    #ifdef ARG_9_DEFAULT
    #define ELSE_ASSIGN_ARG_9 else A9 = ARG_9_DEFAULT;
    #else
    #define ELSE_ASSIGN_ARG_9
    #endif

    #ifdef ARG_10_DEFAULT
    #define ELSE_ASSIGN_ARG_10 else A10 = ARG_10_DEFAULT;
    #else
    #define ELSE_ASSIGN_ARG_10
    #endif

    // ----------------------------------------------------------------------------------------

    namespace mex_binding
    {
        using namespace dlib;

        template <typename T>
        struct is_input_type
        {
            const static unsigned long value = (!is_same_type<void,T>::value && (!is_reference_type<T>::value || is_const_type<T>::value )) ? 1 : 0;
        };
        template <typename T>
        struct is_output_type
        {
            const static unsigned long value = (!is_same_type<void,T>::value && is_reference_type<T>::value && !is_const_type<T>::value) ? 1 : 0;
        };

        template <typename funct>
        struct funct_traits
        {
            const static unsigned long num_inputs = is_input_type<typename sig_traits<funct>::arg1_type>::value +
                is_input_type<typename sig_traits<funct>::arg2_type>::value +
                is_input_type<typename sig_traits<funct>::arg3_type>::value +
                is_input_type<typename sig_traits<funct>::arg4_type>::value +
                is_input_type<typename sig_traits<funct>::arg5_type>::value +
                is_input_type<typename sig_traits<funct>::arg6_type>::value +
                is_input_type<typename sig_traits<funct>::arg7_type>::value +
                is_input_type<typename sig_traits<funct>::arg8_type>::value +
                is_input_type<typename sig_traits<funct>::arg9_type>::value +
                is_input_type<typename sig_traits<funct>::arg10_type>::value;

            const static unsigned long num_outputs= is_output_type<typename sig_traits<funct>::arg1_type>::value +
                is_output_type<typename sig_traits<funct>::arg2_type>::value +
                is_output_type<typename sig_traits<funct>::arg3_type>::value +
                is_output_type<typename sig_traits<funct>::arg4_type>::value +
                is_output_type<typename sig_traits<funct>::arg5_type>::value +
                is_output_type<typename sig_traits<funct>::arg6_type>::value +
                is_output_type<typename sig_traits<funct>::arg7_type>::value +
                is_output_type<typename sig_traits<funct>::arg8_type>::value +
                is_output_type<typename sig_traits<funct>::arg9_type>::value +
                is_output_type<typename sig_traits<funct>::arg10_type>::value;
        };

    // ----------------------------------------------------------------------------------------

        template <typename T>
        struct is_array_type
        {
            // true if T is std::vector or dlib::array
            const static bool value = is_std_vector<T>::value || dlib::is_array<T>::value;

        };

    // ----------------------------------------------------------------------------------------

        template <
            typename T,
            typename enabled = void
            >
        struct inner_type
        {
            typedef T type;
        };

        template < typename T>
        struct inner_type<T, typename dlib::enable_if_c<is_matrix<T>::value || is_array2d<T>::value || dlib::is_array<T>::value >::type>
        {
            typedef typename T::type type;
        };

        template < typename T>
        struct inner_type<T, typename dlib::enable_if<is_std_vector<T> >::type>
        {
            typedef typename T::value_type type;
        };

    // -------------------------------------------------------

        struct invalid_args_exception
        {
            invalid_args_exception(const std::string& msg_): msg(msg_) {}
            std::string msg;
        };

    // -------------------------------------------------------

        template <
            typename matrix_type,
            typename EXP
            >
        typename dlib::enable_if_c<is_matrix<matrix_type>::value && is_same_type<typename inner_type<matrix_type>::type,typename EXP::type>::value >::type
        assign_mat (
            const long arg_idx,
            matrix_type& m,
            const matrix_exp<EXP>& src
        )
        {
            if (matrix_type::NR != 0 && matrix_type::NR != src.nc())
            {
                std::ostringstream sout;
                sout << "Argument " << arg_idx+1 << " expects a matrix with " << matrix_type::NR << " rows but got one with " << src.nc();
                mexErrMsgIdAndTxt("mex_function:validate_and_populate_arg",
                                  sout.str().c_str());
            }
            if (matrix_type::NC != 0 && matrix_type::NC != src.nr())
            {
                std::ostringstream sout;
                sout << "Argument " << arg_idx+1 << " expects a matrix with " << matrix_type::NC << " columns but got one with " << src.nr();
                mexErrMsgIdAndTxt("mex_function:validate_and_populate_arg",
                                  sout.str().c_str());
            }

            m = trans(src);
        }

        template <
            typename matrix_type,
            typename EXP
            >
        typename dlib::enable_if_c<is_array2d<matrix_type>::value && is_same_type<typename inner_type<matrix_type>::type,typename EXP::type>::value >::type
        assign_mat (
            const long arg_idx,
            matrix_type& m,
            const matrix_exp<EXP>& src
        )
        {
            assign_image(m , trans(src));
        }

        template <
            typename matrix_type,
            typename EXP
            >
        typename disable_if_c<(is_array2d<matrix_type>::value || is_matrix<matrix_type>::value) &&
        is_same_type<typename inner_type<matrix_type>::type,typename EXP::type>::value >::type
        assign_mat (
            const long arg_idx,
            matrix_type& ,
            const matrix_exp<EXP>&
        )
        {
            std::ostringstream sout;
            sout << "mex_function has some bug in it related to processing input argument " << arg_idx+1;
            mexErrMsgIdAndTxt("mex_function:validate_and_populate_arg",
                              sout.str().c_str());
        }

    // -------------------------------------------------------

        template <
            typename T,
            typename U
            >
        typename dlib::enable_if_c<is_built_in_scalar_type<T>::value || is_same_type<T,bool>::value >::type
        assign_scalar (
            const long arg_idx,
            T& dest,
            const U& src
        )
        {
            if (is_signed_type<U>::value && src < 0 && is_unsigned_type<T>::value)
            {
                std::ostringstream sout;
                sout << "Error, input argument " << arg_idx+1 << " must be a non-negative number.";
                mexErrMsgIdAndTxt("mex_function:validate_and_populate_arg",
                                  sout.str().c_str());
            }
            else
            {
                dest = src;
            }
        }

        template <
            typename T,
            typename U
            >
        typename dlib::disable_if_c<is_built_in_scalar_type<T>::value || is_same_type<T,bool>::value >::type
        assign_scalar (
            const long arg_idx,
            T& ,
            const U&
        )
        {
            std::ostringstream sout;
            sout << "mex_function has some bug in it related to processing input argument " << arg_idx+1;
            mexErrMsgIdAndTxt("mex_function:validate_and_populate_arg",
                              sout.str().c_str());
        }

    // -------------------------------------------------------

        void assign_function_handle (
            const long arg_idx,
            function_handle& dest,
            const mxArray* src
        )
        {
            const_cast<void*&>(dest.h) = (void*)src;
        }

        template <
            typename T
            >
        void assign_function_handle (
            const long arg_idx,
            T& ,
            const mxArray*
        )
        {
            std::ostringstream sout;
            sout << "mex_function has some bug in it related to processing input argument " << arg_idx+1;
            mexErrMsgIdAndTxt("mex_function:validate_and_populate_arg",
                              sout.str().c_str());
        }

    // -------------------------------------------------------

        template <
            typename T
            >
        typename dlib::enable_if<is_array_type<T> >::type
        assign_std_vector (
            const long arg_idx,
            T& dest,
            const mxArray* src
        )
        {
            const long nr = mxGetM(src);
            const long nc = mxGetN(src);

            typedef typename inner_type<T>::type type;

            if (!mxIsCell(src))
            {
                std::ostringstream sout;
                sout << " argument " << arg_idx+1 << " must be a cell array";
                throw invalid_args_exception(sout.str());
            }
            if (nr != 1 && nc != 1)
            {
                std::ostringstream sout;
                sout << " argument " << arg_idx+1 << " must be a cell array with exactly 1 row or 1 column (i.e. a row or column vector)";
                throw invalid_args_exception(sout.str());
            }

            const long size = nr*nc;
            dest.resize(size);

            for (unsigned long i = 0; i < dest.size(); ++i)
            {
                try
                {
                    validate_and_populate_arg(i, mxGetCell(src, i), dest[i]);
                }
                catch (invalid_args_exception& e)
                {
                    std::ostringstream sout;
                    sout << "Error in argument " << arg_idx+1 << ": element " << i+1 << " of cell array not the expected type.\n";
                    sout << "\t" << e.msg;
                    throw invalid_args_exception(sout.str());
                }
            }

        }

        template <
            typename T
            >
        typename disable_if<is_array_type<T> >::type
        assign_std_vector (
            const long arg_idx,
            T& ,
            const mxArray*
        )
        {
            std::ostringstream sout;
            sout << "mex_function has some bug in it related to processing input argument " << arg_idx+1;
            mexErrMsgIdAndTxt("mex_function:validate_and_populate_arg",
                              sout.str().c_str());
        }

    // -------------------------------------------------------

        template <typename T>
        void assign_image (
            const long arg_idx,
            T&,
            const dlib::uint8* data,
            long nr,
            long nc
        )
        {
            std::ostringstream sout;
            sout << "mex_function has some bug in it related to processing input argument " << arg_idx+1;
            mexErrMsgIdAndTxt("mex_function:validate_and_populate_arg",
                              sout.str().c_str());
        }

        template <typename MM>
        void assign_image(
            const long ,
            array2d<dlib::rgb_pixel,MM>& img,
            const dlib::uint8* data,
            long nr,
            long nc
        )
        {
            img.set_size(nr, nc);
            for (long c = 0; c < img.nc(); ++c)
                for (long r = 0; r < img.nr(); ++r)
                    img[r][c].red = *data++;
            for (long c = 0; c < img.nc(); ++c)
                for (long r = 0; r < img.nr(); ++r)
                    img[r][c].green = *data++;
            for (long c = 0; c < img.nc(); ++c)
                for (long r = 0; r < img.nr(); ++r)
                    img[r][c].blue = *data++;
        }

    // -------------------------------------------------------

        template <typename T>
        void validate_and_populate_arg (
            long arg_idx,
            const mxArray *prhs,
            T& arg
        )
        {
            using namespace mex_binding;
            if (is_built_in_scalar_type<T>::value || is_same_type<T,bool>::value)
            {
                if( !(mxIsDouble(prhs) || mxIsSingle(prhs) || mxIsLogical(prhs) ) ||
                    mxIsComplex(prhs) ||
                    mxGetNumberOfElements(prhs)!=1 )
                {
                    std::ostringstream sout;
                    sout << " argument " << arg_idx+1 << " must be a scalar";
                    throw invalid_args_exception(sout.str());
                }

                assign_scalar(arg_idx, arg , mxGetScalar(prhs));
            }
            else if (is_matrix<T>::value || is_array2d<T>::value)
            {
                typedef typename inner_type<T>::type type;

                const int num_dims = mxGetNumberOfDimensions(prhs);
                const long nr = mxGetM(prhs);
                const long nc = mxGetN(prhs);

                if (is_same_type<type,dlib::rgb_pixel>::value)
                {
                    if (!(num_dims == 3 && mxGetDimensions(prhs)[2] == 3 && mxIsUint8(prhs)))
                    {
                        std::ostringstream sout;
                        sout << " argument " << arg_idx+1 << " must be a 3-D NxMx3 image matrix of uint8";
                        throw invalid_args_exception(sout.str());
                    }

                    const long rows = mxGetDimensions(prhs)[0];
                    const long cols = mxGetDimensions(prhs)[1];
                    assign_image(arg_idx, arg , (const dlib::uint8*)mxGetData(prhs), rows, cols);
                    return;
                }

                if (num_dims != 2)
                {
                    std::ostringstream sout;
                    sout << " argument " << arg_idx+1 << " must be a 2-D matrix (got a " << num_dims << "-D matrix)";
                    throw invalid_args_exception(sout.str());
                }

                if (is_same_type<type,double>::value)
                {
                    if (!mxIsDouble(prhs) || mxIsComplex(prhs))
                    {
                        std::ostringstream sout;
                        sout << " argument " << arg_idx+1 << " must be a matrix of doubles";
                        throw invalid_args_exception(sout.str());
                    }
                    assign_mat(arg_idx, arg , pointer_to_matrix(mxGetPr(prhs), nc, nr));
                }
                else if (is_same_type<type, float>::value)
                {
                    if (!mxIsSingle(prhs) || mxIsComplex(prhs))
                    {
                        std::ostringstream sout;
                        sout << " argument " << arg_idx+1 << " must be a matrix of single/float";
                        throw invalid_args_exception(sout.str());
                    }

                    assign_mat(arg_idx, arg , pointer_to_matrix((const float*)mxGetData(prhs), nc, nr));
                }
                else if (is_same_type<type, bool>::value)
                {
                    if (!mxIsLogical(prhs))
                    {
                        std::ostringstream sout;
                        sout << " argument " << arg_idx+1 << " must be a matrix of logical elements.";
                        throw invalid_args_exception(sout.str());
                    }

                    assign_mat(arg_idx, arg , pointer_to_matrix((const bool*)mxGetData(prhs), nc, nr));
                }
                else if (is_same_type<type, dlib::uint8>::value)
                {
                    if (!mxIsUint8(prhs) || mxIsComplex(prhs))
                    {
                        std::ostringstream sout;
                        sout << " argument " << arg_idx+1 << " must be a matrix of uint8";
                        throw invalid_args_exception(sout.str());
                    }

                    assign_mat(arg_idx, arg , pointer_to_matrix((const dlib::uint8*)mxGetData(prhs), nc, nr));
                }
                else if (is_same_type<type, dlib::int8>::value)
                {
                    if (!mxIsInt8(prhs) || mxIsComplex(prhs))
                    {
                        std::ostringstream sout;
                        sout << " argument " << arg_idx+1 << " must be a matrix of int8";
                        throw invalid_args_exception(sout.str());
                    }

                    assign_mat(arg_idx, arg , pointer_to_matrix((const dlib::int8*)mxGetData(prhs), nc, nr));
                }
                else if (is_same_type<type, dlib::int16>::value ||
                        (is_same_type<type, short>::value && sizeof(short) == sizeof(dlib::int16)))
                {
                    if (!mxIsInt16(prhs) || mxIsComplex(prhs))
                    {
                        std::ostringstream sout;
                        sout << " argument " << arg_idx+1 << " must be a matrix of int16";
                        throw invalid_args_exception(sout.str());
                    }

                    assign_mat(arg_idx, arg , pointer_to_matrix((const type*)mxGetData(prhs), nc, nr));
                }
                else if (is_same_type<type, dlib::uint16>::value ||
                        (is_same_type<type, unsigned short>::value && sizeof(unsigned short) == sizeof(dlib::uint16)))
                {
                    if (!mxIsUint16(prhs) || mxIsComplex(prhs))
                    {
                        std::ostringstream sout;
                        sout << " argument " << arg_idx+1 << " must be a matrix of uint16";
                        throw invalid_args_exception(sout.str());
                    }

                    assign_mat(arg_idx, arg , pointer_to_matrix((const type*)mxGetData(prhs), nc, nr));
                }
                else if (is_same_type<type, dlib::int32>::value ||
                        (is_same_type<type, int>::value && sizeof(int) == sizeof(dlib::int32)) ||
                        (is_same_type<type, long>::value && sizeof(long) == sizeof(dlib::int32)))
                {
                    if (!mxIsInt32(prhs) || mxIsComplex(prhs))
                    {
                        std::ostringstream sout;
                        sout << " argument " << arg_idx+1 << " must be a matrix of int32";
                        throw invalid_args_exception(sout.str());
                    }

                    assign_mat(arg_idx, arg , pointer_to_matrix((const type*)mxGetData(prhs), nc, nr));
                }
                else if (is_same_type<type, dlib::uint32>::value ||
                        (is_same_type<type, unsigned int>::value && sizeof(unsigned int) == sizeof(dlib::uint32)) ||
                        (is_same_type<type, unsigned long>::value && sizeof(unsigned long) == sizeof(dlib::uint32)))
                {
                    if (!mxIsUint32(prhs) || mxIsComplex(prhs))
                    {
                        std::ostringstream sout;
                        sout << " argument " << arg_idx+1 << " must be a matrix of uint32";
                        throw invalid_args_exception(sout.str());
                    }

                    assign_mat(arg_idx, arg , pointer_to_matrix((const type*)mxGetData(prhs), nc, nr));
                }
                else if (is_same_type<type, dlib::uint64>::value ||
                        (is_same_type<type, unsigned int>::value && sizeof(unsigned int) == sizeof(dlib::uint64)) ||
                        (is_same_type<type, unsigned long>::value && sizeof(unsigned long) == sizeof(dlib::uint64)))
                {
                    if (!mxIsUint64(prhs) || mxIsComplex(prhs))
                    {
                        std::ostringstream sout;
                        sout << " argument " << arg_idx+1 << " must be a matrix of uint64";
                        throw invalid_args_exception(sout.str());
                    }

                    assign_mat(arg_idx, arg , pointer_to_matrix((const type*)mxGetData(prhs), nc, nr));
                }
                else if (is_same_type<type, dlib::int64>::value ||
                        (is_same_type<type, int>::value && sizeof(int) == sizeof(dlib::int64)) ||
                        (is_same_type<type, long>::value && sizeof(long) == sizeof(dlib::int64)))
                {
                    if (!mxIsInt64(prhs) || mxIsComplex(prhs))
                    {
                        std::ostringstream sout;
                        sout << " argument " << arg_idx+1 << " must be a matrix of int64";
                        throw invalid_args_exception(sout.str());
                    }

                    assign_mat(arg_idx, arg , pointer_to_matrix((const type*)mxGetData(prhs), nc, nr));
                }
                else
                {
                    mexErrMsgIdAndTxt("mex_function:validate_and_populate_arg",
                                      "mex_function uses unsupported matrix type");
                }
            }
            else if (is_array_type<T>::value)
            {
                assign_std_vector(arg_idx, arg, prhs);

            }
            else if (is_same_type<T,function_handle>::value)
            {
                if (!mxIsClass(prhs, "function_handle"))
                {
                    std::ostringstream sout;
                    sout << " argument " << arg_idx+1 << " must be a function handle.";
                    throw invalid_args_exception(sout.str());
                }
                assign_function_handle(arg_idx, arg, prhs);
            }
            else
            {
                mexErrMsgIdAndTxt("mex_function:validate_and_populate_arg",
                                  "mex_function uses unsupported input argument type");
            }
        }

        void validate_and_populate_arg(
            long arg_idx,
            const mxArray *prhs,
            std::string& arg
        )
        {
            if (!mxIsChar(prhs))
            {
                std::ostringstream sout;
                sout << " argument " << arg_idx+1 << " must be a char string";
                throw invalid_args_exception(sout.str());
            }

            const long nr = mxGetM(prhs);
            const long nc = mxGetN(prhs);
            const long size = nr*nc;
            arg.resize(size+1);
            if (mxGetString(prhs, &arg[0], arg.size()))
            {
                std::ostringstream sout;
                sout << " argument " << arg_idx+1 << " encountered an error while calling mxGetString()";
                throw invalid_args_exception(sout.str());
            }
            arg.resize(size);
        }

    // ----------------------------------------------------------------------------------------

        template <typename EXP>
        typename dlib::enable_if<is_same_type<dlib::rgb_pixel,typename EXP::type> >::type assign_image_to_matlab (
            dlib::uint8* mat,
            const matrix_exp<EXP>& item
        )
        {
            for (long c = 0; c < item.nc(); ++c)
                for (long r = 0; r < item.nr(); ++r)
                    *mat++ = item(r,c).red;
            for (long c = 0; c < item.nc(); ++c)
                for (long r = 0; r < item.nr(); ++r)
                    *mat++ = item(r,c).green;
            for (long c = 0; c < item.nc(); ++c)
                for (long r = 0; r < item.nr(); ++r)
                    *mat++ = item(r,c).blue;
        }

        template <typename T, typename EXP>
        typename disable_if<is_same_type<dlib::rgb_pixel,typename EXP::type> >::type assign_image_to_matlab (
            T* mat,
            const matrix_exp<EXP>&
        )
        {
            mexErrMsgIdAndTxt("mex_function:validate_and_populate_arg",
                              "mex_function uses unsupported output image argument type");
        }

        template <typename T>
        typename dlib::enable_if<is_matrix<T> >::type assign_to_matlab(
            mxArray*& plhs,
            const T& item
        )
        {
            typedef typename T::type type;

            type* mat = 0;

            if (is_same_type<double, type>::value)
            {
                plhs = mxCreateDoubleMatrix(item.nr(),
                                            item.nc(),
                                            mxREAL);

                mat = (type*)mxGetPr(plhs);
            }
            else if (is_same_type<float, type>::value )
            {
                plhs = mxCreateNumericMatrix(item.nr(),
                                             item.nc(),
                                             mxSINGLE_CLASS,
                                             mxREAL);

                mat = (type*)mxGetData(plhs);
            }
            else if (is_same_type<bool, type>::value )
            {
                plhs = mxCreateLogicalMatrix(item.nr(),
                                             item.nc());

                mat = (type*)mxGetData(plhs);
            }
            else if (is_same_type<dlib::uint8, type>::value )
            {
                plhs = mxCreateNumericMatrix(item.nr(),
                                             item.nc(),
                                             mxUINT8_CLASS,
                                             mxREAL);

                mat = (type*)mxGetData(plhs);
            }
            else if (is_same_type<dlib::int8, type>::value )
            {
                plhs = mxCreateNumericMatrix(item.nr(),
                                             item.nc(),
                                             mxINT8_CLASS,
                                             mxREAL);

                mat = (type*)mxGetData(plhs);
            }
            else if (is_same_type<dlib::int16, type>::value ||
                     (is_same_type<short,type>::value && sizeof(short) == sizeof(dlib::int16)))
            {
                plhs = mxCreateNumericMatrix(item.nr(),
                                             item.nc(),
                                             mxINT16_CLASS,
                                             mxREAL);

                mat = (type*)mxGetData(plhs);
            }
            else if (is_same_type<dlib::uint16, type>::value ||
                     (is_same_type<unsigned short,type>::value && sizeof(unsigned short) == sizeof(dlib::uint16)))
            {
                plhs = mxCreateNumericMatrix(item.nr(),
                                             item.nc(),
                                             mxUINT16_CLASS,
                                             mxREAL);

                mat = (type*)mxGetData(plhs);
            }
            else if (is_same_type<dlib::int32, type>::value ||
                     (is_same_type<long,type>::value && sizeof(long) == sizeof(dlib::int32)))
            {
                plhs = mxCreateNumericMatrix(item.nr(),
                                             item.nc(),
                                             mxINT32_CLASS,
                                             mxREAL);

                mat = (type*)mxGetData(plhs);
            }
            else if (is_same_type<dlib::uint32, type>::value ||
                     (is_same_type<unsigned long,type>::value && sizeof(unsigned long) == sizeof(dlib::uint32)))
            {
                plhs = mxCreateNumericMatrix(item.nr(),
                                             item.nc(),
                                             mxUINT32_CLASS,
                                             mxREAL);

                mat = (type*)mxGetData(plhs);
            }
            else if (is_same_type<dlib::uint64, type>::value ||
                     (is_same_type<unsigned long,type>::value && sizeof(unsigned long) == sizeof(dlib::uint64)))
            {
                plhs = mxCreateNumericMatrix(item.nr(),
                                             item.nc(),
                                             mxUINT64_CLASS,
                                             mxREAL);

                mat = (type*)mxGetData(plhs);
            }
            else if (is_same_type<dlib::int64, type>::value  ||
                     (is_same_type<long,type>::value && sizeof(long) == sizeof(dlib::int64)))
            {
                plhs = mxCreateNumericMatrix(item.nr(),
                                             item.nc(),
                                             mxINT64_CLASS,
                                             mxREAL);

                mat = (type*)mxGetData(plhs);
            }
            else if (is_same_type<dlib::rgb_pixel, type>::value)
            {
                mwSize dims[3] = {item.nr(), item.nc(), 3};
                plhs = mxCreateNumericArray(3, dims, mxUINT8_CLASS, mxREAL);

                assign_image_to_matlab((dlib::uint8*)mxGetData(plhs), item);
                return;
            }
            else
            {
                mexErrMsgIdAndTxt("mex_function:validate_and_populate_arg",
                                  "mex_function uses unsupported output argument type");
            }

            for (long c = 0; c < item.nc(); ++c)
            {
                for ( long  r= 0; r < item.nr(); ++r)
                {
                    *mat++ = item(r,c);
                }
            }
        }

        void assign_to_matlab(
            mxArray*& plhs,
            const std::string& item
        )
        {
            plhs = mxCreateString(item.c_str());
        }

        template <typename T, typename MM>
        void assign_to_matlab(
            mxArray*& plhs,
            const array2d<T,MM>& item
        )
        {
            assign_to_matlab(plhs,array_to_matrix(item));
        }

        template <typename T>
        typename dlib::enable_if<is_array_type<T> >::type assign_to_matlab(
            mxArray*& plhs,
            const T& item
        )
        {
            mwSize dims[1] = {item.size()};
            plhs = mxCreateCellArray(1,dims);
            for (unsigned long i = 0; i < item.size(); ++i)
            {
                mxArray* next = 0;
                assign_to_matlab(next, item[i]);
                mxSetCell(plhs, i, next);
            }
        }

        template <typename T>
        typename dlib::disable_if_c<is_matrix<T>::value || is_array_type<T>::value ||
                                    is_same_type<T,function_handle>::value>::type assign_to_matlab(
            mxArray*& plhs,
            const T& item
        )
        {
            plhs = mxCreateDoubleScalar(item);
        }

        void assign_to_matlab (
            mxArray*& plhs,
            const char* str
        )
        {
            assign_to_matlab(plhs, std::string(str));
        }

        void assign_to_matlab(
            mxArray*& plhs,
            const function_handle& h
        )
        {
        }

    // ----------------------------------------------------------------------------------------

        template <
            unsigned long num_args
            >
        struct call_mex_function_helper;

        template <>
        struct call_mex_function_helper<1>
        {
            template <typename funct>
            void callit(
                const funct& ,
                int nlhs, mxArray *plhs[],
                int nrhs, const mxArray *prhs[]
            ) const
            {
                typedef typename sig_traits<funct>::arg1_type arg1_type;

                typename basic_type<arg1_type>::type A1;

                int i = 0;
                if (i < nrhs && is_input_type<arg1_type>::value) {validate_and_populate_arg(i,prhs[i],A1); ++i;} ELSE_ASSIGN_ARG_1;

                mex_function(A1);

                i = 0;
                if (is_output_type<arg1_type>::value) {assign_to_matlab(plhs[i],A1); ++i;}
            }
        };

        template <>
        struct call_mex_function_helper<2>
        {
            template <typename funct>
            void callit(
                const funct& ,
                int nlhs, mxArray *plhs[],
                int nrhs, const mxArray *prhs[]
            ) const
            {
                typedef typename sig_traits<funct>::arg1_type arg1_type;
                typedef typename sig_traits<funct>::arg2_type arg2_type;

                typename basic_type<arg1_type>::type A1;
                typename basic_type<arg2_type>::type A2;

                int i = 0;
                if (i < nrhs && is_input_type<arg1_type>::value) {validate_and_populate_arg(i,prhs[i],A1); ++i;} ELSE_ASSIGN_ARG_1;
                if (i < nrhs && is_input_type<arg2_type>::value) {validate_and_populate_arg(i,prhs[i],A2); ++i;} ELSE_ASSIGN_ARG_2;

                mex_function(A1,A2);

                i = 0;
                if (is_output_type<arg1_type>::value) {assign_to_matlab(plhs[i],A1); ++i;}
                if (is_output_type<arg2_type>::value) {assign_to_matlab(plhs[i],A2); ++i;}
            }
        };

        template <>
        struct call_mex_function_helper<3>
        {
            template <typename funct>
            void callit(
                const funct& ,
                int nlhs, mxArray *plhs[],
                int nrhs, const mxArray *prhs[]
            ) const
            {
                typedef typename sig_traits<funct>::arg1_type arg1_type;
                typedef typename sig_traits<funct>::arg2_type arg2_type;
                typedef typename sig_traits<funct>::arg3_type arg3_type;

                typename basic_type<arg1_type>::type A1;
                typename basic_type<arg2_type>::type A2;
                typename basic_type<arg3_type>::type A3;

                int i = 0;
                if (i < nrhs && is_input_type<arg1_type>::value) {validate_and_populate_arg(i,prhs[i],A1); ++i;} ELSE_ASSIGN_ARG_1;
                if (i < nrhs && is_input_type<arg2_type>::value) {validate_and_populate_arg(i,prhs[i],A2); ++i;} ELSE_ASSIGN_ARG_2;
                if (i < nrhs && is_input_type<arg3_type>::value) {validate_and_populate_arg(i,prhs[i],A3); ++i;} ELSE_ASSIGN_ARG_3;

                mex_function(A1,A2,A3);

                i = 0;
                if (is_output_type<arg1_type>::value) {assign_to_matlab(plhs[i],A1); ++i;}
                if (is_output_type<arg2_type>::value) {assign_to_matlab(plhs[i],A2); ++i;}
                if (is_output_type<arg3_type>::value) {assign_to_matlab(plhs[i],A3); ++i;}
            }
        };

        template <>
        struct call_mex_function_helper<4>
        {
            template <typename funct>
            void callit(
                const funct& ,
                int nlhs, mxArray *plhs[],
                int nrhs, const mxArray *prhs[]
            ) const
            {
                typedef typename sig_traits<funct>::arg1_type arg1_type;
                typedef typename sig_traits<funct>::arg2_type arg2_type;
                typedef typename sig_traits<funct>::arg3_type arg3_type;
                typedef typename sig_traits<funct>::arg4_type arg4_type;

                typename basic_type<arg1_type>::type A1;
                typename basic_type<arg2_type>::type A2;
                typename basic_type<arg3_type>::type A3;
                typename basic_type<arg4_type>::type A4;

                int i = 0;
                if (i < nrhs && is_input_type<arg1_type>::value) {validate_and_populate_arg(i,prhs[i],A1); ++i;} ELSE_ASSIGN_ARG_1;
                if (i < nrhs && is_input_type<arg2_type>::value) {validate_and_populate_arg(i,prhs[i],A2); ++i;} ELSE_ASSIGN_ARG_2;
                if (i < nrhs && is_input_type<arg3_type>::value) {validate_and_populate_arg(i,prhs[i],A3); ++i;} ELSE_ASSIGN_ARG_3;
                if (i < nrhs && is_input_type<arg4_type>::value) {validate_and_populate_arg(i,prhs[i],A4); ++i;} ELSE_ASSIGN_ARG_4;

                mex_function(A1,A2,A3,A4);

                i = 0;
                if (is_output_type<arg1_type>::value) {assign_to_matlab(plhs[i],A1); ++i;}
                if (is_output_type<arg2_type>::value) {assign_to_matlab(plhs[i],A2); ++i;}
                if (is_output_type<arg3_type>::value) {assign_to_matlab(plhs[i],A3); ++i;}
                if (is_output_type<arg4_type>::value) {assign_to_matlab(plhs[i],A4); ++i;}
            }
        };

        template <>
        struct call_mex_function_helper<5>
        {
            template <typename funct>
            void callit(
                const funct& ,
                int nlhs, mxArray *plhs[],
                int nrhs, const mxArray *prhs[]
            ) const
            {
                typedef typename sig_traits<funct>::arg1_type arg1_type;
                typedef typename sig_traits<funct>::arg2_type arg2_type;
                typedef typename sig_traits<funct>::arg3_type arg3_type;
                typedef typename sig_traits<funct>::arg4_type arg4_type;
                typedef typename sig_traits<funct>::arg5_type arg5_type;

                typename basic_type<arg1_type>::type A1;
                typename basic_type<arg2_type>::type A2;
                typename basic_type<arg3_type>::type A3;
                typename basic_type<arg4_type>::type A4;
                typename basic_type<arg5_type>::type A5;

                int i = 0;
                if (i < nrhs && is_input_type<arg1_type>::value) {validate_and_populate_arg(i,prhs[i],A1); ++i;} ELSE_ASSIGN_ARG_1;
                if (i < nrhs && is_input_type<arg2_type>::value) {validate_and_populate_arg(i,prhs[i],A2); ++i;} ELSE_ASSIGN_ARG_2;
                if (i < nrhs && is_input_type<arg3_type>::value) {validate_and_populate_arg(i,prhs[i],A3); ++i;} ELSE_ASSIGN_ARG_3;
                if (i < nrhs && is_input_type<arg4_type>::value) {validate_and_populate_arg(i,prhs[i],A4); ++i;} ELSE_ASSIGN_ARG_4;
                if (i < nrhs && is_input_type<arg5_type>::value) {validate_and_populate_arg(i,prhs[i],A5); ++i;} ELSE_ASSIGN_ARG_5;

                mex_function(A1,A2,A3,A4,A5);

                i = 0;
                if (is_output_type<arg1_type>::value) {assign_to_matlab(plhs[i],A1); ++i;}
                if (is_output_type<arg2_type>::value) {assign_to_matlab(plhs[i],A2); ++i;}
                if (is_output_type<arg3_type>::value) {assign_to_matlab(plhs[i],A3); ++i;}
                if (is_output_type<arg4_type>::value) {assign_to_matlab(plhs[i],A4); ++i;}
                if (is_output_type<arg5_type>::value) {assign_to_matlab(plhs[i],A5); ++i;}
            }
        };

        template <>
        struct call_mex_function_helper<6>
        {
            template <typename funct>
            void callit(
                const funct& ,
                int nlhs, mxArray *plhs[],
                int nrhs, const mxArray *prhs[]
            ) const
            {
                typedef typename sig_traits<funct>::arg1_type arg1_type;
                typedef typename sig_traits<funct>::arg2_type arg2_type;
                typedef typename sig_traits<funct>::arg3_type arg3_type;
                typedef typename sig_traits<funct>::arg4_type arg4_type;
                typedef typename sig_traits<funct>::arg5_type arg5_type;
                typedef typename sig_traits<funct>::arg6_type arg6_type;

                typename basic_type<arg1_type>::type A1;
                typename basic_type<arg2_type>::type A2;
                typename basic_type<arg3_type>::type A3;
                typename basic_type<arg4_type>::type A4;
                typename basic_type<arg5_type>::type A5;
                typename basic_type<arg6_type>::type A6;

                int i = 0;
                if (i < nrhs && is_input_type<arg1_type>::value) {validate_and_populate_arg(i,prhs[i],A1); ++i;} ELSE_ASSIGN_ARG_1;
                if (i < nrhs && is_input_type<arg2_type>::value) {validate_and_populate_arg(i,prhs[i],A2); ++i;} ELSE_ASSIGN_ARG_2;
                if (i < nrhs && is_input_type<arg3_type>::value) {validate_and_populate_arg(i,prhs[i],A3); ++i;} ELSE_ASSIGN_ARG_3;
                if (i < nrhs && is_input_type<arg4_type>::value) {validate_and_populate_arg(i,prhs[i],A4); ++i;} ELSE_ASSIGN_ARG_4;
                if (i < nrhs && is_input_type<arg5_type>::value) {validate_and_populate_arg(i,prhs[i],A5); ++i;} ELSE_ASSIGN_ARG_5;
                if (i < nrhs && is_input_type<arg6_type>::value) {validate_and_populate_arg(i,prhs[i],A6); ++i;} ELSE_ASSIGN_ARG_6;

                mex_function(A1,A2,A3,A4,A5,A6);

                i = 0;
                if (is_output_type<arg1_type>::value) {assign_to_matlab(plhs[i],A1); ++i;}
                if (is_output_type<arg2_type>::value) {assign_to_matlab(plhs[i],A2); ++i;}
                if (is_output_type<arg3_type>::value) {assign_to_matlab(plhs[i],A3); ++i;}
                if (is_output_type<arg4_type>::value) {assign_to_matlab(plhs[i],A4); ++i;}
                if (is_output_type<arg5_type>::value) {assign_to_matlab(plhs[i],A5); ++i;}
                if (is_output_type<arg6_type>::value) {assign_to_matlab(plhs[i],A6); ++i;}
            }
        };

        template <>
        struct call_mex_function_helper<7>
        {
            template <typename funct>
            void callit(
                const funct& ,
                int nlhs, mxArray *plhs[],
                int nrhs, const mxArray *prhs[]
            ) const
            {
                typedef typename sig_traits<funct>::arg1_type arg1_type;
                typedef typename sig_traits<funct>::arg2_type arg2_type;
                typedef typename sig_traits<funct>::arg3_type arg3_type;
                typedef typename sig_traits<funct>::arg4_type arg4_type;
                typedef typename sig_traits<funct>::arg5_type arg5_type;
                typedef typename sig_traits<funct>::arg6_type arg6_type;
                typedef typename sig_traits<funct>::arg7_type arg7_type;

                typename basic_type<arg1_type>::type A1;
                typename basic_type<arg2_type>::type A2;
                typename basic_type<arg3_type>::type A3;
                typename basic_type<arg4_type>::type A4;
                typename basic_type<arg5_type>::type A5;
                typename basic_type<arg6_type>::type A6;
                typename basic_type<arg7_type>::type A7;

                int i = 0;
                if (i < nrhs && is_input_type<arg1_type>::value) {validate_and_populate_arg(i,prhs[i],A1); ++i;} ELSE_ASSIGN_ARG_1;
                if (i < nrhs && is_input_type<arg2_type>::value) {validate_and_populate_arg(i,prhs[i],A2); ++i;} ELSE_ASSIGN_ARG_2;
                if (i < nrhs && is_input_type<arg3_type>::value) {validate_and_populate_arg(i,prhs[i],A3); ++i;} ELSE_ASSIGN_ARG_3;
                if (i < nrhs && is_input_type<arg4_type>::value) {validate_and_populate_arg(i,prhs[i],A4); ++i;} ELSE_ASSIGN_ARG_4;
                if (i < nrhs && is_input_type<arg5_type>::value) {validate_and_populate_arg(i,prhs[i],A5); ++i;} ELSE_ASSIGN_ARG_5;
                if (i < nrhs && is_input_type<arg6_type>::value) {validate_and_populate_arg(i,prhs[i],A6); ++i;} ELSE_ASSIGN_ARG_6;
                if (i < nrhs && is_input_type<arg7_type>::value) {validate_and_populate_arg(i,prhs[i],A7); ++i;} ELSE_ASSIGN_ARG_7;

                mex_function(A1,A2,A3,A4,A5,A6,A7);

                i = 0;
                if (is_output_type<arg1_type>::value) {assign_to_matlab(plhs[i],A1); ++i;}
                if (is_output_type<arg2_type>::value) {assign_to_matlab(plhs[i],A2); ++i;}
                if (is_output_type<arg3_type>::value) {assign_to_matlab(plhs[i],A3); ++i;}
                if (is_output_type<arg4_type>::value) {assign_to_matlab(plhs[i],A4); ++i;}
                if (is_output_type<arg5_type>::value) {assign_to_matlab(plhs[i],A5); ++i;}
                if (is_output_type<arg6_type>::value) {assign_to_matlab(plhs[i],A6); ++i;}
                if (is_output_type<arg7_type>::value) {assign_to_matlab(plhs[i],A7); ++i;}
            }
        };

        template <>
        struct call_mex_function_helper<8>
        {
            template <typename funct>
            void callit(
                const funct& ,
                int nlhs, mxArray *plhs[],
                int nrhs, const mxArray *prhs[]
            ) const
            {
                typedef typename sig_traits<funct>::arg1_type arg1_type;
                typedef typename sig_traits<funct>::arg2_type arg2_type;
                typedef typename sig_traits<funct>::arg3_type arg3_type;
                typedef typename sig_traits<funct>::arg4_type arg4_type;
                typedef typename sig_traits<funct>::arg5_type arg5_type;
                typedef typename sig_traits<funct>::arg6_type arg6_type;
                typedef typename sig_traits<funct>::arg7_type arg7_type;
                typedef typename sig_traits<funct>::arg8_type arg8_type;

                typename basic_type<arg1_type>::type A1;
                typename basic_type<arg2_type>::type A2;
                typename basic_type<arg3_type>::type A3;
                typename basic_type<arg4_type>::type A4;
                typename basic_type<arg5_type>::type A5;
                typename basic_type<arg6_type>::type A6;
                typename basic_type<arg7_type>::type A7;
                typename basic_type<arg8_type>::type A8;

                int i = 0;
                if (i < nrhs && is_input_type<arg1_type>::value) {validate_and_populate_arg(i,prhs[i],A1); ++i;} ELSE_ASSIGN_ARG_1;
                if (i < nrhs && is_input_type<arg2_type>::value) {validate_and_populate_arg(i,prhs[i],A2); ++i;} ELSE_ASSIGN_ARG_2;
                if (i < nrhs && is_input_type<arg3_type>::value) {validate_and_populate_arg(i,prhs[i],A3); ++i;} ELSE_ASSIGN_ARG_3;
                if (i < nrhs && is_input_type<arg4_type>::value) {validate_and_populate_arg(i,prhs[i],A4); ++i;} ELSE_ASSIGN_ARG_4;
                if (i < nrhs && is_input_type<arg5_type>::value) {validate_and_populate_arg(i,prhs[i],A5); ++i;} ELSE_ASSIGN_ARG_5;
                if (i < nrhs && is_input_type<arg6_type>::value) {validate_and_populate_arg(i,prhs[i],A6); ++i;} ELSE_ASSIGN_ARG_6;
                if (i < nrhs && is_input_type<arg7_type>::value) {validate_and_populate_arg(i,prhs[i],A7); ++i;} ELSE_ASSIGN_ARG_7;
                if (i < nrhs && is_input_type<arg8_type>::value) {validate_and_populate_arg(i,prhs[i],A8); ++i;} ELSE_ASSIGN_ARG_8;

                mex_function(A1,A2,A3,A4,A5,A6,A7,A8);

                i = 0;
                if (is_output_type<arg1_type>::value) {assign_to_matlab(plhs[i],A1); ++i;}
                if (is_output_type<arg2_type>::value) {assign_to_matlab(plhs[i],A2); ++i;}
                if (is_output_type<arg3_type>::value) {assign_to_matlab(plhs[i],A3); ++i;}
                if (is_output_type<arg4_type>::value) {assign_to_matlab(plhs[i],A4); ++i;}
                if (is_output_type<arg5_type>::value) {assign_to_matlab(plhs[i],A5); ++i;}
                if (is_output_type<arg6_type>::value) {assign_to_matlab(plhs[i],A6); ++i;}
                if (is_output_type<arg7_type>::value) {assign_to_matlab(plhs[i],A7); ++i;}
                if (is_output_type<arg8_type>::value) {assign_to_matlab(plhs[i],A8); ++i;}
            }
        };

        template <>
        struct call_mex_function_helper<9>
        {
            template <typename funct>
            void callit(
                const funct& ,
                int nlhs, mxArray *plhs[],
                int nrhs, const mxArray *prhs[]
            ) const
            {
                typedef typename sig_traits<funct>::arg1_type arg1_type;
                typedef typename sig_traits<funct>::arg2_type arg2_type;
                typedef typename sig_traits<funct>::arg3_type arg3_type;
                typedef typename sig_traits<funct>::arg4_type arg4_type;
                typedef typename sig_traits<funct>::arg5_type arg5_type;
                typedef typename sig_traits<funct>::arg6_type arg6_type;
                typedef typename sig_traits<funct>::arg7_type arg7_type;
                typedef typename sig_traits<funct>::arg8_type arg8_type;
                typedef typename sig_traits<funct>::arg9_type arg9_type;

                typename basic_type<arg1_type>::type A1;
                typename basic_type<arg2_type>::type A2;
                typename basic_type<arg3_type>::type A3;
                typename basic_type<arg4_type>::type A4;
                typename basic_type<arg5_type>::type A5;
                typename basic_type<arg6_type>::type A6;
                typename basic_type<arg7_type>::type A7;
                typename basic_type<arg8_type>::type A8;
                typename basic_type<arg9_type>::type A9;

                int i = 0;
                if (i < nrhs && is_input_type<arg1_type>::value) {validate_and_populate_arg(i,prhs[i],A1); ++i;} ELSE_ASSIGN_ARG_1;
                if (i < nrhs && is_input_type<arg2_type>::value) {validate_and_populate_arg(i,prhs[i],A2); ++i;} ELSE_ASSIGN_ARG_2;
                if (i < nrhs && is_input_type<arg3_type>::value) {validate_and_populate_arg(i,prhs[i],A3); ++i;} ELSE_ASSIGN_ARG_3;
                if (i < nrhs && is_input_type<arg4_type>::value) {validate_and_populate_arg(i,prhs[i],A4); ++i;} ELSE_ASSIGN_ARG_4;
                if (i < nrhs && is_input_type<arg5_type>::value) {validate_and_populate_arg(i,prhs[i],A5); ++i;} ELSE_ASSIGN_ARG_5;
                if (i < nrhs && is_input_type<arg6_type>::value) {validate_and_populate_arg(i,prhs[i],A6); ++i;} ELSE_ASSIGN_ARG_6;
                if (i < nrhs && is_input_type<arg7_type>::value) {validate_and_populate_arg(i,prhs[i],A7); ++i;} ELSE_ASSIGN_ARG_7;
                if (i < nrhs && is_input_type<arg8_type>::value) {validate_and_populate_arg(i,prhs[i],A8); ++i;} ELSE_ASSIGN_ARG_8;
                if (i < nrhs && is_input_type<arg9_type>::value) {validate_and_populate_arg(i,prhs[i],A9); ++i;} ELSE_ASSIGN_ARG_9;

                mex_function(A1,A2,A3,A4,A5,A6,A7,A8,A9);

                i = 0;
                if (is_output_type<arg1_type>::value) {assign_to_matlab(plhs[i],A1); ++i;}
                if (is_output_type<arg2_type>::value) {assign_to_matlab(plhs[i],A2); ++i;}
                if (is_output_type<arg3_type>::value) {assign_to_matlab(plhs[i],A3); ++i;}
                if (is_output_type<arg4_type>::value) {assign_to_matlab(plhs[i],A4); ++i;}
                if (is_output_type<arg5_type>::value) {assign_to_matlab(plhs[i],A5); ++i;}
                if (is_output_type<arg6_type>::value) {assign_to_matlab(plhs[i],A6); ++i;}
                if (is_output_type<arg7_type>::value) {assign_to_matlab(plhs[i],A7); ++i;}
                if (is_output_type<arg8_type>::value) {assign_to_matlab(plhs[i],A8); ++i;}
                if (is_output_type<arg9_type>::value) {assign_to_matlab(plhs[i],A9); ++i;}
            }
        };

        template <>
        struct call_mex_function_helper<10>
        {
            template <typename funct>
            void callit(
                const funct& ,
                int nlhs, mxArray *plhs[],
                int nrhs, const mxArray *prhs[]
            ) const
            {
                typedef typename sig_traits<funct>::arg1_type arg1_type;
                typedef typename sig_traits<funct>::arg2_type arg2_type;
                typedef typename sig_traits<funct>::arg3_type arg3_type;
                typedef typename sig_traits<funct>::arg4_type arg4_type;
                typedef typename sig_traits<funct>::arg5_type arg5_type;
                typedef typename sig_traits<funct>::arg6_type arg6_type;
                typedef typename sig_traits<funct>::arg7_type arg7_type;
                typedef typename sig_traits<funct>::arg8_type arg8_type;
                typedef typename sig_traits<funct>::arg9_type arg9_type;
                typedef typename sig_traits<funct>::arg10_type arg10_type;

                typename basic_type<arg1_type>::type A1;
                typename basic_type<arg2_type>::type A2;
                typename basic_type<arg3_type>::type A3;
                typename basic_type<arg4_type>::type A4;
                typename basic_type<arg5_type>::type A5;
                typename basic_type<arg6_type>::type A6;
                typename basic_type<arg7_type>::type A7;
                typename basic_type<arg8_type>::type A8;
                typename basic_type<arg9_type>::type A9;
                typename basic_type<arg10_type>::type A10;

                int i = 0;
                if (i < nrhs && is_input_type<arg1_type>::value) {validate_and_populate_arg(i,prhs[i],A1); ++i;} ELSE_ASSIGN_ARG_1;
                if (i < nrhs && is_input_type<arg2_type>::value) {validate_and_populate_arg(i,prhs[i],A2); ++i;} ELSE_ASSIGN_ARG_2;
                if (i < nrhs && is_input_type<arg3_type>::value) {validate_and_populate_arg(i,prhs[i],A3); ++i;} ELSE_ASSIGN_ARG_3;
                if (i < nrhs && is_input_type<arg4_type>::value) {validate_and_populate_arg(i,prhs[i],A4); ++i;} ELSE_ASSIGN_ARG_4;
                if (i < nrhs && is_input_type<arg5_type>::value) {validate_and_populate_arg(i,prhs[i],A5); ++i;} ELSE_ASSIGN_ARG_5;
                if (i < nrhs && is_input_type<arg6_type>::value) {validate_and_populate_arg(i,prhs[i],A6); ++i;} ELSE_ASSIGN_ARG_6;
                if (i < nrhs && is_input_type<arg7_type>::value) {validate_and_populate_arg(i,prhs[i],A7); ++i;} ELSE_ASSIGN_ARG_7;
                if (i < nrhs && is_input_type<arg8_type>::value) {validate_and_populate_arg(i,prhs[i],A8); ++i;} ELSE_ASSIGN_ARG_8;
                if (i < nrhs && is_input_type<arg9_type>::value) {validate_and_populate_arg(i,prhs[i],A9); ++i;} ELSE_ASSIGN_ARG_9;
                if (i < nrhs && is_input_type<arg10_type>::value) {validate_and_populate_arg(i,prhs[i],A10); ++i;} ELSE_ASSIGN_ARG_10;

                mex_function(A1,A2,A3,A4,A5,A6,A7,A8,A9,A10);

                i = 0;
                if (is_output_type<arg1_type>::value) {assign_to_matlab(plhs[i],A1); ++i;}
                if (is_output_type<arg2_type>::value) {assign_to_matlab(plhs[i],A2); ++i;}
                if (is_output_type<arg3_type>::value) {assign_to_matlab(plhs[i],A3); ++i;}
                if (is_output_type<arg4_type>::value) {assign_to_matlab(plhs[i],A4); ++i;}
                if (is_output_type<arg5_type>::value) {assign_to_matlab(plhs[i],A5); ++i;}
                if (is_output_type<arg6_type>::value) {assign_to_matlab(plhs[i],A6); ++i;}
                if (is_output_type<arg7_type>::value) {assign_to_matlab(plhs[i],A7); ++i;}
                if (is_output_type<arg8_type>::value) {assign_to_matlab(plhs[i],A8); ++i;}
                if (is_output_type<arg9_type>::value) {assign_to_matlab(plhs[i],A9); ++i;}
                if (is_output_type<arg10_type>::value) {assign_to_matlab(plhs[i],A10); ++i;}
            }
        };

    // ----------------------------------------------------------------------------------------

        template <
            typename funct
            >
        void call_mex_function (
            const funct& f,
            int nlhs, mxArray *plhs[],
            int nrhs, const mxArray *prhs[]
        )
        {
            const long expected_nrhs = funct_traits<funct>::num_inputs;
            const long expected_nlhs = funct_traits<funct>::num_outputs;
            const long expected_args = expected_nrhs + expected_nlhs;

            long defaulted_args = 0;

            #ifdef ARG_1_DEFAULT
                ++defaulted_args;
                // You can only set an argument's default value if it is an input argument.
                COMPILE_TIME_ASSERT(is_input_type<typename sig_traits<funct>::arg1_type>::value);
                #ifndef ARG_2_DEFAULT
                    // You can't define a default for argument 1 if you don't define one for argument 2 also.
                    COMPILE_TIME_ASSERT(expected_args < 2);
                #endif
                COMPILE_TIME_ASSERT(1 <= expected_args); // You can't define a default for an argument that doesn't exist.
            #endif
            #ifdef ARG_2_DEFAULT
                ++defaulted_args;
                // You can only set an argument's default value if it is an input argument.
                COMPILE_TIME_ASSERT(is_input_type<typename sig_traits<funct>::arg2_type>::value);
                #ifndef ARG_3_DEFAULT
                    // You can't define a default for argument 2 if you don't define one for argument 3 also.
                    COMPILE_TIME_ASSERT(expected_args < 3);
                #endif
                COMPILE_TIME_ASSERT(2 <= expected_args); // You can't define a default for an argument that doesn't exist.
            #endif
            #ifdef ARG_3_DEFAULT
                ++defaulted_args;
                // You can only set an argument's default value if it is an input argument.
                COMPILE_TIME_ASSERT(is_input_type<typename sig_traits<funct>::arg3_type>::value);
                #ifndef ARG_4_DEFAULT
                    // You can't define a default for argument 3 if you don't define one for argument 4 also.
                    COMPILE_TIME_ASSERT(expected_args < 4);
                #endif
                COMPILE_TIME_ASSERT(3 <= expected_args); // You can't define a default for an argument that doesn't exist.
            #endif
            #ifdef ARG_4_DEFAULT
                ++defaulted_args;
                // You can only set an argument's default value if it is an input argument.
                COMPILE_TIME_ASSERT(is_input_type<typename sig_traits<funct>::arg4_type>::value);
                #ifndef ARG_5_DEFAULT
                    // You can't define a default for argument 4 if you don't define one for argument 5 also.
                    COMPILE_TIME_ASSERT(expected_args < 5);
                #endif
                COMPILE_TIME_ASSERT(4 <= expected_args); // You can't define a default for an argument that doesn't exist.
            #endif
            #ifdef ARG_5_DEFAULT
                ++defaulted_args;
                // You can only set an argument's default value if it is an input argument.
                COMPILE_TIME_ASSERT(is_input_type<typename sig_traits<funct>::arg5_type>::value);
                #ifndef ARG_6_DEFAULT
                    // You can't define a default for argument 5 if you don't define one for argument 6 also.
                    COMPILE_TIME_ASSERT(expected_args < 6);
                #endif
                COMPILE_TIME_ASSERT(5 <= expected_args); // You can't define a default for an argument that doesn't exist.
            #endif
            #ifdef ARG_6_DEFAULT
                ++defaulted_args;
                // You can only set an argument's default value if it is an input argument.
                COMPILE_TIME_ASSERT(is_input_type<typename sig_traits<funct>::arg6_type>::value);
                #ifndef ARG_7_DEFAULT
                    // You can't define a default for argument 6 if you don't define one for argument 7 also.
                    COMPILE_TIME_ASSERT(expected_args < 7);
                #endif
                COMPILE_TIME_ASSERT(6 <= expected_args); // You can't define a default for an argument that doesn't exist.
            #endif
            #ifdef ARG_7_DEFAULT
                ++defaulted_args;
                // You can only set an argument's default value if it is an input argument.
                COMPILE_TIME_ASSERT(is_input_type<typename sig_traits<funct>::arg7_type>::value);
                #ifndef ARG_8_DEFAULT
                    // You can't define a default for argument 7 if you don't define one for argument 8 also.
                    COMPILE_TIME_ASSERT(expected_args < 8);
                #endif
                COMPILE_TIME_ASSERT(7 <= expected_args); // You can't define a default for an argument that doesn't exist.
            #endif
            #ifdef ARG_8_DEFAULT
                ++defaulted_args;
                // You can only set an argument's default value if it is an input argument.
                COMPILE_TIME_ASSERT(is_input_type<typename sig_traits<funct>::arg8_type>::value);
                #ifndef ARG_9_DEFAULT
                    // You can't define a default for argument 8 if you don't define one for argument 9 also.
                    COMPILE_TIME_ASSERT(expected_args < 9);
                #endif
                COMPILE_TIME_ASSERT(8 <= expected_args); // You can't define a default for an argument that doesn't exist.
            #endif
            #ifdef ARG_9_DEFAULT
                ++defaulted_args;
                // You can only set an argument's default value if it is an input argument.
                COMPILE_TIME_ASSERT(is_input_type<typename sig_traits<funct>::arg9_type>::value);
                #ifndef ARG_10_DEFAULT
                    // You can't define a default for argument 9 if you don't define one for argument 10 also.
                    COMPILE_TIME_ASSERT(expected_args < 10);
                #endif
                COMPILE_TIME_ASSERT(9 <= expected_args); // You can't define a default for an argument that doesn't exist.
            #endif
            #ifdef ARG_10_DEFAULT
                ++defaulted_args;
                // You can only set an argument's default value if it is an input argument.
                COMPILE_TIME_ASSERT(is_input_type<typename sig_traits<funct>::arg10_type>::value);
                COMPILE_TIME_ASSERT(10 <= expected_args); // You can't define a default for an argument that doesn't exist.
            #endif

            /* check for proper number of arguments */
            if(nrhs > expected_nrhs || nrhs < expected_nrhs - defaulted_args)
            {
                std::ostringstream sout;
                sout << "Expected between " << expected_nrhs-defaulted_args
                    << " and " << expected_nrhs << " input arguments, got " << nrhs << ".";

                mexErrMsgIdAndTxt("mex_function:nrhs",
                                  sout.str().c_str());
            }

            if (nlhs > expected_nlhs)
            {
                std::ostringstream sout;
                sout << "Expected at most " << expected_nlhs << " output arguments, got " << nlhs << ".";

                mexErrMsgIdAndTxt("mex_function:nlhs",
                                  sout.str().c_str());
            }

            try
            {
                call_mex_function_helper<sig_traits<funct>::num_args> helper;
                helper.callit(f, nlhs, plhs, nrhs, prhs);
            }
            catch (invalid_args_exception& e)
            {
                mexErrMsgIdAndTxt("mex_function:validate_and_populate_arg",
                                  ("Input" + e.msg).c_str());
            }
            catch (dlib::error& e)
            {
                mexErrMsgIdAndTxt("mex_function:error",
                                  e.what());
            }

        }

    // ----------------------------------------------------------------------------------------

        class mex_streambuf : public std::streambuf
        {

        public:
            mex_streambuf (
            )
            {
                buf.resize(1000);
                setp(&buf[0], &buf[0] + buf.size()-2);

                // make cout send data to mex_streambuf
                std::cout.rdbuf(this);
            }

        protected:

            int sync (
            )
            {
                int num = static_cast<int>(pptr()-pbase());
                if (num != 0)
                {
                    buf[num] = 0; // null terminate the string
                    mexPrintf("%s",&buf[0]);
                    mexEvalString("drawnow"); // flush print to screen
                    pbump(-num);
                }
                return 0;
            }

            int_type overflow (
                int_type c
            )
            {
                if (c != EOF)
                {
                    *pptr() = c;
                    pbump(1);
                }
                sync();
                return c;
            }

        private:
            std::vector<char> buf;

        };

    // ----------------------------------------------------------------------------------------
    // ----------------------------------------------------------------------------------------

        template <typename T>
        void setup_input_args (
            mxArray*& array,
            const T& item,
            int& nrhs
        )
        {
            assign_to_matlab(array, item);
            ++nrhs;
        }

        void setup_input_args (
            mxArray*& array,
            const function_handle& item,
            int& nrhs
        )
        {
            array = static_cast<mxArray*>(item.h);
            ++nrhs;
        }

        template <typename T>
        void setup_input_args (
            mxArray*& array,
            const output_decorator<T>& item,
            int& nrhs
        )
        {
        }

        template <typename T>
        void setup_output_args (
            const std::string& function_name,
            mxArray* array,
            const T& item,
            int& nrhs
        )
        {
        }

        template <typename T>
        void setup_output_args (
            const std::string& function_name,
            mxArray* array,
            const output_decorator<T>& item,
            int& i
        )
        {
            try
            {
                validate_and_populate_arg(i,array,const_cast<T&>(item.item));
                ++i;
            }
            catch (invalid_args_exception& e)
            {
                throw dlib::error("Error occurred calling MATLAB function '" + function_name + "' from mex file. \n"
                                  "The MATLAB function didn't return what we expected it to.  \nIn particular, return" + e.msg);
            }
        }

        void call_matlab_for_real (
            int nlhs,
            mxArray* plhs[],
            int nrhs,
            mxArray* prhs[],
            const std::string& function_name
        )
        {
            int status = mexCallMATLAB(nlhs, plhs, nrhs, prhs, function_name.c_str());
            if (status)
            {
                throw dlib::error("Error, an exception was thrown when we tried to call the MATLAB function '" + function_name + "'.");
            }
        }

    // ----------------------------------------------------------------------------------------
    // ----------------------------------------------------------------------------------------

    }

    // ----------------------------------------------------------------------------------------
    // ----------------------------------------------------------------------------------------

    void call_matlab (
        const std::string& function_name
    )
    {
        using namespace mex_binding;

        call_matlab_for_real(0,NULL,0,NULL, function_name);
    }

    template <typename T1>
    void free_callback_resources (
        int nlhs,
        mxArray* plhs[],
        int nrhs,
        mxArray* prhs[]
    )
    {
        // free resources
        for (int i = 0; i < nlhs; ++i)
            mxDestroyArray(plhs[i]);

        for (int i = 0; i < nrhs; ++i)
        {
            // don't call mxDestroyArray() on function handles (which should only ever be in prhs[0])
            if (i == 0 && dlib::is_same_type<T1,function_handle>::value)
                continue;
            mxDestroyArray(prhs[i]);
        }
    }

    template <
        typename T1
        >
    void call_matlab (
        const std::string& function_name,
        const T1& A1
    )
    {
        using namespace mex_binding;
        const int num_args = 1;
        mxArray* plhs[num_args] = {0};
        mxArray* prhs[num_args] = {0};

        int nrhs = 0;
        setup_input_args(prhs[nrhs], A1, nrhs);

        const int nlhs = num_args - nrhs;
        call_matlab_for_real(nlhs,plhs,nrhs,prhs, function_name);

        int i = 0;
        setup_output_args(function_name, plhs[i], A1, i);

        free_callback_resources<T1>(nlhs,plhs,nrhs,prhs);
    }

    template <
        typename T1,
        typename T2
        >
    void call_matlab (
        const std::string& function_name,
        const T1& A1,
        const T2& A2
    )
    {
        using namespace mex_binding;
        const int num_args = 2;
        mxArray* plhs[num_args] = {0};
        mxArray* prhs[num_args] = {0};

        int nrhs = 0;
        setup_input_args(prhs[nrhs], A1, nrhs);
        setup_input_args(prhs[nrhs], A2, nrhs);

        const int nlhs = num_args - nrhs;
        call_matlab_for_real(nlhs,plhs,nrhs,prhs, function_name);

        int i = 0;
        setup_output_args(function_name, plhs[i], A1, i);
        setup_output_args(function_name, plhs[i], A2, i);

        free_callback_resources<T1>(nlhs,plhs,nrhs,prhs);
    }

    template <
        typename T1,
        typename T2,
        typename T3
        >
    void call_matlab (
        const std::string& function_name,
        const T1& A1,
        const T2& A2,
        const T3& A3
    )
    {
        using namespace mex_binding;
        const int num_args = 3;
        mxArray* plhs[num_args] = {0};
        mxArray* prhs[num_args] = {0};

        int nrhs = 0;
        setup_input_args(prhs[nrhs], A1, nrhs);
        setup_input_args(prhs[nrhs], A2, nrhs);
        setup_input_args(prhs[nrhs], A3, nrhs);

        const int nlhs = num_args - nrhs;
        call_matlab_for_real(nlhs,plhs,nrhs,prhs, function_name);

        int i = 0;
        setup_output_args(function_name, plhs[i], A1, i);
        setup_output_args(function_name, plhs[i], A2, i);
        setup_output_args(function_name, plhs[i], A3, i);

        free_callback_resources<T1>(nlhs,plhs,nrhs,prhs);
    }

    template <
        typename T1,
        typename T2,
        typename T3,
        typename T4
        >
    void call_matlab (
        const std::string& function_name,
        const T1& A1,
        const T2& A2,
        const T3& A3,
        const T4& A4
    )
    {
        using namespace mex_binding;
        const int num_args = 4;
        mxArray* plhs[num_args] = {0};
        mxArray* prhs[num_args] = {0};

        int nrhs = 0;
        setup_input_args(prhs[nrhs], A1, nrhs);
        setup_input_args(prhs[nrhs], A2, nrhs);
        setup_input_args(prhs[nrhs], A3, nrhs);
        setup_input_args(prhs[nrhs], A4, nrhs);

        const int nlhs = num_args - nrhs;
        call_matlab_for_real(nlhs,plhs,nrhs,prhs, function_name);

        int i = 0;
        setup_output_args(function_name, plhs[i], A1, i);
        setup_output_args(function_name, plhs[i], A2, i);
        setup_output_args(function_name, plhs[i], A3, i);
        setup_output_args(function_name, plhs[i], A4, i);

        free_callback_resources<T1>(nlhs,plhs,nrhs,prhs);
    }

    template <
        typename T1,
        typename T2,
        typename T3,
        typename T4,
        typename T5
        >
    void call_matlab (
        const std::string& function_name,
        const T1& A1,
        const T2& A2,
        const T3& A3,
        const T4& A4,
        const T5& A5
    )
    {
        using namespace mex_binding;
        const int num_args = 5;
        mxArray* plhs[num_args] = {0};
        mxArray* prhs[num_args] = {0};

        int nrhs = 0;
        setup_input_args(prhs[nrhs], A1, nrhs);
        setup_input_args(prhs[nrhs], A2, nrhs);
        setup_input_args(prhs[nrhs], A3, nrhs);
        setup_input_args(prhs[nrhs], A4, nrhs);
        setup_input_args(prhs[nrhs], A5, nrhs);

        const int nlhs = num_args - nrhs;
        call_matlab_for_real(nlhs,plhs,nrhs,prhs, function_name);

        int i = 0;
        setup_output_args(function_name, plhs[i], A1, i);
        setup_output_args(function_name, plhs[i], A2, i);
        setup_output_args(function_name, plhs[i], A3, i);
        setup_output_args(function_name, plhs[i], A4, i);
        setup_output_args(function_name, plhs[i], A5, i);

        free_callback_resources<T1>(nlhs,plhs,nrhs,prhs);
    }

    template <
        typename T1,
        typename T2,
        typename T3,
        typename T4,
        typename T5,
        typename T6
        >
    void call_matlab (
        const std::string& function_name,
        const T1& A1,
        const T2& A2,
        const T3& A3,
        const T4& A4,
        const T5& A5,
        const T6& A6
    )
    {
        using namespace mex_binding;
        const int num_args = 6;
        mxArray* plhs[num_args] = {0};
        mxArray* prhs[num_args] = {0};

        int nrhs = 0;
        setup_input_args(prhs[nrhs], A1, nrhs);
        setup_input_args(prhs[nrhs], A2, nrhs);
        setup_input_args(prhs[nrhs], A3, nrhs);
        setup_input_args(prhs[nrhs], A4, nrhs);
        setup_input_args(prhs[nrhs], A5, nrhs);
        setup_input_args(prhs[nrhs], A6, nrhs);

        const int nlhs = num_args - nrhs;
        call_matlab_for_real(nlhs,plhs,nrhs,prhs, function_name);

        int i = 0;
        setup_output_args(function_name, plhs[i], A1, i);
        setup_output_args(function_name, plhs[i], A2, i);
        setup_output_args(function_name, plhs[i], A3, i);
        setup_output_args(function_name, plhs[i], A4, i);
        setup_output_args(function_name, plhs[i], A5, i);
        setup_output_args(function_name, plhs[i], A6, i);

        free_callback_resources<T1>(nlhs,plhs,nrhs,prhs);
    }

    template <
        typename T1,
        typename T2,
        typename T3,
        typename T4,
        typename T5,
        typename T6,
        typename T7
        >
    void call_matlab (
        const std::string& function_name,
        const T1& A1,
        const T2& A2,
        const T3& A3,
        const T4& A4,
        const T5& A5,
        const T6& A6,
        const T7& A7
    )
    {
        using namespace mex_binding;
        const int num_args = 7;
        mxArray* plhs[num_args] = {0};
        mxArray* prhs[num_args] = {0};

        int nrhs = 0;
        setup_input_args(prhs[nrhs], A1, nrhs);
        setup_input_args(prhs[nrhs], A2, nrhs);
        setup_input_args(prhs[nrhs], A3, nrhs);
        setup_input_args(prhs[nrhs], A4, nrhs);
        setup_input_args(prhs[nrhs], A5, nrhs);
        setup_input_args(prhs[nrhs], A6, nrhs);
        setup_input_args(prhs[nrhs], A7, nrhs);

        const int nlhs = num_args - nrhs;
        call_matlab_for_real(nlhs,plhs,nrhs,prhs, function_name);

        int i = 0;
        setup_output_args(function_name, plhs[i], A1, i);
        setup_output_args(function_name, plhs[i], A2, i);
        setup_output_args(function_name, plhs[i], A3, i);
        setup_output_args(function_name, plhs[i], A4, i);
        setup_output_args(function_name, plhs[i], A5, i);
        setup_output_args(function_name, plhs[i], A6, i);
        setup_output_args(function_name, plhs[i], A7, i);

        free_callback_resources<T1>(nlhs,plhs,nrhs,prhs);
    }

    template <
        typename T1,
        typename T2,
        typename T3,
        typename T4,
        typename T5,
        typename T6,
        typename T7,
        typename T8
        >
    void call_matlab (
        const std::string& function_name,
        const T1& A1,
        const T2& A2,
        const T3& A3,
        const T4& A4,
        const T5& A5,
        const T6& A6,
        const T7& A7,
        const T8& A8
    )
    {
        using namespace mex_binding;
        const int num_args = 8;
        mxArray* plhs[num_args] = {0};
        mxArray* prhs[num_args] = {0};

        int nrhs = 0;
        setup_input_args(prhs[nrhs], A1, nrhs);
        setup_input_args(prhs[nrhs], A2, nrhs);
        setup_input_args(prhs[nrhs], A3, nrhs);
        setup_input_args(prhs[nrhs], A4, nrhs);
        setup_input_args(prhs[nrhs], A5, nrhs);
        setup_input_args(prhs[nrhs], A6, nrhs);
        setup_input_args(prhs[nrhs], A7, nrhs);
        setup_input_args(prhs[nrhs], A8, nrhs);

        const int nlhs = num_args - nrhs;
        call_matlab_for_real(nlhs,plhs,nrhs,prhs, function_name);

        int i = 0;
        setup_output_args(function_name, plhs[i], A1, i);
        setup_output_args(function_name, plhs[i], A2, i);
        setup_output_args(function_name, plhs[i], A3, i);
        setup_output_args(function_name, plhs[i], A4, i);
        setup_output_args(function_name, plhs[i], A5, i);
        setup_output_args(function_name, plhs[i], A6, i);
        setup_output_args(function_name, plhs[i], A7, i);
        setup_output_args(function_name, plhs[i], A8, i);

        free_callback_resources<T1>(nlhs,plhs,nrhs,prhs);
    }

    template <
        typename T1,
        typename T2,
        typename T3,
        typename T4,
        typename T5,
        typename T6,
        typename T7,
        typename T8,
        typename T9
        >
    void call_matlab (
        const std::string& function_name,
        const T1& A1,
        const T2& A2,
        const T3& A3,
        const T4& A4,
        const T5& A5,
        const T6& A6,
        const T7& A7,
        const T8& A8,
        const T9& A9
    )
    {
        using namespace mex_binding;
        const int num_args = 9;
        mxArray* plhs[num_args] = {0};
        mxArray* prhs[num_args] = {0};

        int nrhs = 0;
        setup_input_args(prhs[nrhs], A1, nrhs);
        setup_input_args(prhs[nrhs], A2, nrhs);
        setup_input_args(prhs[nrhs], A3, nrhs);
        setup_input_args(prhs[nrhs], A4, nrhs);
        setup_input_args(prhs[nrhs], A5, nrhs);
        setup_input_args(prhs[nrhs], A6, nrhs);
        setup_input_args(prhs[nrhs], A7, nrhs);
        setup_input_args(prhs[nrhs], A8, nrhs);
        setup_input_args(prhs[nrhs], A9, nrhs);

        const int nlhs = num_args - nrhs;
        call_matlab_for_real(nlhs,plhs,nrhs,prhs, function_name);

        int i = 0;
        setup_output_args(function_name, plhs[i], A1, i);
        setup_output_args(function_name, plhs[i], A2, i);
        setup_output_args(function_name, plhs[i], A3, i);
        setup_output_args(function_name, plhs[i], A4, i);
        setup_output_args(function_name, plhs[i], A5, i);
        setup_output_args(function_name, plhs[i], A6, i);
        setup_output_args(function_name, plhs[i], A7, i);
        setup_output_args(function_name, plhs[i], A8, i);
        setup_output_args(function_name, plhs[i], A9, i);

        free_callback_resources<T1>(nlhs,plhs,nrhs,prhs);
    }

    template <
        typename T1,
        typename T2,
        typename T3,
        typename T4,
        typename T5,
        typename T6,
        typename T7,
        typename T8,
        typename T9,
        typename T10
        >
    void call_matlab (
        const std::string& function_name,
        const T1& A1,
        const T2& A2,
        const T3& A3,
        const T4& A4,
        const T5& A5,
        const T6& A6,
        const T7& A7,
        const T8& A8,
        const T9& A9,
        const T10& A10
    )
    {
        using namespace mex_binding;
        const int num_args = 10;
        mxArray* plhs[num_args] = {0};
        mxArray* prhs[num_args] = {0};

        int nrhs = 0;
        setup_input_args(prhs[nrhs], A1, nrhs);
        setup_input_args(prhs[nrhs], A2, nrhs);
        setup_input_args(prhs[nrhs], A3, nrhs);
        setup_input_args(prhs[nrhs], A4, nrhs);
        setup_input_args(prhs[nrhs], A5, nrhs);
        setup_input_args(prhs[nrhs], A6, nrhs);
        setup_input_args(prhs[nrhs], A7, nrhs);
        setup_input_args(prhs[nrhs], A8, nrhs);
        setup_input_args(prhs[nrhs], A10, nrhs);

        const int nlhs = num_args - nrhs;
        call_matlab_for_real(nlhs,plhs,nrhs,prhs, function_name);

        int i = 0;
        setup_output_args(function_name, plhs[i], A1, i);
        setup_output_args(function_name, plhs[i], A2, i);
        setup_output_args(function_name, plhs[i], A3, i);
        setup_output_args(function_name, plhs[i], A4, i);
        setup_output_args(function_name, plhs[i], A5, i);
        setup_output_args(function_name, plhs[i], A6, i);
        setup_output_args(function_name, plhs[i], A7, i);
        setup_output_args(function_name, plhs[i], A8, i);
        setup_output_args(function_name, plhs[i], A10, i);

        free_callback_resources<T1>(nlhs,plhs,nrhs,prhs);
    }

    // ----------------------------------------------------------------------------------------
    // ----------------------------------------------------------------------------------------

    void call_matlab (
        const function_handle& funct
    )
    {
        call_matlab("feval", funct);
    }

    // ----------------------------------------------------------------------------------------
    // ----------------------------------------------------------------------------------------

    /* The gateway function called by MATLAB*/
    void mexFunction( int nlhs, mxArray *plhs[],
                      int nrhs, const mxArray *prhs[])
    {
        // Only remap cout if we aren't using octave since octave already does this.
    #if !defined(OCTAVE_IMPORT) && !defined(OCTAVE_API)
        // make it so cout prints to mexPrintf()
        static mex_binding::mex_streambuf sb;
    #endif

        mex_binding::call_mex_function(mex_function, nlhs, plhs, nrhs, prhs);
    }

    // ----------------------------------------------------------------------------------------