Deploy MATLAB Function That Accepts Struct Array as Input Argument - MATLAB & Simulink (original) (raw)

This example shows how to package a MATLAB® function that accepts a struct array as input and deploy it to an application written in C++. The workflow is supported on Windows®, Linux®, and macOS systems.

For a discussion on how to work with MATLAB struct array based on this example, see Work with MATLAB Structure Arrays.

Prerequisites

Create MATLAB Function

Create a MATLAB file named analyzeData.m with the following code:

function outputStruct = analyzeData(inputStruct) % This function takes a MATLAB struct 'inputStruct' as input, performs % statistical analysis on each numeric field, and returns a struct % 'outputStruct' containing the results of these analyses. Non-numeric % fields in the input struct are ignored. % % Inputs: % inputStruct - Struct with fields containing numeric data. % % Outputs: % outputStruct - Struct with the same fields as 'inputStruct'. Each % field is a struct with 'mean', 'std', and 'max' % of the corresponding field in 'inputStruct'. %

arguments (Input) inputStruct (1,1) struct end

% Initialize outputStruct outputStruct = struct();

% Get field names from the input struct fields = fieldnames(inputStruct);

% Loop over each field and perform analysis for i = 1:length(fields) fieldName = fields{i};

% Ensure the field contains numeric data
if isnumeric(inputStruct.(fieldName))
    % Calculate mean
    outputStruct.(fieldName).mean = mean(inputStruct.(fieldName));

    % Calculate standard deviation
    outputStruct.(fieldName).std = std(inputStruct.(fieldName));

    % Calculate max value
    outputStruct.(fieldName).max = max(inputStruct.(fieldName));
else
    warning('Field %s is not numeric and was skipped.', fieldName);
end

end end

Established MATLAB users may find the presence of an arguments block unconventional. Thearguments block lets you represent C++ data types with an equivalent MATLAB type.

Test the MATLAB function at the command prompt.

data = struct(); data.temperatures = [72, 75, 69, 68, 70]; data.pressures = [30, 29.5, 30.2, 29.9, 30.1]; output = analyzeData(data) output.temperatures(:) output.pressures(:)

output = struct with fields:

temperatures: [1×1 struct]
   pressures: [1×1 struct]

ans = struct with fields:

mean: 70.8000
 std: 2.7749
 max: 75

ans = struct with fields:

mean: 29.9400

Package MATLAB Function Using compiler.build.cppSharedLibrary

Create a code archive (.ctf file) and header (.hpp file) from the MATLAB function using the compiler.build.cppSharedLibrary function.

buildResults = compiler.build.cppSharedLibrary("analyzeData.m", ... OutputDir=".\output", Verbose="on");

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 and the corresponding header (.hpp file). 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

To finalize integration, you need the analyzeData.ctf code archive file and the analyzeDatav2.hpp header file from thegeneric_interface folder. You can view the header file here:

analyzeDatav2.hpp

/* File: .\output\analyzeDatav2.hpp *

#include "MatlabTypesInterface.hpp" #include

template struct return_type_analyzeData { typedef void type; };

template typename return_type_analyzeData::type analyzeData(std::shared_ptr _matlabPtr, matlab::data::Array inputStruct) { static_assert(nargout<=1, "Too many outputs specified. Maximum outputs is 1."); } template<> struct return_type_analyzeData<0> { typedef void type; };

template<> struct return_type_analyzeData<1> { typedef matlab::data::Array type; };

template<> void analyzeData<0>(std::shared_ptr _matlabPtr, matlab::data::Array inputStruct) { matlab::data::ArrayFactory _arrayFactory; std::vectormatlab::data::Array _args = { inputStruct }; _matlabPtr->feval(u"analyzeData", 0, _args); }

template<> matlab::data::Array analyzeData<1>(std::shared_ptr _matlabPtr, matlab::data::Array inputStruct) { matlab::data::ArrayFactory _arrayFactory; std::vectormatlab::data::Array _args = { inputStruct }; matlab::data::Array _result_mda = _matlabPtr->feval(u"analyzeData", _args); matlab::data::Array _result; _result = _result_mda; return _result; }

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 header (.hpp file) 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 StructConsoleApp.cpp with the following code.
    StructConsoleApp.cpp

#include
#include
#include "MatlabCppSharedLib.hpp"
#include "P:\MATLAB\work\output\v2\generic_interface\analyzeDatav2.hpp"
#include
// Start MATLAB Runtime, initialize it, and return an object to it
std::shared_ptrmatlab::cpplib::MATLABApplication setup() {
auto mode = matlab::cpplib::MATLABApplicationMode::IN_PROCESS;
std::vectorstd::u16string options = { u"-nojvm" };
std::shared_ptrmatlab::cpplib::MATLABApplication matlabApplication =
matlab::cpplib::initMATLABApplication(mode, options);
return matlabApplication;
}
// Function to print the MATLAB computation results
void printMatlabResults(matlab::data::Array outputArray) {
matlab::data::StructArray structArray = outputArray;
auto topLevelStructFieldNamesIterable = structArray.getFieldNames();
std::vectormatlab::data::MATLABFieldIdentifier
topLevelStructFieldNames(topLevelStructFieldNamesIterable.begin(), topLevelStructFieldNamesIterable.end());
for (const matlab::data::MATLABFieldIdentifier& fieldName : topLevelStructFieldNames) {
std::string outerFieldName(fieldName);
std::cout << "Field: " << outerFieldName << std::endl;
matlab::data::TypedArrayRefmatlab::data::Struct nestedStruct = outputArray[0][fieldName];

    auto nestedStructFieldNamesIterable = nestedStruct.getFieldNames();  
    std::vector<matlab::data::MATLABFieldIdentifier>  
        nestedStructFieldNames(nestedStructFieldNamesIterable.begin(), nestedStructFieldNamesIterable.end());  
    for (const matlab::data::MATLABFieldIdentifier& fieldName : nestedStructFieldNames) {  
        std::string innerFieldName(fieldName);  
        matlab::data::TypedArrayRef<double> fieldValue = nestedStruct[0][fieldName];  
        double value = fieldValue[0];  
        std::cout << "  " << innerFieldName << ": " << std::fixed << std::setprecision(4) << value << std::endl;  
    }  
}  

}
// Initialize the code archive (.ctf file), specify input arguments, call the MATLAB function,
// and print the result
int mainFunc(std::shared_ptrmatlab::cpplib::MATLABApplication app, int argc, const char* argv[]) {
try {
auto libPtr = matlab::cpplib::initMATLABLibrary(app, u"analyzeData.ctf");
std::shared_ptr matlabPtr(std::move(libPtr));
matlab::data::ArrayFactory factory;
matlab::data::TypedArray temperatures = factory.createArray({ 5 }, { 72, 75, 69, 68, 70 });
matlab::data::TypedArray pressures = factory.createArray({ 5 }, { 30, 29.5, 30.2, 29.9, 30.1 });
matlab::data::StructArray inputStruct = factory.createStructArray({ 1 }, { "temperatures", "pressures" });
inputStruct[0]["temperatures"] = temperatures;
inputStruct[0]["pressures"] = pressures;
matlab::data::Array outputStruct = analyzeData<1>(matlabPtr, inputStruct);
printMatlabResults(outputStruct);
}
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;  
}  

} 2. Compile and link the application by executing the mbuild function at the MATLAB command prompt.
mbuild -v StructConsoleApp.cpp -outdir output\bin

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\StructConsoleApp.exe

Field: temperatures mean: 70.8000 std: 2.7749 max: 75.0000 Field: pressures mean: 29.9400 std: 0.2702 max: 30.2000

See Also

compiler.build.cppSharedLibrary

Topics