Use MATLAB Handle Classes in C++ - MATLAB & Simulink (original) (raw)

Overview

The MATLABĀ® engine API for C++ supports MATLAB handle classes when using the strongly typed interface. This feature translates the behavior of MATLAB classes that inherit from handle classes into equivalent C++ code, preserving specific features and behaviors. (since R2024a)

Key Features

When you generate a C++ header file from a MATLAB class that inherits from a handle class using the matlab.engine.typedinterface.generateCPP function, you get this functionality:

It is important to note that in the context of the MATLAB engine API for C++, the only functionalities currently supported are copy behavior, comparison operators, and the isvalid function.

Inherent Capabilities of MATLAB Handle Classes

Deriving from the MATLAB handle class enables a subclass to:

Example Files

These example files demonstrate the use and integration of MATLAB handle classes with C++ applications using the engine API for C++:

#include "MatlabEngine.hpp"  
#include "P:\MATLAB\work\stronghandle\BankAccount.hpp"  // Include generated header file  
int main() {  
    // Connect to MATLAB  
    std::shared_ptr<matlab::engine::MATLABEngine> matlabPtr = matlab::engine::startMATLAB();  
    //matlab::data::ArrayFactory factory;  
    //matlabPtr->feval(u"addpath", 0, {factory.createScalar<std::u16string>(u"P:\\MATLAB\\work\\stronghandle") });  
    // add "P:\\MATLAB\\work\\stronghandle" using feval to the MATLAB path  
    matlabPtr->eval(u"addpath('P:\\MATLAB\\work\\stronghandle')");  
    try {  
        // Create a BankAccount object with an initial balance of 100  
        BankAccount myAccount(matlabPtr, 100.0);  
        // Deposit 50 into the account  
        myAccount.deposit(50.0);  
        std::cout << "Deposited $50" << std::endl;  
        // Withdraw 30 from the account  
        myAccount.withdraw(30.0);  
        std::cout << "Withdrew $30" << std::endl;  
        // Check the balance  
        matlab::data::Array balance = myAccount.checkBalance<1>();  
        double currentBalance = balance[0];  
        std::cout << "Current balance: $" << currentBalance << std::endl;  
        // Createa a joint account using that references the same existing account  
        BankAccount jointAccount = myAccount;  
        //Deposit 20 using the shared reference  
        jointAccount.deposit(20.0);  
        // Check the balance  
        balance = myAccount.checkBalance<1>();  
        currentBalance = balance[0];  
        matlab::data::Array jointBalance = jointAccount.checkBalance<1>();  
        double currentJointBalance = jointBalance[0];  
        std::cout << "Deposited $20 into the joint account" << std::endl;  
        std::cout << "Current balance: $" << currentBalance << std::endl;  
        std::cout << "Current joint balance: $" << currentJointBalance << std::endl;  
    }  
    catch (const std::exception& e) {  
        std::cerr << "Error: " << e.what() << std::endl;  
        return -1;  
    }  
    return 0;  
}  

MATLAB BankAccount Class

BankAccount.m

classdef BankAccount < handle 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
        bal = obj.Balance;
    end
end

end

Class definition: The MATLAB class BankAccount is defined as a subclass of the handle class, which allows it to exhibit reference behavior. This means instances of this class can be passed by reference.

Private properties: The class has one private property,Balance, which is a double.

Methods: The class includes methods for depositing, withdrawing, and checking the balance. These methods ensure controlled access and modification of theBalance property.

Generated C++ BankAccount Header

BankAccount.hpp

/* File: BankAccount.hpp
*
* MATLAB Strongly Typed Interface Version: R2024b
* C++ source code generated on: 20-Dec-2023
*/
#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;
}

Class inheritance: The C++ BankAccount class does not explicitly inherit from a standard C++ class that mimics a MATLAB handle class. However, it inherits fromMATLABHandleObject. This custom class is intended to provide similar reference-type behavior as a MATLAB handle class.

Constructors and overloaded operators: The MATLABHandleObject C++ class includes constructors and overloaded operators (==,!=, <, >,<=, and >=). These operators provide similar functionalities to the MATLAB handle class, which supports comparison operations.

bool operator==(const BankAccount& A, const BankAccount& B);
bool operator!=(const BankAccount& A, const BankAccount& B);
bool operator<(const BankAccount& A, const BankAccount& B);
bool operator>(const BankAccount& A, const BankAccount& B);
bool operator<=(const BankAccount& A, const BankAccount& B);
bool operator>=(const BankAccount& A, const BankAccount& B);

Method mapping: The C++ methods (deposit,withdraw, and checkBalance) correspond to the MATLAB class methods. These methods interact with MATLAB using feval, which evaluates MATLAB functions from C++.

MATLAB Signature C++ Signature
function deposit(obj, amount) void deposit(double amount) {
function withdraw(obj, amount) void withdraw(double amount) {
function bal = checkBalance(obj) template<> void BankAccount::checkBalance<0>() {

Inheritance from MATLAB Handle Class

In MATLAB, inheriting from the handle class allows objects to be passed by reference. It also comes with built-in comparison operators.

In the C++ representation, the concept of a handle is replicated through the use of MATLABHandleObject and std::shared_ptr. The comparison operators are explicitly defined to mimic the behavior of a MATLAB handle class in C++.

classdef BankAccount < handle class BankAccount : public MATLABHandleObject