Main Content

C++ の制限を回避する例

使用しているコンパイル済みライブラリに、C++ ライブラリに対する MATLAB® インターフェイスでサポートされていないデータ型または言語機能が含まれている場合、ラッパー ヘッダー ファイルを作成することにより、この機能を含めることができる場合があります。このトピックと、制限に関する C++ インターフェイス回避方法の記事では、一部の制限に関する例を提供します。詳細については、C/C++ サポートに関する制限を参照してください。

Windows® で回避方法の例を実行するには、次を行います。

  • C++ ヘッダー ファイルのステートメントを .hpp ファイルにコピーします。

  • ソース コードを .cpp ファイルにコピーし、Windows でのコンパイル済みライブラリ ファイルのビルド例の手順を使用してビルドします。

  • MATLAB コードを実行して、ライブラリ定義を生成します。

  • 必要に応じて、ライブラリ定義ファイルを編集します。

  • MATLAB コードを実行して、パッケージ フォルダー内にインターフェイスをビルドします。

  • 依存ライブラリ ファイルがある場合はパッケージ フォルダーにコピーします。

  • MATLAB コードを実行して、機能をテストします。

std 名前空間のクラス オブジェクト

C++ ライブラリに対する MATLAB インターフェイスには、std 名前空間で定義されたオブジェクトを使用する関数が含まれていません。たとえば、次のヘッダー ファイル Student.hpp 内の関数は、std::stack オブジェクトを渡します。MATLAB インターフェイスをビルドする場合、関数 readStudents および getStudents は含まれません。

#ifndef STUDENT_HEADER
#define STUDENT_HEADER

#include <stack>

#ifdef _WIN32
#ifdef EXPORT
#define DLL_EXPORT __declspec(dllexport)
#else
#define DLL_EXPORT __declspec(dllimport)
#endif
#else
#define DLL_EXPORT __attribute__((visibility ("default")))
#endif

class DLL_EXPORT Student {
    int rollNumber;
public:
    Student();
    Student(int rollNo);
    int getRollNumber();
};

DLL_EXPORT void readStudents(const std::stack<Student>& students);

DLL_EXPORT std::stack<Student> getStudents(int size);

#endif
  1. この例を Windows で実行するには、Windows でのコンパイル済みライブラリ ファイルのビルド例を使用して、ソース ファイル Student.cpp からファイル Student.lib および Student.dll を作成します。

    #define EXPORT
    
    #include "Student.hpp"
    
    
    Student::Student() : rollNumber(0) {}
    
    Student::Student(int rollNo) : rollNumber(rollNo) {}
    
    int Student::getRollNumber() {
        return rollNumber;
    }
    
    DLL_EXPORT void readStudents(const std::stack<Student>& students) {
    }
    
    DLL_EXPORT std::stack<Student> getStudents(int size) {
        std::stack<Student> students;
        for (int i = 0; i < size; i++) {
            students.push(Student(i+1));
        }
        return students;
    }
  2. Student.hpp ヘッダー ファイルを作成します。

  3. std::stack オブジェクトを表すクラス CStack を作成します。この定義をヘッダー ファイル CStack.hpp 内に配置します。

    //Wrapper header to access/pass std::stack objects from MATLAB
    #ifndef stack_header
    #define stack_header
    
    #include <stack>
    #include <stdexcept>
    
    template<typename T>
    class CStack {
        std::stack<T> data;
    public:
        CStack() {}
        
        // This parameterized constructor is required for the wrapper functions
        // and is not included in the MATLAB interface
        CStack(const std::stack<T>& d):data(d) {} 
    
        // Function to access the topmost element in stack
        T* get() {
            if (data.empty())
                throw std::runtime_error("Retrieving element from Empty Stack");
            return &data.top();
        }
        
        // Function to remove elements from stack
        void remove() { 
            if (data.empty())
                throw std::runtime_error("Stack is empty");
            data.pop();
        }
        
        // Function to add elements to stack
        void add(const T* element) { 
            data.push(*element);
        }
        
        // This method is required for the wrapper functions, and
        // is not included in the MATLAB interface
        const std::stack<T>& getData() const{ 
            return data;
        }
    };
    #endif
  4. CStack クラスを使用して関数 readStudents を呼び出す関数 readStudentsWrapper と、関数 getStudents を呼び出す関数 getStudentsWrapper を定義します。これらの関数を StudentWrapper.hpp という名前のラッパー ヘッダー ファイルに含めます。

    //Header to call readStudents and getStudents functions from MATLAB
    #include "Student.hpp"
    #include "CStack.hpp"
    
    //wrapper function to access the function that accepts std::stack input
    void readStudentsWrapper(const CStack<Student>& students) { 
        readStudents(students.getData());
    }
    
    //wrapper function to access the function that returns the std::stack
    CStack<Student> getStudentsWrapper(int size) { 
        auto students = getStudents(size);
        CStack<Student> cstackStudents(students);
        return cstackStudents;
    }
  5. stack という名前のパッケージ内にライブラリ定義を生成します。

    clibgen.generateLibraryDefinition( ...
        ["Student.hpp","StudentWrapper.hpp"], ...
        PackageName="stack", ...
        Libraries="Student.lib", ...
        TreatObjectPointerAsScalar=true, ...
        Verbose=true);
    Warning: File 'manifest.json' not found. 
    Warning: Some C++ language constructs in the header file are not supported and not imported.
    
    Did not add 'readStudents' at Student.hpp:24.
      Type 'stack' is from std namespace or system header and is not supported.
    
    Did not add 'getStudents' at Student.hpp:26.
      Type 'stack' is from std namespace or system header and is not supported.
    
    Did not add constructor to class 'CStack<Student>' at CStack.hpp:16.
      Type 'stack' is from std namespace or system header and is not supported.
    
    Did not add member 'getData' to class 'CStack<Student>' at CStack.hpp:39.
      Type 'stack' is from std namespace or system header and is not supported.
     
    Using MinGW64 Compiler (C++) compiler.
    Generated definition file definestack.m and data file 'stackData.xml' 
    contain definitions for 13 constructs supported by MATLAB.
    Build using build(definestack).

    Did not add のメッセージを無視します。MATLAB で、readStudentsgetStudents の代わりに関数 readStudentsWrappergetStudentsWrapper を呼び出します。クラス CStack のコンストラクターとメンバー getData は内部で使用され、インターフェイスから呼び出すことはできません。

  6. インターフェイスをビルドします。

    build(definestack)
    addpath('stack')
  7. ライブラリ ファイル Student.dllstack フォルダーにコピーします。

  8. readStudentsWrapper を呼び出します。

    studentsStack = clib.stack.CStack_Student_;
    studentsStack.add(clib.stack.Student(1))
    studentsStack.add(clib.stack.Student(2))
    studentsStack.add(clib.stack.Student(3))
    clib.stack.readStudentsWrapper(studentsStack)
  9. getStudentsWrapper を呼び出します。

    clear studentsStack;
    studentsStack = clib.stack.getStudentsWrapper(3);
    student = studentsStack.get; % returns topmost element from studentStack
    studentsStack.remove % removes topmost element of stack

インスタンス化が不完全か不足しているクラス テンプレート

C++ ライブラリに対する MATLAB インターフェイスは、インスタンス化されていないテンプレート クラスをサポートしていません。たとえば、次のヘッダー ファイル Templates.hpp 内の Pairs クラスはインスタンス化されていません。

// Header for Template class
#ifndef templates_Header
#define templates_Header

template <class T>
class Pairs { 
public:
    T val1;
    T val2;
    Pairs() : val1(0), val2(0) {}
    Pairs(T first, T second) : val1(first), val2(second) {}
    T getVal1() {
        return val1;
    }
    T getVal2() {
        return val2;
    }
};
#endif
  1. Pairs クラスを含めるには、次のラッパー ヘッダー ファイル TemplatesWrapper.hpp を作成します。Pairsint および double のデータ型をサポートすると仮定します。

    //Wrapper to instantiate template class Pairs
    #include "Templates.hpp"
    
    /* Data types that will be used for Pairs class. */
    template class Pairs<int>;
    template class Pairs<double>;
  2. ライブラリ定義を生成します。

    clibgen.generateL ibraryDefinition( ...
        ["TemplatesWrapper.hpp","Templates.hpp"], ...
        PackageName="Templates", ...
        Verbose=true)
    Generated definition file defineTemplates.m and data file 'TemplatesData.xml' 
    contain definitions for 16 constructs supported by MATLAB.
    Build using build(defineTemplates).
  3. インターフェイスをビルドします。

    build(defineTemplates)
    addpath('Templates')
  4. Pairs オブジェクトを作成し、関数 getVal1 および getVal2 を呼び出します。

    Pairs1 = clib.Templates.Pairs_int_(2,3);
    Val1 = Pairs1.getVal1;
    Pairs2 = clib.Templates.Pairs_double_(4.5,10.9);
    Val2 = Pairs2.getVal2;
    Pairs3 = clib.Templates.Pairs_int_(4.3,10.9);
    Val2 = Pairs3.getVal2;

プリプロセッサ命令

C++ ライブラリに対する MATLAB インターフェイスは、プリプロセッサ命令 (マクロ) をサポートしていません。たとえば、次のヘッダー ファイル Area.hpp は、マクロ PI を定義します。MATLAB インターフェイスをビルドする場合、PI は含められません。

//Header with Macro preprocessor directive
#ifndef area_header
#define area_header

#ifdef _WIN32
#ifdef EXPORT
#define DLL_EXPORT __declspec(dllexport)
#else
#define DLL_EXPORT __declspec(dllimport)
#endif
#else
#define DLL_EXPORT __attribute__((visibility ("default")))
#endif

#define PI 3.1415   

DLL_EXPORT double getArea(int radius, double piVal);

#endif
  1. この例を Windows で実行するには、Windows でのコンパイル済みライブラリ ファイルのビルド例を使用して、ソース ファイル Area.cpp からファイル Area.lib および Area.dll を作成します。

    #define EXPORT
    
    #include "Area.hpp"
    
    DLL_EXPORT double getArea(int radius, double piVal) {
        return piVal*radius*radius;
    }
  2. Area.hpp ヘッダー ファイルを作成します。

  3. PI を含めるには、プリプロセッサ命令の値を取得する関数 getPI を定義する次のラッパー ヘッダー ファイル WrapperPI.hpp を作成します。

    //Wrapper to access the preprocessor directive value
    #include "Area.hpp"
    
    double getPI(){ //Wrapper function retrieves the value of PI
        return PI;
    }
  4. Area という名前のパッケージ内にライブラリ定義を生成します。

    clibgen.generateLibraryDefinition( ...
        ["Area.hpp","WrapperPI.hpp"], ...
        PackageName="Area", ...
        Libraries="Area.lib", ...
        TreatObjectPointerAsScalar=true, ...
        Verbose=true)
  5. インターフェイスをビルドします。

    build(defineArea)
    addpath('Area')
  6. ライブラリ ファイル Area.dllArea フォルダーにコピーします。

  7. getArea を呼び出します。

    pi = clib.Area.getPI;
    area = clib.Area.getArea(2,pi)
    area =
    
       12.5660

string 配列

C++ ライブラリに対する MATLAB インターフェイスは、ベクトルの要素型について引数 std::string をもつ関数を含みません。たとえば、次のヘッダー ファイル StringVector.hpp 内の関数 readStringVectorgetStringVector は、MATLAB インターフェイスのビルド時に含められません。

//Header file which accepts and return the vector of std::string
#ifndef stringVec_header
#define stringVec_header
#include <vector>
#include <string>

#ifdef _WIN32
#ifdef EXPORT
#define DLL_EXPORT __declspec(dllexport)
#else
#define DLL_EXPORT __declspec(dllimport)
#endif
#else
#define DLL_EXPORT __attribute__((visibility ("default")))
#endif

DLL_EXPORT void readStringVector(const std::vector<std::string>& stringVector); //readStringVector function gets dropped as vector of std::string input is not supported in MATLAB C++ interface.

DLL_EXPORT std::vector<std::string> getStringVector(int size); //getStringVector function gets dropped as return type of vector of std::string is not supported for MATLAB C++ Interface

#endif
  1. この例を Windows で実行するには、Windows でのコンパイル済みライブラリ ファイルのビルド例を使用して、ソース ファイル StringVector.cpp からファイル StringVector.lib および StringVector.dll を作成します。

    #define EXPORT
    
    #include "StringVector.hpp"
    
    DLL_EXPORT void readStringVector(const std::vector<std::string>& stringVector) {
    }
    
    DLL_EXPORT std::vector<std::string> getStringVector(int size) {
        std::vector<std::string> stringVector;
        for (int i = 0; i < size; i++) {
           stringVector.push_back(("string"+ std::to_string(i+1)));
        }
        return stringVector;
    }
  2. StringVector.hpp ヘッダー ファイルを作成します。

  3. std::string 型の std::vector をサポートするには、MATLAB とライブラリとの間で std::vector を受け渡す次のメソッドを定義する CVector クラスを作成します。

    • パラメーター化されたコンストラクター:

      CVector(const std::vector<T>& d): data(d)
    • 移動コンストラクター:

      CVector(std::vector<T>&& d) : data(std::move(d))
    • インデックス位置の要素にアクセスするメソッド:

      T get(int index)
    • ベクトルに要素を追加するメソッド:

      void add(const T& element)
    • ベクトルからデータを取得するメソッド:

      const std::vector<T>& getData() const

    CVector.hpp は次のクラスを定義します。

    //Wrapper header to access/pass std::vector from MATLAB
    #ifndef cvector_header
    #define cvector_header
    
    #include <vector>
    
    template<typename T>
    class CVector {
        std::vector<T> data;
    public:
        CVector() {}
        CVector(const std::vector<T>& d): data(d) {}
        CVector(std::vector<T>&& d) : data(std::move(d)) {}
        T get(int index) { 
            return data.at(index-1);
        }
        void add(const T& element) { 
            data.push_back(element);
        }
        const std::vector<T>& getData() const {
            return data;
        }
    };
    
    #endif
  4. readStringVectorgetStringVector を含めるには、CVector の引数を C++ ライブラリのメソッドに渡すメソッドを定義するヘッダー ファイル WrapperStringVector.hpp を作成します。

    // Header that allows the readStringVector and getStringVector functions 
    // to be accessed from MATLAB interface
    #include "StringVector.hpp"
    #include "CVector.hpp"
    #include <string>
    
    void wrapperReadStringVector(const CVector<std::string>& stringVec) { 
        readStringVector(stringVec.getData());
    }
    
    CVector<std::string> wrapperGetStringVector(int size) { 
        auto strVec = getStringVector(size);
        CVector<std::string> cvecString(strVec);
        return cvecString;
    }
  5. StringVector という名前のフォルダー内にライブラリ定義を生成します。

    clibgen.generateLibraryDefinition( ...
        ["StringVector.hpp","WrapperStringVector.hpp"], ...
        PackageName="StringVector", ...
        Libraries="StringVector.lib", ...
        TreatObjectPointerAsScalar=true, ...
        Verbose=true)
  6. インターフェイスをビルドします。

    build(defineWrapperStringVector)
    addpath('WrapperStringVector')
  7. ライブラリ ファイル StringVector.dllStringVector フォルダーにコピーします。

  8. 関数を呼び出します。

    % Instantiate the CVector class
    stringVectorObj = clib.StringVector.CVector_std____cxx11__basic_string_char_Std__char_traits_c;
    
    % Add elements to vector
    stringVectorObj.add("Jack");
    stringVectorObj.add("John");
    stringVectorObj.add("Joe");
    
    % Call function with std::string vector input with CVector
    clib.StringVector.wrapperReadStringVector(stringVectorObj);
    clear stringVectorObj;

Windows でのコンパイル済みライブラリ ファイルのビルド例

Windows のコマンド プロンプトで、MinGW-w64 コンパイラへのパスをシステム パスに追加します。たとえば、コンパイラが mingwpathname にある場合は、次を入力します。

set mingwpath = 'mingwpathname';
set PATH=%mingwpath%;%PATH%

C++ ソース ファイルの場所に移動します。

以下のコマンドを実行して、ソース ファイル source.cpp からライブラリ ファイルを生成します。

g++ -c source.cpp -o source.obj -std=c++11
g++ -shared -o source.dll source.obj -Wl,--out-implib,source.lib

関連するトピック