How to import C++ arrays into Matlab & loop over a C++ program from within Matlab?

14 views (last 30 days)
I would like to know the particular steps I must take in order to connect Matlab and C++ for the following problems:
1. How can I import the results from a C++ program, in particular a 5-dim array of doubles and a 5-dim array of strings, into Matlab in order to analyse the results? 2. I would like to call a C++ program from within Matlab, handing over one parameter, letting the C++ program run and getting a resulting constant back. Depending on this resulting constant I want to change the input parameter and loop over the C++ program again. It shall result in kind of a golden section search...
I already had a look at the Matlab documentation and in particular the mex function. I managed to install the necessary compilers for mex and can compile the original C++ code. Additionally I tried out the arrayProduct_01.cpp. However, I got lost in the comprehensive documentation and am quite confused by now... What do I really need of all this and how to adjust it for my problem?
Thanks a lot for any help!!
  2 Comments
James Tursa
James Tursa on 3 Mar 2015
Edited: James Tursa on 3 Mar 2015
Please post a simple version of your code, at least the I/O parts. For a 5-dim array of doubles, the easiest thing would probably be to create an mxArray with the dimensions in reverse order and then use memcpy to copy the entire variable at once. Then use permute to reverse the dimensions of the mxArray. This is to account for the row vs column memory order of elements difference between C and MATLAB.
merle
merle on 13 Mar 2015
Edited: merle on 13 Mar 2015
Hi James, thanks a lot for your response and sorry for the late reply... somehow didn't get notified...
Below is my sample code where I try to hand over a 3D array from Matlab to C++ (works) and to return another 3D array (here actually the same as the input array). Compiling works but when I call the function from Matlab, e.g. with a 3x3x3 array, half of the returned array is empty. What do I do wrong?
I also don't understand yet how to "to create an mxArray with the dimensions in reverse order and then use memcpy to copy the entire variable at once" as suggested. Thanks for any more hints.
Merlin
Code:
#include <math.h>
#include "matrix.h" // mwSize, mwIndex
#include "mex.h"
void mexFunction(int nlhs, mxArray *plhs[], // plhs: array of mxArray pointers to output
int nrhs, const mxArray *prhs[]) // prhs: array of mxArray pointers to input
{
// Macros for the ouput and input arguments
#define A_IN prhs[0] // 3-dim double array IN
#define B_OUT plhs[0] // 3-dim double array OUT
// Declaration:
mwSize ndim_A; // number of dimensions of A
const mwSize *dims_A; // dimensions of A
char *pointer_A; // pointers to input matrix
char *pointer_B; // pointer to output matrix
size_t dim1, dim2, dim3;
// Check arguments:
if (nrhs<1 || nrhs>2)
mexErrMsgTxt("Wrong number of input arguments.");
else if (nlhs > 1)
mexErrMsgTxt("Too many output arguments.");
// Initialization:
ndim_A = mxGetNumberOfDimensions(A_IN); // get # of dimensions
dims_A = mxGetDimensions(A_IN); // get dimensions
pointer_A = (char *)mxGetData(A_IN); // pointer to input matrix A
B_OUT = mxCreateCharArray(ndim_A,dims_A); // Create 3D character array
pointer_B = (char *)mxGetData(B_OUT); // pointer to output matrix B
// Copy data into the Output-mxArray:
for(dim3 = 0; dim3 < dims_A[2]; dim3++)
{
for(dim2 = 0; dim2 < dims_A[1]; dim2++)
{
for(dim1 = 0; dim1 < dims_A[0]; dim1++)
{
pointer_B[dim3+dim2*dims_A[2]+dim1*dims_A[2]*dims_A[1]] =
pointer_A[dim3+dim2*dims_A[2]+dim1*dims_A[2]*dims_A[1]];
}
}
}
return;
}

Sign in to comment.

Accepted Answer

James Tursa
James Tursa on 13 Mar 2015
Edited: James Tursa on 13 Mar 2015
Thanks for posting your code.
I haven't checked your indexing logic yet, but before we get into that you must first correct your pointer types. MATLAB stores char data as 2-bytes per char, not 1-byte per char as in C. Using a (char *) type in C to copy MATLAB char data is only going to get half of the data copied. So change all of your (char *) types in C that are used for MATLAB char array data to (mxChar *) instead, the macro defined in the API header files to deal with character elements (some type of 2-byte unsigned integer behind the scenes).
Regarding the indexing, I can't give specific advice without knowing more details of what you are doing. That being said, the general advice is that MATLAB stores arrays in memory in "column" order (like Fortran), but C/C++ store arrays in memory in "row" order. So if you are copying between them this needs to be accounted for. Either you need to explicitly write the code for element-by-element copying or use a combination of memcpy and permute functionality. E.g., for a 2 x 3 case:
MATLAB:
x = zeros(2,3);
The storage order in memory will be:
x(1,1)
x(2,1)
x(1,2)
x(2,2)
x(1,3)
x(2,3)
In C/C++:
double x[2][3];
The storage order in memory will be:
x[0][0]
x[0][1]
x[0][2]
x[1][0]
x[1][1]
x[1][2]
Even after accounting for the 1-based vs 0-based indexing, you can see that the indexing in memory doesn't match up between the two. If you needed to copy the C/C++ array to MATLAB, one method would be to create a 3 x 2 mxArray (i.e., dimensions reversed), then use memcpy on the entire block of memory, and then on the MATLAB side transpose it. That would get you back to the ordering you would expect. For multi-dimensional arrays, e.g. 3 x 4 x 5 in C/C++, create the mxArray as 5 x 4 x 3, then use memcpy on the entire block, then on the MATLAB side permute(array,[3 2 1]).
  2 Comments
merle
merle on 15 Mar 2015
Hi James,
that was great help, thanks!
I indeed had to use mxChar to account for the different byte lengths of the char data type in Matlab and C++. It required to include the header file >cstring>. The loop worked but I nevertheless implemented the more efficient memcpy now.
Below follows the corrected and improved sample code for the import and export of multidimensional char and double arrays in case anybody faces a similar problem...
// MEX function to test data import from Matlab to C++ and export back
// for multi dimensional double and character arrays
// ------------------------------------------------------------------------
// Compile in Matlab with: -mex filename.cpp
// Call in Matlab with: [DoubleArray_OUT CharArray_OUT] = imexport(DoubleArray_IN CharArray_IN)
// ========================================================================
#include <cstring> // memcpy
#include <math.h>
#include "matrix.h" // mwSize, mwIndex
#include "mex.h"
#define IS_REAL_FULL_DOUBLE(P) (!mxIsComplex(P) && !mxIsSparse(P) && mxIsDouble(P))
#define IS_REAL_FULL_CHAR(P) (mxIsChar(P) && !mxIsSparse(P))
void mexFunction(int nlhs, mxArray *plhs[], // plhs: array of mxArray pointers to output
int nrhs, const mxArray *prhs[]) // prhs: array of mxArray pointers to input
{
// Macros for the double ouput and input arguments:
#define double_IN prhs[0] // 3-dim double array IN
#define double_OUT plhs[0] // 3-dim double array OUT
// Macros for the char ouput and input arguments:
#define char_IN prhs[1] // 3-dim double array IN
#define char_OUT plhs[1] // 3-dim double array OUT
// Check number of arguments:
if (nrhs<2 || nrhs>3)
mexErrMsgTxt("Error: Two input arguments required.");
else if (nrhs<2 || nrhs>3)
mexErrMsgTxt("Internal error: Wrong number of output arguments.");
// Check type of arguments:
if (!IS_REAL_FULL_DOUBLE(double_IN))
mexErrMsgTxt("Error: First input argument must be a double array.");
else if (!IS_REAL_FULL_CHAR(char_IN))
mexErrMsgTxt("Error: Second input argument must be a char array.");
// Declaration for double arrays:
mwSize iter_doubleIN; // number of dimensions of input
const mwSize *dims_doubleIN; // dimensions of input
mxArray *p_doubleIN; // pointer to input array
mxArray *p_doubleOUT; // pointer to output array
int bytes_to_copy_double, iter_double;
// int dim1, dim2, dim3; // not needed when memcpy is used
// Declaration for char arrays:
mwSize iter_charIN; // number of dimensions of input
const mwSize *dims_charIN; // dimensions of input
mxChar *p_charIN; // pointers to char input array
mxChar *p_charOUT; // pointer to char output array
int bytes_to_copy_char, iter_char;
// size_t dim1, dim2, dim3; // not needed when memcpy is used
// Initialization for double arrays:
iter_doubleIN = mxGetNumberOfDimensions(double_IN); // get # of dims
dims_doubleIN = mxGetDimensions(double_IN); // get dimensions
p_doubleIN = (mxArray *)mxGetPr(double_IN); // pointer to input
double_OUT = mxCreateNumericArray(iter_doubleIN,dims_doubleIN,mxDOUBLE_CLASS,mxREAL); // Create an N[0]×...× N[K-1] array for output
p_doubleOUT = (mxArray *)mxGetPr(double_OUT); // pointer to output
// Initialization for char arrays:
iter_charIN = mxGetNumberOfDimensions(char_IN); // get # of dims
dims_charIN = mxGetDimensions(char_IN); // get dimensions
p_charIN = (mxChar *)mxGetData(char_IN); // pointer to input
char_OUT = mxCreateCharArray(iter_charIN,dims_charIN); // Create N[0]×...×N[K-1] char array for output
p_charOUT = (mxChar *)mxGetData(char_OUT); // pointer to output
// DOUBLE - Copy data into the output array:
bytes_to_copy_double = 1;
for(iter_double=0; iter_double < iter_doubleIN; iter_double++)
{
bytes_to_copy_double = bytes_to_copy_double*dims_doubleIN[iter_double];
}
bytes_to_copy_double = bytes_to_copy_double * sizeof(double); // total size of bytes to be copied
memcpy( (void *)p_doubleOUT, (void *)p_doubleIN, bytes_to_copy_double ); // void * memcpy ( void * destination, const void * source, size_t num );
// CHAR - Copy data into the output array:
bytes_to_copy_char = 1;
for(iter_char=0; iter_char < iter_charIN; iter_char++)
{
bytes_to_copy_char = bytes_to_copy_char*dims_charIN[iter_char];
}
bytes_to_copy_char = bytes_to_copy_char * 2; // *2 because Matlab stores each char data as 2-bytes instead of 1-byte as in C
memcpy( (void *)p_charOUT, (void *)p_charIN, bytes_to_copy_char ); // void * memcpy ( void * destination, const void * source, size_t num );
/* Alternative to copy data into the char output array(less efficient):
for(dim3 = 0; dim3 < dims_charIN[2]; dim3++) // Compute a matrix with normalized columns
{
for(dim2 = 0; dim2 < dims_charIN[1]; dim2++)
{
for(dim1 = 0; dim1 < dims_charIN[0]; dim1++)
{
p_charOUT[dim3+dim2*dims_charIN[2]+dim1*dims_charIN[2]*dims_charIN[1]] =
p_charIN[dim3+dim2*dims_charIN[2]+dim1*dims_charIN[2]*dims_charIN[1]];
}
}
} */
/* Alternative to copy data into the double output mxArray (less efficient):
for(dim3 = 0; dim3 <= dims_doubleIN[2]; dim3++)
{
mexPrintf("dim3 = %d \n",dim3);
for(dim2 = 0; dim2 <= dims_doubleIN[1]; dim2++)
{
mexPrintf("dim2 = %d \n",dim2);
for(dim1 = 0; dim1 <= dims_doubleIN[0]; dim1++)
{
mexPrintf("dim1 = %d \n",dim1);
mexPrintf("element %d \n",(dim1-1)+(dim2-1)*(dims_doubleIN[0])+(dim3-1)*(dims_doubleIN[0])*(dims_doubleIN[1]));
p_doubleOUT[(dim3)+(dim2)*(dims_doubleIN[2])+(dim1)*(dims_doubleIN[2])*(dims_doubleIN[1])] =
p_doubleIN[(dim3)+(dim2)*(dims_doubleIN[2])+(dim1)*(dims_doubleIN[2])*(dims_doubleIN[1])];
// p_doubleOUT[(dim1-1)+(dim2-1)*(dims_doubleIN[0])+(dim3-1)*(dims_doubleIN[0])*(dims_doubleIN[1])] = p_doubleIN[(dim1-1)+(dim2-1)*(dims_doubleIN[0])+(dim3-1)*(dims_doubleIN[0])*(dims_doubleIN[1])];
}
}
} */
return;
// Clean up workspace:
mxDestroyArray(p_doubleIN); // free memory occupied by input mxArray
mxDestroyArray(p_doubleOUT); // free memory occupied by output mxArray
mxFree(p_charIN); // free memory occupied by input mxChar
mxFree(p_charOUT); // free memory occupied by output mxChar
}
// Test for example with:
/* A(:,:,1)=['a','b','c';'d','e','f';'g','h','i'];
A(:,:,2)=['j','k','l';'m','n','o';'p','q','r'];
A(:,:,3)=['s','t','u';'v','w','x';'y','z','z'];
B(:,:,1)=[ 1 2 3; 4 5 6; 7 8 9];
B(:,:,2)=[10 11 12; 13 14 15; 16 17 18];
B(:,:,3)=[19 20 21; 22 23 24; 25 26 27]; */
James Tursa
James Tursa on 16 Mar 2015
Simpler way to get number of bytes to copy:
bytes_to_copy_double = mxGetNumberOfElements(double_IN) * mxGetElementSize(double_IN);
bytes_to_copy_char = = mxGetNumberOfElements(char_IN) * mxGetElementSize(char_IN);

Sign in to comment.

More Answers (0)

Categories

Find more on Write C Functions Callable from MATLAB (MEX Files) in Help Center and File Exchange

Community Treasure Hunt

Find the treasures in MATLAB Central and discover how the community can help you!

Start Hunting!