Search code examples
c++matlabmexeigen3

pass data between C++ Eigen Matrix and MATLAB mxArray with a cell array


I would like to pass data between Eigen Matrix/Vector and mex arrays. In the following code, I defined a mex array called y_output, which contains a cell array. The variable y_output will be passed to MATLAB. Each element in y_output is a vector but with different lengths. I would like to pass a pointer that points to Eigen vectors to the mex array y_output.

Notice that the data stored in y will be modified with a user-defined function. After calling the function, I would assume that the data stored in y_output will be modified corresponding. However, I cannot directly pass the pointer from y_output to y. Is there any way to make it possible? Thanks!

This question is similar but different from the one at Pass C++ Eigen matrix to Matlab mex output. This question is asking how to pass an array of matrices, while the question in that link is asking how to pass a matrix.

#include "mex.h"
#include "matrix.h"
#include <Eigen>


void mexFunction(int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[])
{


// prhs[0]: a cell array of length T, each element is a vector with different lengths
mwSize T =  mxGetNumberOfElements(prhs[0]);
mwSize* n = new mwSize[T];
Eigen::VectorXd* z = new Eigen::VectorXd[T];

for(int t=0; t<T; t++){
    n[t] = mxGetNumberOfElements(mxGetCell(prhs[0], t));
    z[t] = Eigen::Map<Eigen::VectorXd>(mxGetPr(mxGetCell(prhs[0], t)), n[t]);
}

// create a cell matrix with T rows and one columns
mxArray* y_output = mxCreateCellMatrix(T,1);

// create corresponding Eigen objects
Eigen::VectorXd* y = new Eigen::VectorXd[T]();

Eigen::VectorXd y_temp(n[0]); y_temp.setZero();

for(int t=0; t<T; t++){
    mxSetCell(y_output, t, mxCreateDoubleMatrix(n[t], 1, mxREAL));

    y[t] = Eigen::VectorXd::Zero(n[t]);
    y_temp.resize(n[t]);

    Eigen::Map<Eigen::VectorXd> y[t](mxGetPr(mxGetCell(y_output, t)), n[t]); // This is not correct!



}


// Myfun(y, z);

// set output
plhs[0] = y_output;

}

Solution

  • Your solution copies data around both for the input and the output of MyFunc. It is possible to pass both arguments using a std::vector of Eigen::Map objects. The following code is based on your answer. Lines starting with //-- were removed from your code and replaced by the line which follow.

    As a sidenote: Avoid allocating arrays with new (in 99.99% of all cases), but use a std::vector instead. This takes care of deallocating all resources when the object gets out of scope. Also, in MyFunc you don't have to guess the size of y and z.

    #include "mex.h"
    #include "matrix.h"
    //-- #include <Eigen> <-- this should be <Eigen/Eigen>, but likely Eigen/Core suffices
    //                        Perhaps you also need to change your include-path 
    #include <Eigen/Core>
    
    #include <vector>
    
    void mexFunction(int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[])
    {
    
        // prhs[0]: a cell array of length T, each element is a vector with different lengths
        mwSize T =  mxGetNumberOfElements(prhs[0]);
        //-- mwSize* n = new mwSize[T];
        std::vector<mwSize> n(T);
        //-- Eigen::VectorXd* z = new Eigen::VectorXd[T];
        std::vector<Eigen::Map<const Eigen::VectorXd> > z; // input vector of Maps
        z.reserve(T);
    
        for(int t=0; t<T; t++){
            // Note: You don't actually seem to need n[t], except for creating y_output
            n[t] = mxGetNumberOfElements(mxGetCell(prhs[0], t));
            //-- z[t] = Eigen::Map<Eigen::VectorXd>(mxGetPr(mxGetCell(prhs[0], t)), n[t]);
            z.emplace_back(mxGetPr(mxGetCell(prhs[0], t)), n[t]);
        }
    
        // create a cell matrix with T rows and one columns
        mxArray* y_output = mxCreateCellMatrix(T,1);
    
        // create corresponding Eigen objects
        //-- Eigen::VectorXd* y = new Eigen::VectorXd[T]();
        std::vector<Eigen::Map<Eigen::VectorXd> > y; // output vector of Maps
        y.reserve(T);
    
        // This must be called after setting up y: 
        //-- Myfun(y, z);
    
        //-- double* ptemp;
        for(int t=0; t<T; t++){
            mxSetCell(y_output, t, mxCreateDoubleMatrix(n[t], 1, mxREAL));
    
            //-- ptemp = mxGetPr(mxGetCell(y_output, t));
            //-- // assign the data stored in y[t] to the contents in y_output.
            //-- for(int i=0; i<n[t]; i++){
            //--     //mxGetPr(mxGetCell(y_output, t))[i] = y[t](i);
            //--     ptemp[i] = y[t](i);
            //-- }
            y.emplace_back(mxGetPr(mxGetCell(y_output, t)), n[t]);
        }
    
        // Now call Myfun, but the function now needs to accept vectors of Eigen::Map, instead of pointers to VectorXd
        // It should be possible to keep the Code inside Myfun unchanged
        // Myfun(y, z);
    
        //-- ptemp = NULL;
    
        // set output
        plhs[0] = y_output;
    
    }