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 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.

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:

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.

  1. In the work folder for this example create a new file named PassCppStructConsoleApp.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:

  1. Open Visual Studio and navigate to your project in theSolution Explorer.
  2. Right-click on your project name and selectProperties from the context menu.
  3. In the properties window, navigate to Configuration Properties > C/C++ >Preprocessor.
    Remove _DEBUG from thePreprocessor Definitions list.
  4. 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

Topics