主要内容

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

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

R2026b

此示例说明如何打包 MATLAB® 包中包含的 MATLAB 类并将其部署到 C++ 应用程序。它使用 MATLAB Data API 来管理 MATLAB 代码和 C++ 应用程序之间的数据交换。MATLAB 类可通过前端的 MATLAB 函数访问。该工作流支持Windows®、Linux® 和 macOS。

前提条件

  • 创建一个对 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。

文件

示例文件的位置

示例文件

matlabroot\extern\examples\compilersdk\c_cpp\strongly_typed

每个示例文件的用途

文件目的
+shapes包含两个类的包:MyPosition.m 和 MyRectangle.m。
MyPosition.m+shapes 包中的类接受一个点的 X 和 Y 坐标并创建一个 MyPosition 对象。
MyRectangle.m+shapes 包中的类接受指定为 MyPosition 对象的两个点并创建一个 MyRectangle 对象。
calculatearea.m接受 MyRectangle 对象作为输入并计算矩形面积的函数。
shapes_mda.cpp集成了打包 MATLAB 代码生成的代码存档(.ctf 文件)头文件(.hpp 文件)的 C++ 应用程序代码。

将示例文件复制到当前工作文件夹。

appDir = fullfile(matlabroot,'extern','examples','compilersdk','c_cpp','strongly_typed');
copyfile(appDir)

创建 MATLAB 函数和类

  1. 检查 MyPosition.m、MyRectangle.m 和 calculatearea.m 的代码。

    • +shapes 包包含两个 MATLAB 类:MyPosition.m 和 MyRectangle.m。

    • 位于 calculatearea.m 包之外的 +shapes MATLAB 函数接受 MyRectangle 对象作为输入并计算矩形的面积。

    +shapes
    |- MyPosition.m
    |- MyRectangle.m
    classdef MyPosition
        properties
            X (1,1) double {mustBeReal}
            Y (1,1) double {mustBeReal}
        end
    end
    
    classdef MyRectangle
        properties
            UpperLeft  (1,1) shapes.MyPosition
            LowerRight (1,1) shapes.MyPosition
        end
        methods
            function R = enlarge(R, n)
                arguments
                    R (1,1) shapes.MyRectangle
                    n (1,1) double {mustBeReal}
                end
                R.UpperLeft.X = R.UpperLeft.X - n;
                R.UpperLeft.Y = R.UpperLeft.Y - n;
                R.LowerRight.X = R.LowerRight.X + n;
                R.LowerRight.Y = R.LowerRight.Y + n;
            end
            function R = show(R)
                arguments
                    R (1,1) shapes.MyRectangle
                end
                disp([R.UpperLeft.X R.UpperLeft.Y R.LowerRight.X R.LowerRight.Y]);
            end
        end
    end
    
    function r = calculatearea(rect)
    
    arguments
        rect (1,1) shapes.MyRectangle
    end
    
    r = (rect.LowerRight.Y - rect.UpperLeft.Y) * (rect.LowerRight.X - rect.UpperLeft.X);
    
    end

    资深 MATLAB 用户可能会发现,在类中看到 properties 模块,在方法或函数中看到 arguments 模块,并且每个块都详细记录数据类型信息,这是很不寻常的。这两个模块都允许您用等效的 MATLAB 类型表示 C++ 数据类型。例如,如果您的 C++ 应用程序采用 double 数据类型来表示一个值,那么您现在可以在 MATLAB 中将其表示为 double。您还可以将 MATLAB 对象指定为参量或属性类型。例如,MyRectangle 类将 shapes.MyPosition 指定为其 UpperLeft 和 LowerRight 属性的 MyRectangle。当 C++ 应用程序具有严格的类型要求时,这个指定类型的选项很有用。有关详细信息,请参阅C++ 和强类型 MATLAB 代码之间的数据类型映射。

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

  2. 使用以下代码创建一个名为 runshapes.m 的 MATLAB 脚本,并在 MATLAB 命令提示符下执行它。该脚本说明了类和函数如何交互以生成输出。

    % Import shapes package
    import shapes.*;
    
    %% Create Rectangle 1 
    % Create MyPosition object for point 1
    p1 = MyPosition; 
    p1.X = 10;
    p1.Y = 5;
    % Create MyPosition object for point 2
    p2 = MyPosition; 
    p2.X = 50;
    p2.Y = 20;
    % Create MyRectangle object
    r1 = MyRectangle; 
    r1.UpperLeft = p1;
    r1.LowerRight = p2;
    % Calculate area of rectangle 1
    a1 = calculatearea(r1);
    % Display rectangle 1 info
    disp('Rectangle 1')
    disp(['Point 1 = ' '(' num2str(p1.X,'%.6f'), ',' num2str(p1.Y,'%.6f') ')'])
    disp(['Point 2 = ' '(' num2str(p2.X,'%.6f'), ',' num2str(p2.Y,'%.6f') ')'])
    disp(['Rectangle ' '(' num2str(p1.X,'%.6f'), ',' num2str(p1.Y,'%.6f') ')',...
        ' -> ' '(' num2str(p2.X,'%.6f'), ',' num2str(p2.Y,'%.6f') ')'])
    
    %% Create Rectangle 2 
    % Create rectangle 2 by enlarging rectangle 1
    r2 = r1.enlarge(10);
    % Get positions of rectangle 2
    q1 = r2.UpperLeft();
    q2 = r2.LowerRight();
    % Calculate area of rectangle 2
    a2 = calculatearea(r2);
    % Display rectangle 2 info
    disp('Rectangle 2')
    disp(['Point 1 = ' '(' num2str(q1.X,'%.6f'), ',' num2str(q1.Y,'%.6f') ')'])
    disp(['Point 2 = ' '(' num2str(q2.X,'%.6f'), ',' num2str(q2.Y,'%.6f') ')'])
    disp(['Rectangle ' '(' num2str(q1.X,'%.6f'), ',' num2str(q1.Y,'%.6f') ')',...
        ' -> ' '(' num2str(q2.X,'%.6f'), ',' num2str(q2.Y,'%.6f') ')'])
    
    %% Display the area of the two rectangles
    disp(['Area of rectangle r1 = ' num2str(a1)])
    disp(['Area of rectangle r2 = ' num2str(a2)])
    runshapes
    Rectangle 1
    Point 1 = (10.000000,5.000000)
    Point 2 = (50.000000,20.000000)
    Rectangle (10.000000,5.000000) -> (50.000000,20.000000)
    Rectangle 2
    Point 1 = (0.000000,-5.000000)
    Point 2 = (60.000000,30.000000)
    Rectangle (0.000000,-5.000000) -> (60.000000,30.000000)
    Area of rectangle r1 = 600
    Area of rectangle r2 = 2100

使用 compiler.build.cppSharedLibrary 创建 C++ 共享库

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

files = ["calculatearea.m", "+shapes"];
buildResults = compiler.build.cppSharedLibrary(files, OutputDir="output", LibraryName="libshapes", Verbose="on")

该函数在您当前工作目录中名为 output 的文件夹中生成以下文件。

P:\MATLAB\WORK\OUTPUT
│   GettingStarted.html
│   includedSupportPackages.txt
│   mccExcludedFiles.log
│   readme.txt
│   requiredMCRProducts.txt
│   unresolvedSymbols.txt
│
└───v2
    └───generic_interface
            libshapes.ctf
            libshapesv2.hpp
            readme.txt

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

#include "MatlabTypesInterface.hpp"
#include <map>

namespace shapes {
    class MyPosition : public MATLABObject<MATLABControllerType> {
    public:

        // constructors
        MyPosition() : MATLABObject() {}

        MyPosition(std::shared_ptr<MATLABControllerType> matlabPtr) :
            MATLABObject(matlabPtr, u"shapes.MyPosition")
        {}

        MyPosition(std::shared_ptr<MATLABControllerType> matlabPtr, 
            std::vector<matlab::data::Array> _args) {
            m_matlabPtr = matlabPtr;
            matlab::data::Array _result = MATLABCallOneOutputMethod(u"shapes.MyPosition", _args);
            m_object = _result;
        }

        MyPosition(std::shared_ptr<MATLABControllerType> matlabPtr, matlab::data::Array obj) :
            MATLABObject(matlabPtr, obj)
        {}

        // properties
        double getX() { return MATLABGetScalarProperty<double>(u"X"); }
        void setX(double value) { return MATLABSetScalarProperty(u"X", value); }
        double getY() { return MATLABGetScalarProperty<double>(u"Y"); }
        void setY(double value) { return MATLABSetScalarProperty(u"Y", value); }

        // methods
    };
}

namespace shapes {
    class MyRectangle : public MATLABObject<MATLABControllerType> {
    public:

        // constructors
        MyRectangle() : MATLABObject() {}

        MyRectangle(std::shared_ptr<MATLABControllerType> matlabPtr) :
            MATLABObject(matlabPtr, u"shapes.MyRectangle")
        {}

        MyRectangle(std::shared_ptr<MATLABControllerType> matlabPtr, 
            std::vector<matlab::data::Array> _args) {
            m_matlabPtr = matlabPtr;
            matlab::data::Array _result = MATLABCallOneOutputMethod(u"shapes.MyRectangle", _args);
            m_object = _result;
        }

        MyRectangle(std::shared_ptr<MATLABControllerType> matlabPtr, matlab::data::Array obj) :
            MATLABObject(matlabPtr, obj)
        {}

        // properties
        shapes::MyPosition getUpperLeft() {
            matlab::data::Array obj = MATLABGetScalarProperty<matlab::data::Array>(u"UpperLeft");
            return shapes::MyPosition(m_matlabPtr, obj);
        }
        void setUpperLeft(shapes::MyPosition obj) {
            MATLABSetScalarProperty(u"UpperLeft", matlab::data::Array(obj));
        }
        shapes::MyPosition getLowerRight() {
            matlab::data::Array obj = MATLABGetScalarProperty<matlab::data::Array>(u"LowerRight");
            return shapes::MyPosition(m_matlabPtr, obj);
        }
        void setLowerRight(shapes::MyPosition obj) {
            MATLABSetScalarProperty(u"LowerRight", matlab::data::Array(obj));
        }

        // methods
    private:
        template<size_t nargout>
        struct return_type_show { typedef void type; };

    public:
        template<size_t nargout = 1>
        typename return_type_show<nargout>::type show() {
            static_assert(nargout <= 1, "Too many outputs specified. Maximum outputs is 1.");
        }

    private:
        template<size_t nargout>
        struct return_type_enlarge { typedef void type; };

    public:
        template<size_t nargout = 1>
        typename return_type_enlarge<nargout>::type enlarge(double n) {
            static_assert(nargout <= 1, "Too many outputs specified. Maximum outputs is 1.");
        }

    };
    template<>
    struct MyRectangle::return_type_show<0> { typedef void type; };

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

    template<>
    void MyRectangle::show<0>() {
        matlab::data::ArrayFactory _arrayFactory;
        std::vector<matlab::data::Array> _args = {
            m_object };
        m_matlabPtr->feval(u"show", 0, _args);
    }

    template<>
    matlab::data::Array MyRectangle::show<1>() {
        matlab::data::ArrayFactory _arrayFactory;
        std::vector<matlab::data::Array> _args = {
            m_object };
        matlab::data::Array _result_mda = m_matlabPtr->feval(u"show", _args);
        matlab::data::Array _result;
        _result = _result_mda;
        return _result;
    }

    template<>
    struct MyRectangle::return_type_enlarge<0> { typedef void type; };

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

    template<>
    void MyRectangle::enlarge<0>(double n) {
        matlab::data::ArrayFactory _arrayFactory;
        std::vector<matlab::data::Array> _args = {
            m_object,
            _arrayFactory.createArray<double>({1,1}, {n}) };
        m_matlabPtr->feval(u"enlarge", 0, _args);
    }

    template<>
    matlab::data::Array MyRectangle::enlarge<1>(double n) {
        matlab::data::ArrayFactory _arrayFactory;
        std::vector<matlab::data::Array> _args = {
            m_object,
            _arrayFactory.createArray<double>({1,1}, {n}) };
        matlab::data::Array _result_mda = m_matlabPtr->feval(u"enlarge", _args);
        matlab::data::Array _result;
        _result = _result_mda;
        return _result;
    }
}

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

template<size_t nargout = 1>
typename return_type_calculatearea<nargout>::type calculatearea(
    std::shared_ptr<MATLABControllerType> _matlabPtr, shapes::MyRectangle rect) {
    static_assert(nargout <= 1, "Too many outputs specified. Maximum outputs is 1.");
}
template<>
struct return_type_calculatearea<0> { typedef void type; };

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

template<>
void calculatearea<0>(std::shared_ptr<MATLABControllerType> _matlabPtr, 
    shapes::MyRectangle rect) {
    matlab::data::ArrayFactory _arrayFactory;
    std::vector<matlab::data::Array> _args = { rect };
    _matlabPtr->feval(u"calculatearea", 0, _args);
}

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

有关 MATLAB 类和函数在头文件中如何映射到 C++ 的详细讨论,请参阅将 MATLAB 类和函数映射到 C++。

有关数据类型映射的详细信息,请参阅C++ 和强类型 MATLAB 代码之间的数据类型映射。

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

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

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

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

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

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

  1. 在本示例的工作文件夹中,创建一个名为 shapes_mda.cpp 的新文件,其中包含以下代码:

    // Include header files
    #include <cmath> // for abs
    #include <iostream>
    #include "MatlabCppSharedLib.hpp"
    #include "output/v2/generic_interface/libshapesv2.hpp"
    
    using namespace shapes;
    
    // 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;
    }
    
    // This is an example of a new function that can be written in the C++ driver code without having to
    // modify the MATLAB code.
    static double calculateperimeter(MyRectangle rectangle)
    {
        // Get the positions of the upper left and lower right corners.
        MyPosition upperLeft = rectangle.getUpperLeft();
        MyPosition lowerRight = rectangle.getLowerRight();
    
        // Calculate the width and height of the rectangle.
        double width = abs(lowerRight.getX() - upperLeft.getX());
        double height = abs(upperLeft.getY() - lowerRight.getY());
    
        // Calculate and return the perimeter.
        return 2 * (width + height);
    }
    
    // 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, const int argc, 
    const char* argv[])
    {
        try {
            auto libPtr = matlab::cpplib::initMATLABLibrary(app, u"libshapes.ctf");
            std::shared_ptr<MATLABControllerType> matlabPtr(std::move(libPtr));
          
            // Create a rectangle from two points
            MyPosition p1(matlabPtr);
            p1.setX(10);
            p1.setY(5);
            std::cout << "Rectangle 1\n";
            p1.show();
            MyPosition p2(matlabPtr);
            p2.setX(50);
            p2.setY(20);
            p2.show();
            MyRectangle r1(matlabPtr);
            r1.setUpperLeft(p1);
            r1.setLowerRight(p2);
            r1.show();
    
            // Call MATLAB function to calculate first rectangle area and print result
            matlab::data::TypedArray<double> area1 = calculatearea(matlabPtr, r1);
            std::cout << "Area of rectangle r1 = " << area1[0] << "\n";
            std::cout << "Perimeter of rectangle r1 = " << calculateperimeter(r1) << std::endl;
            
            // Create second rectangle by enlarging the first one
            MyRectangle r2(matlabPtr, r1.enlarge(10.0));
            MyPosition p1r2(matlabPtr, r2.getUpperLeft());
            MyPosition p2r2(matlabPtr, r2.getLowerRight());
            std::cout << "Rectangle 2\n";
            p1r2.show();
            p2r2.show();
            r2.show();
    
            // Call MATLAB function to calculate second rectangle area and print result
            matlab::data::TypedArray<double> doubles = calculatearea(matlabPtr, r2);
            std::cout << "Area of rectangle r2 = " << doubles[0] << "\n";
            std::cout << "Perimeter of rectangle r2 = " << calculateperimeter(r2) << std::endl;
    
        }
        catch (const std::exception& exc) {
            std::cerr << exc.what() << std::endl;
            return -1;
        }
        return 0;
    }
    
    // Call setup() to initialize MATLAB Runtime, use runMain() to run mainFunc(), 
    // and reset MATLAB Runtime after completion
    int main(const int argc, const char* argv[])
    {
        int ret = 0;
        try {
            auto matlabApplication = setup();
            ret = matlab::cpplib::runMain(mainFunc, std::move(matlabApplication), argc, argv);
            matlabApplication.reset();
        }
        catch (const std::exception& exc) {
            std::cerr << exc.what() << std::endl;
            return -1;
        }
        return ret;
    }

    注意

    在编写 C++ 应用程序代码时,必须包含由 mcc 命令或 C++ 共享库编译器应用程序生成的头文件(.hpp 文件),以及使用 #include 指令包含的 MatlabCppSharedLib.hpp 头文件。请根据需要修改文件的路径。

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

    mbuild -v shapes_mda.cpp -outdir output\bin

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

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

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

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

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

运行 C++ 应用程序

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

!output\bin\shapes_mda.exe
Rectangle 1
Point (10.000000, 5.000000)
Point (50.000000, 20.000000)
Rectangle (10.000000, 5.000000) -> (50.000000, 20.000000)
Area of rectangle r1 = 600
Rectangle 2
Point (0.000000, -5.000000)
Point (60.000000, 30.000000)
Rectangle (0.000000, -5.000000) -> (60.000000, 30.000000)
Area of rectangle r2 = 2100

另请参阅

|

主题