Main Content

Use MATLAB Handle Classes in C++

R2026b

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:

  • Copy behavior: The generated C++ code replicates MATLAB handle class copy behavior. In MATLAB, handle objects are reference types, meaning that when you copy these objects, both the original and the new variable refer to the same object.

  • Comparison operators: The C++ representation of MATLAB handle classes supports comparison operations. You can compare C++ objects, derived from MATLAB handle classes, using the standard operators ==, !=, <, >, <=, and >=.

  • isvalid function support: The C++ interface supports the isvalid function, which checks if handle objects are valid or have been deleted.

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:

  • Inherit methods.

  • Define events and listeners.

  • Define dynamic properties.

  • Implement set and get methods.

  • Customize copy behavior.

Example Files

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

  • BankAccount.m: This MATLAB class file inherits from the handle class and provides basic banking functionality. For more information, see MATLAB BankAccount Class.

  • generateHeader.m: A MATLAB script used to generate the corresponding C++ header file from the BankAccount class, illustrating the integration with the engine API for C++.

    matlab.engine.typedinterface.generateCPP( ...
        "BankAccount.hpp", ...
        Classes="BankAccount")
  • run_BankAccount.m: An example using the BankAccount class.

    %% Create a 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())]);
  • BankConsoleApplication.cpp: This C++ console application demonstrates the use of the generated header file from the MATLAB BankAccount class. It replicates the functionality of the MATLAB script run_BankAccount.m, but within a C++ environment. This application shows key operations such as account creation, deposits, withdrawals, and balance inquiries, mirroring the actions performed in the MATLAB script.

    #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

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 the Balance property.

Generated C++ BankAccount Header

/* 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 from MATLABHandleObject. 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<MATLABControllerType>