Pass C++ Structures to Deployed MATLAB Functions - MATLAB & Simulink (original) (raw)
This example shows how to pass C++ structures to a MATLAB® function deployed within a C++ application. The workflow is supported on Windows®, Linux®, and macOS systems.
To pass C++ structs to MATLAB functions, in your application code, you must start MATLAB Runtime in IN_PROCESS
mode. You need to use theUSING_TYPE_WITH_MATLAB
macro to inform MATLAB about the custom C++ types, ensuring proper recognition and handling during function calls. Additionally, you must use the matlab::cpplib::MATLABLibrary::feval method in your C++ code to evaluate and execute the MATLAB functions when passing these structures.
Prerequisites
- Create a new work folder that is visible to the MATLAB search path. This example uses a folder named
work
. - Verify that you have set up a C++ development environment. For details, see Set Up C++ Development Environment. This example uses MATLAB as a C++ development environment. Therefore, verify that you have a C++ compiler installed by typing
mbuild -setup C++
at the MATLAB command prompt. - Verify that you have met all of the MATLAB Compiler SDK™ C++ target requirements. For details, see MATLAB Compiler SDK C++ Target Requirements.
- End users must have an installation of MATLAB Runtime to run the application. For details, see Download and Install MATLAB Runtime.
For testing purposes, you can use an installation of MATLAB instead of MATLAB Runtime when running the C++ application.
Create C++ Header File Containing Namespace and Struct Definitions
Create a header file named cppstruct.hpp
that includes a namespace and structure definitions for the data you want to pass to the deployed MATLAB function.
#include <vector>
#include <string>
#include <map>
// Define a namespace for the struct definitions
namespace cppstruct {
// Define a struct for the input data
struct InputStruct {
std::vector<double> temperatures;
std::vector<double> pressures;
};
// Define a struct for the statistical results of each field
struct FieldStats {
double mean;
double std;
double max;
};
// Define a struct for the output data
struct OutputStruct {
FieldStats temperatures;
FieldStats pressures;
};
}
The cppstruct.hpp
header file defines a namespacecppstruct
that contains three structures:InputStruct
, FieldStats
, andOutputStruct
.
InputStruct
holds vectors of temperature and pressure data.FieldStats
stores the following statistical results for a field: mean, standard deviation, and maximum value.OutputStruct
is a nested structure that containsFieldStats
for both temperatures and pressures.
Generate MATLAB Interface to C++ Header File
Generate a MATLAB interface for the C++ header file namedcppstruct.hpp
using the clibgen.buildInterface function. In this particular case, we chose to name the interface data
, but in other cases, you can use different names.
clibgen.buildInterface("cppstruct.hpp", InterfaceName="data")
This creates a folder named data
and generates a DLL nameddataInterface.dll
. Theclibgen.buildInterface
function follows the convention of appending the word Interface
to the interface name you provide.
Add Interface to MATLAB Path
To use the C++ structures in your MATLAB code, add the interface folder to the MATLAB path.
addpath("P:\MATLAB\work\data")
Create MATLAB Function
Create a MATLAB file named analyzeData.m
with the following code:
function outputStruct = analyzeData(inputStruct) % analyzeData Analyzes numeric fields in a C++ struct and returns % statistical measures. % % This function takes 'inputStruct' as input, which is a MATLAB % representation of a C++ struct. This representation is generated using % the clibgen.buildInterface function. The function performs statistical % analysis on each numeric field within the struct and returns the results % in a new struct 'outputStruct'. The analyses include computing the mean, % standard deviation, and maximum value for each numeric field. % % Inputs: % inputStruct - A MATLAB representation of a C++ struct % (clib.data.cppstruct.InputStruct) that has temperature and pressure % data. It contains the fields: % - temperatures: a clib.array.data.Double array of temperature values % - pressures: a clib.array.data.Double array of pressure values % % Outputs: % outputStruct - A MATLAB representation of a C++ struct % (clib.data.cppstruct.OutputStruct) with the same fields as % 'inputStruct'. For each numerical array in 'inputStruct', the % corresponding field in 'outputStruct' is a struct containing: % - mean: Mean value of the numerical array % - std: Standard deviation of the numerical array % - max: Maximum value of the numerical array
arguments (Input) inputStruct (1,1) clib.data.cppstruct.InputStruct end
arguments (Output) outputStruct (1,1) clib.data.cppstruct.OutputStruct end
% Initialize outputStruct outputStruct = clib.data.cppstruct.OutputStruct;
% Perform analysis on 'temperatures' field if isa(inputStruct.temperatures, 'clib.array.data.Double') % Cast C++ clib.array.data.Double array to MATLAB double array doubleTemperatures = double(inputStruct.temperatures);
% Calculate mean
outputStruct.temperatures.mean = mean(doubleTemperatures);
% Calculate standard deviation
outputStruct.temperatures.std = std(doubleTemperatures);
% Calculate max value
outputStruct.temperatures.max = max(doubleTemperatures);
else warning('Field temperatures is not numeric and was skipped.'); end
% Perform analysis on 'pressures' field if isa(inputStruct.pressures, 'clib.array.data.Double') % Cast C++ clib.array.data.Double array to MATLAB double array doublePressures = double(inputStruct.pressures);
% Calculate mean
outputStruct.pressures.mean = mean(doublePressures);
% Calculate standard deviation
outputStruct.pressures.std = std(doublePressures);
% Calculate max value
outputStruct.pressures.max = max(doublePressures);
else warning('Field pressures is not numeric and was skipped.'); end
end
To use C++ structs in a MATLAB function, you need to specify the argument definitions in the function's arguments block. These definitions indicate that the C++ structs defined in the cppstruct.hpp
header file will be used. First, ensure the folder containing the generated MATLAB interface files is added to the MATLAB path. The structures defined in the header file are accessed in MATLAB with the naming conventionclib._`<interfacename>.<namespace>.<structurename>`_
. For example, in this case, you useclib.data.cppstruct.InputStruct
for the input structure and clib.data.cppstruct.OutputStruct
for the output structure.
For details on data type mapping, see C++ to MATLAB Data Type Mapping.
Test the MATLAB function at the command prompt.
input = clib.data.cppstruct.InputStruct; input.temperatures = [72, 75, 69, 68, 70]; input.pressures = [30, 29.5, 30.2, 29.9, 30.1]; output = analyzeData(input) disp("Temperatures:") disp(output.temperatures) disp("Pressures:") disp(output.pressures)
output = OutputStruct with properties:
temperatures: [1×1 clib.data.cppstruct.FieldStats]
pressures: [1×1 clib.data.cppstruct.FieldStats]
Temperatures: FieldStats with properties:
mean: 70.8000
std: 2.7749
max: 75
Pressures: FieldStats with properties:
mean: 29.9400
std: 0.2702
max: 30.2000
Package MATLAB Function Using compiler.build.cppSharedLibrary
Create a code archive (.ctf
file) from the MATLAB function using the compiler.build.cppSharedLibrary function.
buildResults = compiler.build.cppSharedLibrary("analyzeData.m", ... OutputDir=".\output", Verbose="on",... AdditionalFiles="P:\MATLAB\work\data\dataInterface.dll");
The interface library dataInterface.dll
must be added as a dependent file using the AdditionalFiles
name-value pair.
The function produces a suite of files, as enumerated below, and places them in the specified output
directory. Among these, the key files utilized during the integration process are the code archive (.ctf
file) containing the MATLAB code. For information on the other files, see Files Generated After Packaging MATLAB Functions.
P:\MATLAB\WORK\OUTPUT │ GettingStarted.html │ includedSupportPackages.txt │ mccExcludedFiles.log │ readme.txt │ requiredMCRProducts.txt │ unresolvedSymbols.txt │ └───v2 └───generic_interface analyzeData.ctf analyzeDatav2.hpp readme.txt
Note
The analyzeDatav2.hpp
header file, which is typically used for working with strongly typed interfaces, is generated but not used in this context.
Note
The generated artifacts do not include MATLAB Runtime or an installer. To create an installer using thebuildResults
object, see compiler.package.installer.
Integrate MATLAB Code Archive into C++ Application
You can finalize the integration process in your preferred C++ development environment, including MATLAB or alternatives such as Microsoft® Visual Studio® on Windows. This example, however, uses MATLAB as a C++ development environment. For details, see Set Up C++ Development Environment.
To integrate the generated MATLAB code archive (.ctf
file) and into a C++ application, adhere to these guidelines:
- Use a
#include
directive to incorporate the C++ header file (.hpp
file) containing the structure definitions in your C++ application code. - Ensure the code archive (
.ctf
file) is positioned in a location that the C++ executable can access.
Completing the integration step requires proficient C++ skills for writing application code. You can use the following sample C++ application code as guide when writing your own application.
- In the
work
folder for this example create a new file namedPassCppStructConsoleApp.cpp
with the following code.PassCppStructConsoleApp.cpp
#include <iomanip>
#include <iostream>
#include <vector>
#include "MatlabCppSharedLib.hpp"
#include "cppstruct.hpp" //include user-defined C++ structs
// Inform MATLAB about the custom C++ types to be used
USING_TYPE_WITH_MATLAB(cppstruct::OutputStruct, "data");
USING_TYPE_WITH_MATLAB(cppstruct::InputStruct, "data");
USING_TYPE_WITH_MATLAB(cppstruct::FieldStats, "data");
// Start MATLAB Runtime, initialize it, and return an object to it
std::shared_ptr<matlab::cpplib::MATLABApplication> setup() {
auto mode = matlab::cpplib::MATLABApplicationMode::IN_PROCESS; // must be started in IN_PROCESS mode to pass C++ structs
std::vector<std::u16string> options = { u"-nojvm" };
std::shared_ptr<matlab::cpplib::MATLABApplication> matlabApplication =
matlab::cpplib::initMATLABApplication(mode, options);
return matlabApplication;
}
// Function to print the MATLAB computation results
void printResults(const cppstruct::OutputStruct& output) {
std::cout << "Temperatures - Mean: " << std::fixed << std::setprecision(4)
<< output.temperatures.mean
<< ", Std: " << output.temperatures.std
<< ", Max: " << output.temperatures.max << std::endl;
std::cout << "Pressures - Mean: " << std::fixed << std::setprecision(4)
<< output.pressures.mean
<< ", Std: " << output.pressures.std
<< ", Max: " << output.pressures.max << std::endl;
}
// Initialize the code archive (.ctf file), specify input arguments, call the MATLAB function,
// and print the result
int mainFunc(std::shared_ptr<matlab::cpplib::MATLABApplication> app, int argc, const char* argv[]) {
try {
std::cout << "Executing MATLAB function ..." << std::endl;
auto libPtr = matlab::cpplib::initMATLABLibrary(app, u"analyzeData.ctf");
if (!libPtr) {
std::cerr << "Failed to initialize MATLAB library." << std::endl;
return -1;
}
// Initialize InputStruct with temperatures and pressures
cppstruct::InputStruct input;
input.temperatures = { 72, 75, 69, 68, 70 };
input.pressures = { 30, 29.5, 30.2, 29.9, 30.1 };
// Set the pointer to input
cppstruct::InputStruct* inputPtr = &input;
if (!inputPtr) {
std::cerr << "Invalid input pointer." << std::endl;
return -1;
}
// Call the MATLAB function with the input struct
cppstruct::OutputStruct* output =
libPtr->feval<cppstruct::OutputStruct*>("analyzeData", inputPtr);
if (!output) {
std::cerr << "Failed to obtain valid output from MATLAB function." << std::endl;
return -1;
}
// Print the results
printResults(*output);
}
catch (const std::exception& e) {
std::cerr << "An error occurred: " << e.what() << std::endl;
return -1;
}
return 0;
}
int main(int argc, const char* argv[]) {
int ret = 0;
try {
auto matlabApplication = setup();
ret = matlab::cpplib::runMain(mainFunc, std::move(matlabApplication), argc, argv);
matlabApplication.reset();
return ret;
}
catch (const std::exception& e) {
std::cerr << "An error occurred during setup: " << e.what() << std::endl;
return -1;
}
}
IN_PROCESS
Mode
In the application code, you must start MATLAB Runtime in IN_PROCESS
mode in order to pass C++ structs to MATLAB functions.USING_TYPE_WITH_MATLAB
Macro
The USING_TYPE_WITH_MATLAB
macro is used to inform MATLAB about the custom C++ types that will be utilized within the integration. The general form of this macro isUSING_TYPE_WITH_MATLAB(namespace::structure_name, "interface_name")
, wherenamespace::structure_name
is the fully qualified name of the C++ structure, andinterface_name
is the name of the interface defined for MATLAB. This setup ensures that MATLAB correctly recognizes and handles these structures during function calls. For instance, in the provided code, the macro is used to map the cppstruct::OutputStruct
,cppstruct::InputStruct
, andcppstruct::FieldStats
types to the interface named data
.matlab::cpplib::MATLABLibrary::feval
Method
When using the strongly typed interface, you include the generated header file (.hpp
file) in your application code, allowing you to directly call MATLAB functions defined in the (.ctf
file) from your C++ code. However, when passing C++ structures to deployed MATLAB functions, you cannot call these functions directly. Instead, you must use the matlab::cpplib::MATLABLibrary::feval method in your C++ code to evaluate and execute the MATLAB functions.
2. Compile and link the application by executing the mbuild function at the MATLAB command prompt.
mbuild -v PassCppStructConsoleApp.cpp -outdir output\bin
Visual Studio
If you are using Visual Studio for the integration phase, follow the steps in Set Up Microsoft Visual Studio for C++ Development (Windows Only). Additionally, you need to make some changes to ensure your debug build uses the same C/C++ Runtime Library as MATLAB. To do this:
- Open Visual Studio and navigate to your project in theSolution Explorer.
- Right-click on your project name and selectProperties from the context menu.
- In the properties window, navigate to Configuration Properties > C/C++ >Preprocessor.
Remove_DEBUG
from thePreprocessor Definitions list. - Navigate to Configuration Properties >C/C++ > Code Generation. Change the Runtime Library setting to Multi-threaded DLL (/MD).
Handle Code Archive (.ctf
file)
To ensure your C++ application can access the code archive (.ctf
file) containing MATLAB code, place the file in a location accessible to the executable. For this example we are going to do this by setting theCPPSHARED_BASE_CTF_PATH
environment variable in the MATLAB desktop environment.
setenv("CPPSHARED_BASE_CTF_PATH","P:\MATLAB\work\output\v2\generic_interface")
If you're using Visual Studio, see Set Environment Variables in Visual Studio.
For a complete list of code archive (.ctf
file) placement options, see Code Archive (.ctf file) Placement.
Run C++ Application
For testing purposes, you can run the application from MATLAB command prompt. This does not require a MATLAB Runtime installation.
!output\bin\PassCppStructConsoleApp.exe
Executing MATLAB function ... Temperatures - Mean: 70.8000, Std: 2.7749, Max: 75.0000 Pressures - Mean: 29.9400, Std: 0.2702, Max: 30.2000
See Also
clibgen.buildInterface | compiler.build.cppSharedLibrary | arguments | matlab::cpplib::MATLABLibrary::feval