Deploy MATLAB Class That Inherits from MATLAB Handle Class - MATLAB & Simulink (original) (raw)

Data API: MATLAB® Data Array

This example shows how to package a MATLAB class that inherits from a MATLAB handle class and deploy it within a C++ application. It uses the MATLAB Data API for managing data exchange between the MATLAB function and the C++ application. The workflow is supported on Windows®, Linux®, and macOS.

Prerequisites

Handle Class Support

When you deploy a MATLAB class that inherits from a MATLAB handle class, the following functionality is now supported:

Create MATLAB Function

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

classdef BankAccount < handle % BankAccount - A class for managing a bank account. % % This class provides methods to deposit, withdraw, and check the % balance of a bank account. % % BankAccount Properties: % Balance - The current balance of the account (private access). % % BankAccount Methods: % BankAccount - Constructor, initializes account with a balance. % deposit - Deposit money into the account. % withdraw - Withdraw money from the account. % checkBalance - Check the current balance of the account

properties (Access = private)
    Balance (1,1) double {mustBeReal}
end

methods
    % Constructor to initialize the account with a balance
    function obj = BankAccount(initialBalance)
        arguments (Input)
            initialBalance (1,1) double {mustBeReal}
        end
        if nargin == 0
            initialBalance = 0;
        end
        obj.Balance = initialBalance;
    end
    
    % Method to deposit money
    function deposit(obj, amount)
        arguments (Input)
            obj (1,1) BankAccount
            amount (1,1) double {mustBeReal}
        end
        if amount > 0
            obj.Balance = obj.Balance + amount;
        else
            error('Amount must be positive');
        end
    end
    
    % Method to withdraw money
    function withdraw(obj, amount)
        arguments (Input)
            obj (1,1) BankAccount
            amount (1,1) double {mustBeReal}
        end
        if amount <= obj.Balance && amount > 0
            obj.Balance = obj.Balance - amount;
        else
            error('Insufficient funds or invalid amount');
        end
    end
    
    % Method to check the balance
    function bal = checkBalance(obj)
        arguments (Input)
            obj (1,1) BankAccount
        end
        arguments (Output)
            bal (1,1) double {mustBeReal}
        end
        bal = obj.Balance;
    end
end

end

Established MATLAB users may find it unconventional to see a properties block in a class and an arguments block in a method or function, each detailed with data type information. Both blocks let you represent C++ data types with an equivalent MATLAB type. For instance, if your C++ application employs adouble data type representing a value, you can now represent that in MATLAB as a double. You can also specify a MATLAB object as an argument or property type. For example, thewithdraw method specifies BankAccount as a type for one of the input arguments. This option to specify types is useful in situations where a C++ application has strict type requirements. For details, see Data Type Mappings Between C++ and Strongly Typed MATLAB Code.

Test the MATLAB class at the command prompt.

%% Create a new bank account with an initial balance of 100 account = BankAccount(100);

%% Deposit 50 into the account account.deposit(50); disp(['Balance after deposit: ', num2str(account.checkBalance())]);

%% Withdraw 30 from the account account.withdraw(30); disp(['Balance after withdrawal: ', num2str(account.checkBalance())]);

%% Create a joint account that references the same existing account jointAccount = account;

%% Deposit 20 using the shared reference jointAccount.deposit(20); disp(['Balance from sharedAccount: ', num2str(jointAccount.checkBalance())]); disp(['Balance from original account: ', num2str(account.checkBalance())]);

Balance after deposit: 150 Balance after withdrawal: 120 Balance from sharedAccount: 140 Balance from original account: 140

Note

MATLAB

functions that use varargin andvarargout are unsupported.

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("BankAccount.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 BankAccount.ctf BankAccountv2.hpp readme.txt

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

BankAccountv2.hpp

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

class BankAccount : public MATLABHandleObject<MATLABControllerType> {
public:

    friend bool operator==(const BankAccount& A, const BankAccount& B);
    friend bool operator!=(const BankAccount& A, const BankAccount& B);
    friend bool operator<(const BankAccount& A, const BankAccount& B);
    friend bool operator>(const BankAccount& A, const BankAccount& B);
    friend bool operator<=(const BankAccount& A, const BankAccount& B);
    friend bool operator>=(const BankAccount& A, const BankAccount& B);
    // constructors
    BankAccount() : MATLABHandleObject() {}

    BankAccount(std::shared_ptr<MATLABControllerType> _matlabPtr, double initialBalance) :
        MATLABHandleObject()
    {
        m_matlabPtr = _matlabPtr;
        matlab::data::ArrayFactory _arrayFactory;
        std::vector<matlab::data::Array> _args = {
            _arrayFactory.createArray<double>({1,1}, {initialBalance}) };
        matlab::data::Array _result = m_matlabPtr->feval(u"BankAccount", _args);
        m_object = _result;
    }


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

    // properties

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

public:
    template<size_t nargout = 1>
    typename return_type_checkBalance<nargout>::type checkBalance() {
        static_assert(nargout <= 1, "Too many outputs specified. Maximum outputs is 1.");
    }
    void withdraw(double amount) {
        matlab::data::ArrayFactory _arrayFactory;
        std::vector<matlab::data::Array> _args = {
            m_object,
            _arrayFactory.createArray<double>({1,1}, {amount}) };
        m_matlabPtr->feval(u"withdraw", 0, _args);
    }
    void deposit(double amount) {
        matlab::data::ArrayFactory _arrayFactory;
        std::vector<matlab::data::Array> _args = {
            m_object,
            _arrayFactory.createArray<double>({1,1}, {amount}) };
        m_matlabPtr->feval(u"deposit", 0, _args);
    }

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

public:
    template<size_t nargout = 1>
    typename return_type_eq<nargout>::type eq(matlab::data::Array B) {
        static_assert(nargout <= 1, "Too many outputs specified. Maximum outputs is 1.");
    }

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

public:
    template<size_t nargout = 1>
    typename return_type_ne<nargout>::type ne(matlab::data::Array B) {
        static_assert(nargout <= 1, "Too many outputs specified. Maximum outputs is 1.");
    }

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

public:
    template<size_t nargout = 1>
    typename return_type_lt<nargout>::type lt(matlab::data::Array B) {
        static_assert(nargout <= 1, "Too many outputs specified. Maximum outputs is 1.");
    }

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

public:
    template<size_t nargout = 1>
    typename return_type_gt<nargout>::type gt(matlab::data::Array B) {
        static_assert(nargout <= 1, "Too many outputs specified. Maximum outputs is 1.");
    }

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

public:
    template<size_t nargout = 1>
    typename return_type_le<nargout>::type le(matlab::data::Array B) {
        static_assert(nargout <= 1, "Too many outputs specified. Maximum outputs is 1.");
    }

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

public:
    template<size_t nargout = 1>
    typename return_type_ge<nargout>::type ge(matlab::data::Array B) {
        static_assert(nargout <= 1, "Too many outputs specified. Maximum outputs is 1.");
    }

};
template<>
struct BankAccount::return_type_checkBalance<0> { typedef void type; };

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

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

template<>
struct BankAccount::return_type_eq<0> { typedef void type; };

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

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

template<>
struct BankAccount::return_type_ne<0> { typedef void type; };

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

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

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

template<>
struct BankAccount::return_type_lt<0> { typedef void type; };

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

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

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

template<>
struct BankAccount::return_type_gt<0> { typedef void type; };

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

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

template<>
struct BankAccount::return_type_le<0> { typedef void type; };

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

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

template<>
struct BankAccount::return_type_ge<0> { typedef void type; };

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

template<>
void BankAccount::ge<0>(matlab::data::Array B) {
    matlab::data::ArrayFactory _arrayFactory;
    std::vector<matlab::data::Array> _args = {
        m_object,
        B };
    m_matlabPtr->feval(u"ge", 0, _args);
}
template<>
matlab::data::Array BankAccount::ge<1>(matlab::data::Array B) {
    matlab::data::ArrayFactory _arrayFactory;
    std::vector<matlab::data::Array> _args = {
        m_object,
        B };
    matlab::data::Array _result_mda = m_matlabPtr->feval(u"ge", _args);
    matlab::data::Array _result;
    _result = _result_mda;
    return _result;
}
bool operator==(const BankAccount& A, const BankAccount& B) {
    std::vector<matlab::data::Array> _args = { A.m_object, B.m_object };
    matlab::data::TypedArray<bool> _result_mda = A.m_matlabPtr->feval(u"eq", _args);
    bool _result = _result_mda[0];
    return _result;
}
bool operator!=(const BankAccount& A, const BankAccount& B) {
    std::vector<matlab::data::Array> _args = { A.m_object, B.m_object };
    matlab::data::TypedArray<bool> _result_mda = A.m_matlabPtr->feval(u"ne", _args);
    bool _result = _result_mda[0];
    return _result;
}
bool operator<(const BankAccount& A, const BankAccount& B) {
    std::vector<matlab::data::Array> _args = { A.m_object, B.m_object };
    matlab::data::TypedArray<bool> _result_mda = A.m_matlabPtr->feval(u"lt", _args);
    bool _result = _result_mda[0];
    return _result;
}
bool operator>(const BankAccount& A, const BankAccount& B) {
    std::vector<matlab::data::Array> _args = { A.m_object, B.m_object };
    matlab::data::TypedArray<bool> _result_mda = A.m_matlabPtr->feval(u"gt", _args);
    bool _result = _result_mda[0];
    return _result;
}
bool operator<=(const BankAccount& A, const BankAccount& B) {
    std::vector<matlab::data::Array> _args = { A.m_object, B.m_object };
    matlab::data::TypedArray<bool> _result_mda = A.m_matlabPtr->feval(u"le", _args);
    bool _result = _result_mda[0];
    return _result;
}
bool operator>=(const BankAccount& A, const BankAccount& B) {
    std::vector<matlab::data::Array> _args = { A.m_object, B.m_object };
    matlab::data::TypedArray<bool> _result_mda = A.m_matlabPtr->feval(u"ge", _args);
    bool _result = _result_mda[0];
    return _result;
}

Mapping Between MATLAB Class and C++ Header File

MATLAB Class Element C++ Header Elementa
classdef BankAccount < handle class definition inheriting from MATLAB handle class class BankAccount : public MATLABHandleObjectThe concept of a MATLAB handle class is replicated through the use ofMATLABHandleObject in C++.
function obj = BankAccount(initialBalance) constructor BankAccount(std::shared_ptr _matlabPtr, double initialBalance)
function deposit(obj, amount) method void deposit(double amount)
function withdraw(obj, amount) method void withdraw(double amount)
function bal = checkBalance(obj) method matlab::data::Array checkBalance<1>()
Relational operators inherited from MATLAB handle class ==!=<><=>= bool operator==bool operator!=bool operatorbool operator<=bool operator>=
a Input arguments are in C++ data types corresponding to those defined in the MATLAB class' arguments block.

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 BankAccountCppConsoleApp.cpp with the following code.
    BankAccountCppConsoleApp.cpp
#include <iostream>  
#include "MatlabCppSharedLib.hpp"  
#include "P:\MATLAB\work\output\v2\generic_interface\BankAccountv2.hpp"  
void displayBalance(BankAccount& account) {  
    try {  
        auto balance = account.checkBalance<1>();  
        double currentBalance = balance[0];  
        std::cout << "Current balance: $" << currentBalance << std::endl;  
    }  
    catch (const std::exception& e) {  
        std::cerr << "Error while checking balance: " << e.what() << std::endl;  
    }  
}  
// 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;  
}  
// 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"BankAccount.ctf");  
        std::shared_ptr<MATLABControllerType> matlabPtr(std::move(libPtr));  
        //Create a bank account object with an initial balance of 100  
        BankAccount account(matlabPtr, 100.0);  
        std::cout << "Created a bank account with an initial balance of $100" << std::endl;  
        //Deposit 50 into the account  
        account.deposit(50.0);  
        std::cout << "Deposited $50" << std::endl;  
        displayBalance(account);  
        //Withdraw 30 from the account  
        account.withdraw(30.0);  
        std::cout << "Withdrew $30" << std::endl;  
        displayBalance(account);  
        // Created a joint account using that references the same existing account  
        BankAccount jointAccount = account;  
        std::cout << "Created a joint account" << std::endl;  
        // Check if the accounts are the same (showcase == operator)  
        bool sameAccount = account == jointAccount;  
        std::cout << "Are the account and joint account the same? " << std::boolalpha << sameAccount << std::endl;  
        // Deposit 20 into the joint account  
        jointAccount.deposit(20.0);  
        // Original account balance  
        std::cout << "Original account:" << std::endl;  
        displayBalance(account);  
        // Joint account balance  
        std::cout << "Joint account:" << std::endl;  
        displayBalance(jointAccount);  
    }  
    catch (const std::exception& exc) {  
        std::cerr << "Error: " << 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;  
}  
  1. Compile and link the application by executing the mbuild function at the MATLAB command prompt.
    mbuild -v BankAccountCppConsoleApp.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\BankAccountCppConsoleApp.exe

Created a bank account with an initial balance of $100 Deposited $50 Current balance: $150 Withdrew $30 Current balance: $120 Created a joint account Are the account and joint account the same? true Original account: Current balance: $140 Joint account: Current balance: $140

See Also

arguments | properties

Topics