使用 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 使用 varargin 和 varargout 的函数不受支持。
使用 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 标头中,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++ 应用程序代码作为指南。
在此示例的
work文件夹中,创建一个名为DistanceConsoleApp.cpp的新文件,并包含以下代码。当您的 MATLAB 函数中不包含详细说明类型信息的
arguments模块时,您的 C++ 应用程序代码必须按如下方式编写:在 C++ 应用程序代码中,包含具有类型信息的参数块和不包含类型信息
arguments模块之间的区别在于 MATLAB 函数的输入和输出的规范。当包含详细说明类型信息的arguments模块时,允许使用 C++ 标准库和原始类型进行输入和输出规范。另一方面,当不包括详细说明类型信息的arguments模块时,输入和输出必须定义为matlab::data::Array对象。通过在 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.exeEuclidean 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++ 共享库,则需要使用mwArrayAPI。但是,该 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 数组转换而来的。给定两个新点
p1和p2分别表示为int32数组,其值分别为[0,0]和[3, 7],calculateDistanceMATLAB 函数计算这两个点之间的欧几里得距离。在这种特殊情况下,计算出的距离是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 中指定数据类型,开发人员可以简化两种语言之间的接口过程。
另请参阅
mbuild | compiler.build.cppSharedLibrary | arguments