Thursday, January 16, 2020

Using workspace variables in C++ Mex functions

When you have C or C++ files that you would like to use in your Simulink model, you have to interface them with a mex file. Variables in Matlab workspace can be used inside C++ mex functions. Example m file and mex function demonstrating scalar, vector, matrix, string and structure usage:

%Tested with Matlab R2019b and Visual Studio 2015
clc; clear all
inScalar = 153;
inVector = [1 2 3];
inMatrix = [1 2 3; 11 22 33; 111 222 333; 1111 2222 3333];
inCharArray = 'c:/temp/a/';
inString = "c:/temp/a/"; %Note double quotes
inStruct.x = [1 2 3];
inStruct.y = [11 22; 111 222];
mex mexWorkspaceDemo.cpp
mexWorkspaceDemo();
view raw mexDriver.m hosted with ❤ by GitHub
/* Demonstrate how to use Matlab workspace variables from C++ mex function.
* Note that you can only set to or get from BASE or GLOBAL workspace, you cannot use caller workspace (workspace of the caller function).
* For more, see https://www.mathworks.com/help/matlab/matlab_external/set-and-get-variables-in-matlab-workspace.html
* See mexDriver.m on how to use this mex function.
* Şamil Korkmaz, Jan 2020
*/
#include "mex.hpp"
#include "mexAdapter.hpp"
#include <iostream>
using matlab::mex::ArgumentList;
using namespace matlab::engine;
using namespace matlab::data;
using namespace std;
class MexFunction : public matlab::mex::Function {
private:
ArrayFactory factory;
shared_ptr<matlab::engine::MATLABEngine> matlabPtr = getEngine();
void getCharArray(char* name, char* output) {
CharArray inCharArray = matlabPtr->getVariable(name);
for(size_t i=0; i < inCharArray.getNumberOfElements(); i++) {
output[i] = inCharArray[i];
}
output[inCharArray.getNumberOfElements()] = 0; //ASCII Null or termination character denoting end of string
}
public:
void operator()(ArgumentList outputs, ArgumentList ins) {
//inputs from workspace:
TypedArray<double> inScalarArray = matlabPtr->getVariable(u"inScalar");
TypedArray<double> inVectorArray = matlabPtr->getVariable(u"inVector");
TypedArray<double> inMatrixArray = matlabPtr->getVariable(u"inMatrix");
TypedArray<MATLABString> inStringArray = matlabPtr->getVariable(u"inString");
char inCharArrayOutput[255];
getCharArray("inCharArray", inCharArrayOutput);
StructArray inStructArray = matlabPtr->getVariable(u"inStruct");
//printf does not print to Command Window, you have to use cout:
cout <<"\nINPUTS FROM MATLAB WORKSPACE:"<<endl;
cout << "inScalarArray[0] = " << inScalarArray[0] << endl; //You can only print if inScalarArray is of type TypedArray<double>, you can't if type ia Array, you will get error C2593: 'operator <<' is ambiguous
cout << "inVectorArray";
for(size_t i=0; i<inVectorArray.getNumberOfElements(); i++) {
cout << "[" << i << "] = " << inVectorArray[i] << " ";
}
cout << endl;
cout << "inMatrixArray.getNumberOfElements() = " << inMatrixArray.getNumberOfElements() << endl;
ArrayDimensions inMatrixDims = inMatrixArray.getDimensions();
size_t nRows = inMatrixDims[0];
size_t nCols = inMatrixDims[1];
for(size_t iRow=0; iRow < nRows; iRow++) {
for(size_t iCol=0; iCol < nCols; iCol++) {
cout << "inMatrixArray[" << iRow << "][" << iCol << "] = " << inMatrixArray[iRow][iCol] << "\t ";
}
cout << endl;
}
cout<<"inCharArray = " << inCharArrayOutput << endl;
string stringVal = inStringArray[0]; //convert from Matlab string to standard C++ string
cout << "stringVal = " << stringVal << endl; //You cannot use inStringArray[0] here.
auto fields = inStructArray.getFieldNames();
std::vector<std::string> fieldNames(fields.begin(), fields.end());
//size_t nElements = inStructArray.getNumberOfElements(); cout << "nElements = " << nElements << endl;
for(size_t i = 0; i<fieldNames.size(); i++) {
//cout << "fieldNames[" << i << "] = " << fieldNames[i] << endl;
TypedArray<double> const structField = inStructArray[0][fieldNames[i]];
ArrayDimensions dims = structField.getDimensions();
size_t nRows = dims[0];
size_t nCols = dims[1];
for(size_t iRow=0; iRow < nRows; iRow++) {
for(size_t iCol=0; iCol < nCols; iCol++) {
double value = structField[iRow][iCol];
cout << "structField[" << iRow << "][" << iCol << "] = " << value << "\t ";
}
cout << endl;
}
cout << endl;
}
//outputs to workspace:
double b = inScalarArray[0]; //convert to C++ double
Array outScalarArray = factory.createScalar(b+10);
double outScalar = outScalarArray[0]; //convert to C++ double
matlabPtr->setVariable(u"outScalar", outScalarArray, WorkspaceType::GLOBAL);
Array outVectorArray = factory.createArray<double>({1, 3}, {11, 22, 33});
matlabPtr->setVariable(u"outVector", outVectorArray, WorkspaceType::GLOBAL);
Array outMatrixArray = factory.createArray<double>({2, 2}, {1, 2, 11, 22});
matlabPtr->setVariable(u"outMatrix", outMatrixArray, WorkspaceType::GLOBAL);
Array outStringArray = factory.createArray<string>({1, 1}, {"samil was here"});
matlabPtr->setVariable(u"outString", outStringArray, WorkspaceType::GLOBAL);
cout <<"\nOUTPUTS FROM MATLAB WORKSPACE:"<<endl;
cout << "outScalar + b = " << outScalar + b << endl; //You cannot use outScalarArray[0] + b, you will get error binary '+': 'matlab::data::ArrayElementRef<false>' does not define this operator
}
};
When you run mexDriver.m, you will get an output similar to this:


No comments:

Post a Comment