主要内容

本页采用了机器翻译。点击此处可查看英文原文。

部署接受结构体数组作为输入参量的 MATLAB 函数

R2026b

此示例说明如何打包接受结构体数组作为输入的 MATLAB® 函数并将其部署到用 C++ 编写的应用程序。Windows®、Linux® 和 macOS 系统支持此工作流。

有关如何基于此示例使用 MATLAB 结构体数组的讨论,请参阅使用 MATLAB 结构体数组。

前提条件

  • 创建一个对 MATLAB 搜索路径可见的新工作文件夹。此示例使用名为 work 的文件夹。

  • 验证您是否已设置 C++ 开发环境。有关详细信息,请参阅设置 C++开发环境。本示例使用 MATLAB 作为 C++ 开发环境。因此,通过在 MATLAB 命令提示符下键入 mbuild -setup C++ 来验证您是否已安装 C++ 编译器。

  • 验证您是否已满足所有 MATLAB Compiler SDK™ C++ 目标要求。有关详细信息,请参阅MATLAB Compiler SDK C++ 目标要求。

  • 最终用户必须安装 MATLAB Runtime 才能运行应用程序。有关详细信息,请参阅下载并安装 MATLAB Runtime。

    出于测试目的,您可以在运行 C++ 应用程序时使用 MATLAB 安装而不是 MATLAB Runtime。

创建 MATLAB 函数

使用以下代码创建一个名为 analyzeData.m 的 MATLAB 文件:

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

资深 MATLAB 用户可能会发现 arguments 模块的存在不太寻常。arguments 模块允许您用等效的 MATLAB 类型表示 C++ 数据类型。

在命令提示符下测试 MATLAB 函数。

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

使用 compiler.build.cppSharedLibrary 封装 MATLAB 函数

使用 compiler.build.cppSharedLibrary 函数从 MATLAB 函数创建代码存档(.ctf 文件)和头文件(.hpp 文件)。

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

该函数生成一套文件(如下所列),并将它们放在指定的 output 目录中。其中,集成过程中使用的关键文件是包含 MATLAB 代码的代码存档(.ctf 文件)和相应的头文件(.hpp 文件)。有关其他文件的信息,请参阅打包 MATLAB 函数后生成的文件。

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

为了完成集成,您需要来自 analyzeData.ctf 文件夹的 analyzeDatav2.hpp 代码存档文件和 generic_interface 头文件。您可以在此处查看头文件:

/* File: .\output\analyzeDatav2.hpp
*
* MATLAB Strongly Typed Interface Version: R2024a
* C++ source code generated on: 01-Apr-2024
*/
#include "MatlabTypesInterface.hpp"
#include <map>


template<size_t nargout>
struct return_type_analyzeData { typedef void type; };


template<size_t nargout = 1>
typename return_type_analyzeData<nargout>::type 
    analyzeData(std::shared_ptr<MATLABControllerType> _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<MATLABControllerType> _matlabPtr, matlab::data::Array inputStruct) { 
    matlab::data::ArrayFactory _arrayFactory;
    std::vector<matlab::data::Array> _args = {
        inputStruct };
    _matlabPtr->feval(u"analyzeData", 0, _args);
}

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

注意

生成的工件不包括 MATLAB Runtime 或安装程序。要使用 buildResults 对象创建安装程序,请参阅 compiler.package.installer。

将 MATLAB 代码存档集成到 C++ 应用程序中

您可以在首选的 C++ 开发环境中完成集成过程,包括 MATLAB 或 Windows 上的 Microsoft® Visual Studio® 等替代方案。但是,此示例使用 MATLAB 作为 C++ 开发环境。有关详细信息,请参阅设置 C++开发环境。

要将生成的 MATLAB 代码存档(.ctf 文件)和头文件(.hpp 文件)集成到 C++ 应用程序中,请遵循以下准则:

  • 使用 #include 指令将生成的头文件(.hpp 文件)合并到您的 C++ 应用程序代码中。

  • 确保代码存档(.ctf 文件)位于 C++ 可执行文件可以访问的位置。

完成集成步骤需要熟练的 C++ 技能来编写应用程序代码。编写自己的应用程序时,可以使用以下示例 C++ 应用程序代码作为指南。

  1. 在此示例的 work 文件夹中,创建一个名为 StructConsoleApp.cpp 的新文件,并包含以下代码。

    #include <iostream>
    #include <vector>
    #include "MatlabCppSharedLib.hpp"
    #include "P:\MATLAB\work\output\v2\generic_interface\analyzeDatav2.hpp"
    #include <iomanip>
    
    // 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;
        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 printMatlabResults(matlab::data::Array outputArray) {
        matlab::data::StructArray structArray = outputArray;
        auto topLevelStructFieldNamesIterable = structArray.getFieldNames();
        std::vector<matlab::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::TypedArrayRef<matlab::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_ptr<matlab::cpplib::MATLABApplication> app, int argc, const char* argv[]) {
        try {
            auto libPtr = matlab::cpplib::initMATLABLibrary(app, u"analyzeData.ctf");
            std::shared_ptr<MATLABControllerType> matlabPtr(std::move(libPtr));
    
            matlab::data::ArrayFactory factory;
            matlab::data::TypedArray<double> temperatures = factory.createArray<double>({ 5 }, { 72, 75, 69, 68, 70 });
            matlab::data::TypedArray<double> pressures = factory.createArray<double>({ 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. 通过在 MATLAB 命令提示符下执行 mbuild 函数来编译和链接应用程序。

    mbuild -v StructConsoleApp.cpp -outdir output\bin

处理代码存档(.ctf 文件)

为确保您的 C++ 应用程序可以访问包含 MATLAB 代码的代码存档(.ctf 文件),请将该文件放置在可执行文件可访问的位置。对于这个示例,我们将通过在 MATLAB 桌面环境中设置 CPPSHARED_BASE_CTF_PATH 环境变量来实现这一点。

setenv("CPPSHARED_BASE_CTF_PATH","P:\MATLAB\work\output\v2\generic_interface")

如果您使用的是 Visual Studio,请参阅在 Visual Studio 中设置环境变量。

有关代码存档(.ctf 文件)放置选项的完整列表,请参阅代码存档(.ctf 文件)的放置。

运行 C++ 应用程序

为了测试目的,您可以从 MATLAB 命令提示符运行该应用程序。这不需要安装 MATLAB Runtime。

!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

另请参阅

主题