主要内容

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

使用 MATLAB Data API 将 MATLAB 函数部署到 C++ 应用程序

此示例说明如何打包 MATLAB® 函数并将其部署在 C++ 应用程序中。它使用 MATLAB Data API 来管理 MATLAB 函数和 C++ 应用程序之间的数据交换。该工作流支持 Windows®、Linux®macOS

前提条件

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

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

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

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

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

创建 MATLAB 函数

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

function distance = calculateDistance(p1, p2)
    % This function calculates the Euclidean distance between two points
    % Inputs:
    %   p1 - a two-element vector [x, y]
    %   p2 - a two-element vector [x, y]
    % Output:
    %   distance - the Euclidean distance between p1 and p2
    
    % Use arguments block to map C++ type to corresponding MATLAB type
    % std::vector<int32_t> <--> (1,2) int32 {mustBeReal}
    
    arguments (Input)
        p1 (1,2) int32 {mustBeReal}
        p2 (1,2) int32 {mustBeReal}
    end

    arguments (Output)
        distance (1,1) int32 {mustBeReal}
    end
    
    % Calculte Euclidean distance
    diff = p1 - p2;
    diffSq = diff.^2;
    sumSq = sum(diffSq);
    distance = sqrt(sumSq);
end

资深 MATLAB 用户可能会发现 arguments 模块的存在不太寻常。arguments 模块允许您用等效的 MATLAB 类型表示 C++ 数据类型。例如,如果您的 C++ 应用程序采用 int32_t 数据类型来表示一个值,那么您现在可以在 MATLAB 中将其表示为 int32。当 C++ 应用程序具有严格的类型要求时,此选项很有用。有关详细信息,请参阅C++ 和强类型 MATLAB 代码之间的数据类型映射

在这个示例中,具有指定类型信息的 arguments 模块用于阐明细微的差别。但是,请记住,合并 arguments 模块完全是可选的。即使没有它,部署过程仍然保持不变。这个示例的各个部分强调了这种差异的体现方面。因此,如果数据类型不是很重要,则不需要使用 arguments 模块来指定类型信息。

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

p1 = int32([0, 0])
p2 = int32([3 4])
distance = calculateDistance(p1,p2)
p1 =
  1×2 int32 row vector
   0   0
p2 =
  1×2 int32 row vector
   3   4
distance =
  int32
   5

注意

MATLAB

使用 vararginvarargout 的函数不受支持。

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

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

buildResults = compiler.build.cppSharedLibrary("calculateDistance.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
            calculateDistance.ctf
            calculateDistancev2.hpp
            readme.txt

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

 calculateDistancev2.hpp

calculateDistancev2.hpp 标头中,MATLAB 函数的 int32 参量规范反映了其 C++ 等效项 int32_t

    arguments (Input)
        p1 (1,2) int32 {mustBeReal}
        p2 (1,2) int32 {mustBeReal}
    end
   
std::vector<int32_t> p1, 
std::vector<int32_t> p2)
    arguments (Output)
        distance (1,1) int32 {mustBeReal}
    end
template<>
struct return_type_calculateDistance<1> { typedef int32_t type; };
...
...
matlab::data::Array _result_mda = _matlabPtr->feval(u"calculateDistance", _args);
int32_t _result;
_result = MatlabTypesInterface::convertMDAtoScalar<int32_t>(_result_mda);

当您的 MATLAB 函数中未包含详细说明类型信息的 arguments 模块时,将导致生成以下头文件:

 calculateDistancev2.hpp(与类型无关)

头文件之间的主要区别在于输入和输出变量的类型规范。当使用指定类型信息的 arguments 模块时,输入和输出分别归类为 std::vector<int32_t>int32_t。相反,当不存在 arguments 模块时,输入和输出将接收 matlab::data::Array 对象类型指定。

注意

生成的工件不包括 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 文件夹中,创建一个名为 DistanceConsoleApp.cpp 的新文件,并包含以下代码。

     DistanceConsoleApp.cpp

    当您的 MATLAB 函数中不包含详细说明类型信息的 arguments 模块时,您的 C++ 应用程序代码必须按如下方式编写:

     DistanceConsoleApp.cpp(与类型无关)

    在 C++ 应用程序代码中,包含具有类型信息的参数块和不包含类型信息 arguments 模块之间的区别在于 MATLAB 函数的输入和输出的规范。当包含详细说明类型信息的 arguments 模块时,允许使用 C++ 标准库和原始类型进行输入和输出规范。另一方面,当不包括详细说明类型信息的 arguments 模块时,输入和输出必须定义为 matlab::data::Array 对象。

  2. 通过在 MATLAB 命令提示符下执行 mbuild 函数来编译和链接应用程序。

    mbuild -v DistanceConsoleApp.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\DistanceConsoleApp.exe
Euclidean distance between [0, 0] and [3, 4] is: 5

使用 MATLAB Data API 部署到 C++ 的精妙之处

  • 使用 MATLAB Data API 将 MATLAB 代码部署到 C++ 应用程序时,MATLAB Compiler SDK 不会生成 C++ 共享库。compiler.build.cppSharedLibrary 函数或 C++ 共享库编译器的主要输出是一个代码存档(.ctf 文件)和一个头文件(.hpp 文件)。如果您希望 MATLAB Compiler SDK 从 MATLAB 代码生成 C++ 共享库,则需要使用 mwArray API。但是,该 API 使用的是较旧的 C++03 标准,缺少基于较新的 C++11 标准构建的 MATLAB Data API 提供的许多现代功能。

  • 自 R2021b 起,MATLAB Compiler SDK 已支持参量类型指定,尽管仅限于输入参量。在 MATLAB 函数中,输出参量的数据类型源自函数内部各种函数的类型规范和交互。因此,输出类型始终映射到 C++ 中的 matlab::data::Array 对象。

  • 自 R2023b 起,输入和输出参量均支持参量类型指定。在 MATLAB 函数内部,各种函数的类型规范和交互继续决定最终的输出类型。但是,当此输出返回到 C++ 应用程序时,它会被转换为 C++ 应用程序所需的类型。此转换在生成的标头(.hpp 文件)中管理。

    考虑 calculateDistance 文件中的 calculateDistance.m 函数。此函数执行四个操作来计算欧几里得距离:

    diff = p1 - p2;
    diffSq = diff.^2;
    sumSq = sum(diffSq);
    distance = sqrt(sumSq);

    当向此函数提供两个输入时,例如 p1 = int32([0, 0])p2 = int32([3 4])int32 类型将一直保持,直到 sum 函数执行为止。执行后,sumSq 变为 double 类型。因此,当 sqrt 执行时,输出参量 distance 也变为 double

    查看 calculateDistancev2.hpp 标头的最后几行:

    matlab::data::Array _result_mda = _matlabPtr->feval(u"calculateDistance", _args);
    int32_t _result;
    _result = MatlabTypesInterface::convertMDAtoScalar<int32_t>(_result_mda);
    return _result;

    检查 calculateDistancev2.hpp 文件的最后几行时,我们观察到对 MATLAB 函数 fevalcalculateDistance 调用。此函数调用的结果存储在变量 _result_mda 中,它是 matlab::data::Array 类型的对象。

    随后,将新变量 _result 声明为 int32_t 类型。在下一行中,_result_mda 变量(最初是 matlab::data::Array 类型)转换为 int32_t。此转换是通过 convertMDAtoScalar<int32_t>() 函数实现的,该函数是 MatlabTypesInterface 库的一部分。

    最后,函数返回 _result。此返回值是一个标量整数 (int32_t),它是从 MATLAB 数组转换而来的。

    给定两个新点 p1p2 分别表示为 int32 数组,其值分别为 [0,0][3, 7]calculateDistance MATLAB 函数计算这两个点之间的欧几里得距离。在这种特殊情况下,计算出的距离是 7.6158,一个 double 数据类型的值。

    然而,当这个结果返回给 C++应用程序时,会发生类型转换。具体来说,double 结果被转换为 int32_t 类型。经过此转换过程,原始 double 精度值 7.6158 被四舍五入为 8,然后被 C++ 应用程序作为 int32_t 接收。

    这说明了 MATLAB 和 C++ 之间的类型转换如何影响数据的精度。在设计和连接 MATLAB 函数和 C++ 应用程序时,意识到这些潜在的舍入问题至关重要。

  • 重要的是要记住,使用 arguments 模块来指定 MATLAB 函数中的类型信息不是强制性的 - 它是一种仅根据 C++ 应用程序的特定要求来使用的功能。然而,此功能提供了几个不容忽视的好处。

    在 MATLAB 函数中定义类型的能力允许更精确地控制从 MATLAB 函数返回到 C++ 应用程序的数据类型。这减少了 C++ 代码中额外类型转换的需要,而这通常是导致性能效率低下和潜在 bug 的根源。

    本质上,此功能有助于 MATLAB 代码与 C++ 应用程序更顺畅地集成。通过直接在 MATLAB 中指定数据类型,开发人员可以简化两种语言之间的接口过程。

另请参阅

| |

主题