I'm trying to run MATLAB from C++ and return the MATLAB output in a structure to C++. The structure could contain any number of things, including arrays of different dimensions and lengths. There's a similar question here, but the answer doesn't give enough detail for me to understand and extrapolate.
I'm running MATLAB using the MatlabEngine.hpp
and MatlabDataArray.hpp
. I need to return a lot of outputs and have tried other methods which don't quite do what I'm after. Using a structure seems the most logical / readable way to do things. I'll try to explain my case with the following examples, which are hopefully written in a way which will be most useful for anyone else with a similar problems.
function output = simple_fun1(a, bc)
% Takes input values of a and an array [a,b]
b = bc(1);
c = bc(2);
sum = a+b+c;
prod = a*b*c;
output = [sum, prod; 3, 4];
This can be run using the C++ code:
#include "MatlabEngine.hpp"
#include "MatlabDataArray.hpp"
#include <iostream>
int main()
{
using namespace matlab::engine;
matlab::data::ArrayFactory factory; // Create MATLAB data array factory
std::unique_ptr<MATLABEngine> matlabPtr = startMATLAB(); // Start MATLAB engine
matlabPtr->eval(u"addpath(genpath('C:/Users/...your path here...'))"); // Add the path to where MATLAB functions are.
std::vector<double> bc{ 10, 13};
std::vector<matlab::data::Array> args({
factory.CreateScalar<double>(7),
factory.CreateArray({ 1, 2 }, bc.cbegin(), bc.cend())
});
matlab::data::TypedArray<double> results = matlabPtr->feval(u"simple_fun1",args); // Run simple_fun1
std::cout << "Sum: " << results[0][0] << std::endl;
std::cout << "Prod: " << results[0][1] << std:endl;
std::cout << "Three: " << results[1][0] << std::endl;
}
This works for a single return of an n by n array. But if I want to return data separately, i.e. function [sum, prod] = simple_fun1(a,bc)
it doesn't work.
I've managed to return multiple outputs using tuples, but then I can only access the first element of the array (if it was an array output) since I've been unable to define a tuple of MATLAB arrays. For example
function [sum, prod] = simple_fun2(a, bc)
% Takes input values of a and an array [a,b]
b = bc(1);
c = bc(2);
sum = a+b+c;
prod = a*b*c;
std::tuple<double, double> results;
results = matlabPtr->feval< std::tuple<double, double>>(u"simple_fun2", double(7), std::vector<double>{ 10, 13}); // Just as another example of how to enter inputs in case that's helpful for anyone.
double s;
double p;
std::tie(s, p) = results;
std::cout << "Sum: " << s << ", Prod: " << p << std::endl;
Instead, I would like to write my MATLAB function so that it returns a structure, which should hopefully simplify the code for large amounts of data being passed and allow the data to have different dimensions. However, I've been unable to create a working example.
function output = simple_fun3(a, bc)
b = bc(1);
c = bc(2);
output.sum = a+b+c;
output.prod = a*b*c;
output.a_sq = a*a;
output.b_sq = b*b;
#include "MatlabEngine.hpp"
#include "MatlabDataArray.hpp"
#include <iostream>
int main()
{
using namespace matlab::engine;
matlab::data::ArrayFactory factory; // Create MATLAB data array factory
std::unique_ptr<MATLABEngine> matlabPtr = startMATLAB(); // Start MATLAB engine
matlabPtr->eval(u"addpath(genpath('C:/Users/...your path here...'))"); // Add the path to where MATLAB functions are.
std::vector<double> bc{ 10, 13};
std::vector<matlab::data::Array> args({
factory.CreateScalar<double>(7),
factory.CreateArray({ 1, 2 }, bc.cbegin(), bc.cend())
});
matlab::data::StructArray my_matlab_struct = factory.createStructArray(matlab::data::ArrayDimensions{ 1, 4}, std::vector<std::string>{'sum', 'prod', 'a_sq', 'b_sq'});
my_matlab_struct = matlabPtr->feval(u"simple_fun3",args);
The above C++ code doesn't work and I don't understand how the structure is being defined; i.e. what the ArrayDimensions are dimensions of. Any help is appreciated. Thanks
After much searching I managed to solve the problem and pass structures between C++ and MATLAB. The solution is based on this, but the code at the link doesn't work due to missing namespaces. Therefore I've put the solution below. Note: the solution assumes you already have MATLAB and C++ setup to interface with each other, which can be a whole other process depending your MATLAB version and, if you're using Visual Studio, your Visual Studio version too.
Not part of my original question, but this may be useful to someone.
#include "MatlabEngine.hpp"
#include "MatlabDataArray.hpp"
#include <iostream>
#pragma comment (lib,"libmat.lib")
#pragma comment (lib,"libmx.lib")
#pragma comment (lib,"libmex.lib")
#pragma comment (lib,"libeng.lib")
int main()
{
using namespace matlab::engine;
matlab::data::ArrayFactory factory; // Create MATLAB data array factory
std::unique_ptr<MATLABEngine> matlabPtr = startMATLAB(); // Start MATLAB engine
matlabPtr->eval(u"addpath(genpath('Insert the path of any MATLAB functions you plan to use'))")
matlab::data::StructArray my_structure = factory.createStructArray({1, 1},{"x","y","z"}); // Create a structure with one element and fields x,y,z
my_structure[0]["x"] = factory.createArray({1, 3},{1., 2., 3.});
my_structure[0]["y"] = factory.createArray({1, 3},{1., 4., 9.});
my_structure[0]["z"] = factory.createArray({1, 3},{1., 8., 27.});
matlabPtr->setVariable(u"Mstruct",my_structure); // pass structure to matlab workspace
matlabPtr->eval(u"Mstruct") // show the structure exists.
// The array can be passed to a function using feval
}
The code below assumes the MATLAB engine is already set up and gets a structure returned from a MATLAB script called matlab_fun
which accepts the input(s) args
.
matlab::data::StructArray my_matlab_struct = matlabPtr->feval(u"matlab_fun",args);
matlab::data::ArrayDimensions dims = my_matlab_struct.getDimensions();
std::cout << "Structure is: " << dims[0] << " by " << dims[1] << std::endl;
size_t numFields = my_matlab_struct.getNumberOfFields();
std::cout << "Structure has " << numFields << " fields." << std::endl;
matlab::data::Range<matlab::data::ForwardIterator, matlab::data::MATLABFieldIdentifier const> fields = my_matlab_struct.getFieldNames();
for (int i=0; i<numFields; i++){
matlab::data::TypedArray<double> data = my_matlab_struct[0][fields.begin()[i]]; // [0] for the first element of the structure. Iterates over the fields.
matlab::data::ArrayDimensions data_dims = data.getDimensions();
for (int j=0; j<data_dims[0];j++){
for (int k=0; k<data_dims[1];k++){
std::cout << data[j][k] << " ";
}
std::cout << std::endl;
}
}