1

There is a standard example of how you can create or change a array in C++ and use it as an array in WarpControlPoints:

/*==========================================================
 * arrayProduct.c - example in MATLAB External Interfaces
 *
 * Multiplies an input scalar (multiplier) 
 * times a 1xN matrix (inMatrix)
 * and outputs a 1xN matrix (outMatrix)
 *
 * The calling syntax is:
 *
 *      outMatrix = arrayProduct(multiplier, inMatrix)
 *
 * This is a MEX-file for MATLAB.
 * Copyright 2007-2012 The MathWorks, Inc.
 *
 *========================================================*/
/* $Revision: 1.1.10.4 $ */

#include "mex.h"

/* The computational routine */
void arrayProduct(double x, double *y, double *z, mwSize n)
{
    mwSize i;
    /* multiply each element y by x */
    for (i=0; i<n; i++) {
        z[i] = x * y[i];
    }
}

/* The gateway function */
void mexFunction( int nlhs, mxArray *plhs[],
                  int nrhs, const mxArray *prhs[])
{
    double multiplier;              /* input scalar */
    double *inMatrix;               /* 1xN input matrix */
    size_t ncols;                   /* size of matrix */
    double *outMatrix;              /* output matrix */

    /* get the value of the scalar input  */
    multiplier = mxGetScalar(prhs[0]);

    /* create a pointer to the real data in the input matrix  */
    inMatrix = mxGetPr(prhs[1]);

    /* get dimensions of the input matrix */
    ncols = mxGetN(prhs[1]);

    /* create the output matrix */
    plhs[0] = mxCreateDoubleMatrix(1,(mwSize)ncols,mxREAL);

    /* get a pointer to the real data in the output matrix */
    outMatrix = mxGetPr(plhs[0]);

    /* call the computational routine */
    arrayProduct(multiplier,inMatrix,outMatrix,(mwSize)ncols);
}

This is basically what I am looking for, just that I want to change a 2D array instead of just a simple array. I tried to create a 2D array (4 x n) and to change the 4th row just to see if it works. If I change the following lines:

/* The computational routine */
void arrayProduct(double x, double *y, double *z, mwSize n)
{
mwSize i;
/* multiply each element y by x */
for (i=0; i<n; i++) {
z[3][i] = x * y[i];
}
}

and

/* create the output matrix */
    plhs[0] = mxCreateDoubleMatrix(4,(mwSize)ncols,mxREAL);

it doesn't work. I get the error that z is neither a field nor a pointer. Can anyone tell me what I have done wrong and how I get this working?

1
  • In Matlab the array index starts from 1 not from 0 as it does in other languages. Commented Aug 29, 2013 at 16:05

3 Answers 3

3

Multidimensional arrays are still stored as a single contiguous array, rather than a 2D C array. Data is in column-major order, which means z[0] is element (1,1), and z[1] is element(2,1), etc, all the way to z[4*N-1]

To compute the linear index from the desired 2D index (row,column) (0-based), just write idx = column*nrows + row;. What that means is you need to pass the nrows value into your computation function.

So: add an extra parameter to your computation function called nrows, and pass that value in when you call it. And index z as a 1D array, as described above.

Sign up to request clarification or add additional context in comments.

Comments

2

The questioner might have already got the answer. However, this answer may be useful for people like me who shifted from MATLAB to C++ and want to use some MATLAB features in C++.

Sending a 1D Array to MATLAB from C++ is very easy, as there are many examples available online to show you the process. But sending a 2D array to MATLAB is a little bit tricky as there is not enough information available (particularly using dynamic arrays). A process has been shown in the below code. The process follows some simple steps,

  1. create a 2D Array in C++.
  2. Send each column of that 2D Array to MATLAB using engPutVariable_func() (user defined function).
  3. Append each column together in MATLAB using engEvalString() in C++.
#include <iostream>
#include <stdlib.h>
#include <stdio.h>
#include <cmath>
#include <string.h>
#include "engine.h"
#include "mex.h"
#include <armadillo> 
/* I'm using armadillo for convenient matrix operations
 * The process is applicable for other Linear Algebra libraries as well (including STL)
*/

using namespace std;
using namespace arma;

int Create_2DArray_in_Matlab(Engine &m_pEngine, dmat PlotData);
int engPutVariable_func(Engine &m_pEngine, dvec TheVector, string MatVarName);


int main(void)
{
    dmat PlotData(100000, 10, fill::randu);
    /* Our Objective is to Put Matrix "PlotData" in MATLAB workspace*/


    /* Opening Matlab Engine */

    Engine *m_pEngine;
    m_pEngine = engOpen(NULL);
    if (m_pEngine == NULL)
    {
        std::cout << "Error" << std::endl;
        exit(1);
    }
    engSetVisible(m_pEngine, 1);

    engEvalString(m_pEngine, "clc;");
    engEvalString(m_pEngine, "close all;");
    engEvalString(m_pEngine, "clear all;");


    Create_2DArray_in_Matlab(*m_pEngine, PlotData);
    engEvalString(m_pEngine, "open PlotData");

    return 0;

}

int Create_2DArray_in_Matlab(Engine &m_pEngine, dmat PlotData)
{
    /*Approach is to put one column at a time, of a 2D Matrix 
     *in Matlab workspace*/

    int SIZE_Dmat_rows = PlotData.n_rows;
    int SIZE_Dmat_cols = PlotData.n_cols;

    for (int i = 0; i < SIZE_Dmat_cols; i++)
    {
        dvec Temp_Col = PlotData.col(i);
        dvec Col_Number(1); Col_Number(0) = i;
        engPutVariable_func(m_pEngine, Temp_Col, "Temp_Col");
        engPutVariable_func(m_pEngine, Col_Number, "Col_Number");

        engEvalString(&m_pEngine, "PlotData(:, Col_Number + 1) = Temp_Col;");


    }
    return 0;
}


int engPutVariable_func(Engine &m_pEngine, dvec TheVector, string MatVarName)
{
    /*This function will put a column vector in MATLAB Workspace*/

    int SIZE = TheVector.n_rows;

    double *Temp = new double[SIZE];


    for (int i = 0; i < SIZE; i++)
    {
        Temp[i] = TheVector(i);
    }

    char Char_VariableName[1000];
    strcpy(Char_VariableName, MatVarName.c_str());

    /* Creating Matlab Variables*/

    mxArray *TheMatVec = NULL;
    TheMatVec = mxCreateDoubleMatrix(SIZE, 1, mxREAL);
    memcpy((void *)mxGetPr(TheMatVec), (void *)Temp, sizeof(Temp)*SIZE);
    engPutVariable(&m_pEngine, Char_VariableName, TheMatVec);

    delete[] Temp;

    return 0;

}


In a simple MATLAB program, This is what we are doing in C++

clc
clear all
close all

A = rand(10, 20); 

for i = 1:length(A)
    A_Temp = A(:, i);
    B(:, i) = A_Temp
    A_Temp = [];
end

Hope this information may be useful...

If there are any queries and criticisms, please comment.

Thanks and regards

5 Comments

Why not just mxCreateDoubleMatrix(SIZE_Dmat_rows, SIZE_Dmat_cols, mxREAL);, and then copy the data directly into it? It seems you found a rather inefficient method here, I think.
@cris luengo, it works for statically defined 2d arrays. But for dynamically defined 2d arrays, I faced some errors. Hence, I followed this approach.
Maybe you should post a question about those errors. If you include a minimal reproducible example, I’m sure I could help iron those out. :)
@CrisLuengo Thank you so much. As I got some way to work around the problem, I did not think of posting the question. Thanks for your suggestion, I will soon definitely post the question.
@The_Learner Is it also possible to dynamically parameterize the armadillo matrix row and col dimensions in C++ through a pointer/reference? I think of a solution to use the getDimensions() method of the matlab::data::TypedArray<T>, similar to this question: stackoverflow.com/questions/65868123/…
1

Fundamentally, a 2-dimensional array always needs its first dimension to be defined.

Treating the pointer (z) as a 2d array breaks that rule.

Without the first dimension (actually, all but the last dimension) defined, the actual offset from the pointer cannot be correctly calculated.

In your code, as you know the size of each dimension is equal, you can calculate the pointer offset yourself, instead.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.