blg-017-在中C++グローバルオブジェクトのコンストラクション関数で反射機構を実現

15705 ワード

C++におけるグローバルオブジェクトのコンストラクション関数による反射機構の実現
  • C++でグローバルオブジェクトのコンストラクション関数を用いて反射機構を実現
  • 1. 反射機構
  • 2. 方法概要
  • 3. 反射機構を導入した単純なファクトリモード
  • 3.1工場類
  • 3.1.1 a_factory.hファイル
  • 3.1.2 a_factory.cpp

  • 3.2製品類
  • 3.2.1 a_interface.h
  • 3.2.2 a 1クラス
  • 3.2.2.1 a1.h
  • 3.2.2.1 a1.cpp
  • 3.2.2.3 a2.h
  • 3.2.2.4 a2.cpp

  • 3.2.3 main.cpp
  • 3.2.4 CMakeLists.txt

  • 3.3コンパイル運転
  • 3.3新製品サブクラスの作成方法
  • 3.3.1 a3.h
  • 3.3.2 a3.cpp
  • 3.3.3 CMakeLists.txt
  • 3.3.4コンパイル運転

  • 4. 注意事項

  • 1.反射機構
    Java言語では、いずれのクラスに対しても実行状態で、このクラスのすべての属性と方法を知ることができます.任意のクラスのインスタンスに対しても、その任意のメソッドと属性を呼び出すことができます.このようなクラスの情報を動的に取得し,オブジェクトメソッドを動的に呼び出す機能を反射メカニズムと呼ぶ.
    反射メカニズムは我々の日常的なプログラミングに多く応用されていないが,設計モードでは非常に強力なツールを反射する.特に設計モードにおけるファクトリモードでは,ファクトリクラスにおける反射機構の使用はswitch caseの煩雑な記述とメンテナンスを免除することができる.反射により、文字列でクラス名を指定して特定のプロダクトクラスを作成することが容易になります.ただしC++では、クラスの生成は完全にコンパイル時に指定されます.Javaのように実行時にオブジェクトを動的に作成することはできません.したがって、製品クラスを追加すると、工場クラスの変更は避けられません.これは私たちが望んでいるものではありません.一度修正すると、テストを再開することを意味します.
    したがって、C++をより容易に修正し、テストの作業量を減らすことができる.本論文では,グローバルオブジェクトの構造関数を用いて,ある程度の反射機構を実現する.
    2.方法の概要
    C++言語の特性のため、どのオブジェクトの作成もコンパイル期間中に明確にする必要があります.したがって,ある程度の反射を実現するために,本手法の主な構成は,プログラムMain関数の前にすべてのクラスの情報を1つのデータ構造に登録することである.これにより、Main関数の実行が開始されると、そのデータ構造にアクセスしてすべてのクラスの情報を取得できます.
    C++は関数体外で文を記述することが許されないため,Main関数の実行前にすべてのクラスのリポジトリをどのように構築するかが主な問題である.したがって、グローバルオブジェクトのコンストラクション関数は、私たちのニーズを満たすのに適しています.グローバル・オブジェクトのライフサイクルは、プログラム全体の実行期間に作用するため、グローバル・オブジェクトのメモリ割り当てと初期化は、プログラムが正式に実行される前に行われます.
    3.反射機構を導入する単純工場モード
    3.1工場類
    3.1.1 a_factory.hファイル
    #ifndef _A_FACTORY_
    #define _A_FACTORY_
    
    #include "a_interface.h"
    
    #define REGISTER_TO_A_FACTORY(PRDT, PRDT_NAME) \
        PRDT reg_##PRDT; \
        a_factory fty_##PRDT(PRDT_NAME, (a_interface *)&reg_##PRDT);
    
    class a_factory
    {
        public:
            a_factory(const char *name, a_interface *obj);
    
        public:
            static a_interface * get_a(const char *name);
    };
    
    #endif

    マクロREGISTER_TO_A_FACTORYは、製品クラスを工場に登録するために使用されます.マクロの定義から、各製品クラスについて、その製品クラスのグローバルオブジェクトが明示されていることがわかります.グローバルプロダクトオブジェクトのアドレスとプロダクト名をコンストラクション関数のパラメータとしてグローバルファクトリオブジェクトを宣言した.この工場オブジェクトは、製品オブジェクトの登録を担当します.登録の具体的な実現については3.1.2を参照してください.
    静的メンバー関数get_a登録されたサブクラス(製品)名を指定してサブクラスオブジェクト(製品)を取得するために使用されます.
    3.1.2 a_factory.cpp
    #include "a_factory.h"
    #include 
    #include 
    
    using namespace std;
    
    static struct {
        char name[128] = {0};
        a_interface *ptr = NULL;
    } registation[100];
    static int reg_idx = 0;
    
    a_factory::a_factory(const char *name, a_interface *obj)
    {
        string key(name);
        strcpy(registation[reg_idx].name, name);
        registation[reg_idx].ptr = obj;
        reg_idx++;
    }
    
    a_interface * a_factory::get_a(const char *name)
    {
        a_interface *tgt = NULL, *ret = NULL;
        int i = 0;
    
        for (i=0; iif (strcasecmp(name, registation[i].name) == 0) {
                return (a_interface *)registation[i].ptr->clone();
            }
        }
    
        return NULL;
    }

    グローバル静的変数registationは、登録されたすべてのオブジェクト、グローバル静的変数reg_を格納します.idxは登録された個数を記録する.この例ではregistationは長さ100の配列である.したがって、最大100個のオブジェクトを登録できます.
    3.2製品類
    3.2.1 a_interface.h
    #ifndef _A_INTERFACE_
    #define _A_INTERFACE_
    
    class a_interface 
    {
        public:
            virtual void output() = 0;
    
        public:
            virtual a_interface * clone() = 0;
    };
    
    #endif

    output()関数は主な製品方法であり,サブクラスはリロード後に具体的な製品機能を実現する.clone()関数は、反射メカニズムで自己オブジェクトを作成する方法です.プロダクトサブクラスでは、この2つの関数を実装する必要があります.
    3.2.2 a 1類
    3.2.2.1 a1.h
    #ifndef _A1_
    #define _A1_
    
    #include "a_interface.h"
    
    class a1 : public a_interface
    {
        public:
            virtual void output();
    
        protected:
            virtual a_interface * clone();
    };
    
    #endif

    サブクラス(特定の製品クラス)は、製品ベースクラスを継承し、その特定の製品メソッド、すなわちoutput関数を実現する必要がある.
    3.2.2.1 a1.cpp
    #include 
    #include "a1.h"
    #include "a_factory.h"
    
    using namespace std;
    
    REGISTER_TO_A_FACTORY(a1, "a1");
    
    void a1::output()
    {
        cout<<"This a1
    "
    ; } a_interface *a1::clone() { return (a_interface *) new a1(); }

    サブクラスはCPPファイルでREGISTER_を使用するだけですTO_A_FACTORYマクロは、自分をregistationに登録すればよい.登録プロセスは、Main関数の前に実行されるグローバルオブジェクトのコンストラクション関数によって完了します.
    3.2.2.3 a2.h
    #ifndef _A2_
    #define _A2_
    
    #include "a_interface.h"
    
    class a2 : public a_interface 
    {
        public:
            virtual void output();
    
        protected:
            virtual a_interface * clone();
    };
    
    #endif

    3.2.2.4 a2.cpp
    #include 
    #include "a2.h"
    #include "a_factory.h"
    
    using namespace std;
    
    REGISTER_TO_A_FACTORY(a2, "a2");
    
    void a2::output()
    {
        cout<<"This a2
    "
    ; } a_interface * a2::clone() { return (a_interface *) new a2(); }

    3.2.3 main.cpp
    #include 
    #include "a_factory.h"
    
    using namespace std;
    
    int main(int argc, char **argv)
    {
        a_interface *a = NULL;
    
        if (argc != 2) {
            cout<<"Input Error"<return 1;
        }
    
        a = a_factory::get_a(argv[1]);
        if (a == NULL) {
            cout<<"Not Found"<return 1;
        }
        a->output();
        return 0;
    }

    3.2.4 CMakeLists.txt
    CMAKE_MINIMUM_REQUIRED(VERSION 2.8)
    
    PROJECT(ref)
    
    SET(DO_LIB_SRC ./a_factory.cpp
                    ./a1.cpp
                    ./a2.cpp)
    SET(A_SRC ./main.cpp)
    
    SET(CMAKE_CXX_FLAGS_DEBUG -g)
    
    ADD_LIBRARY(${PROJECT_NAME} STATIC ${DO_LIB_SRC})
    ADD_EXECUTABLE(a ${A_SRC})
    TARGET_LINK_LIBRARIES(a -Wl,--whole-archive ${PROJECT_NAME} -Wl,--no-whole-archive)

    3.3コンパイル運転
    1.       
    $ ls
    a_factory.cpp  a_interface.h  a1.h    a2.h            main.cpp
    a_factory.h    a1.cpp         a2.cpp  CMakeLists.txt
    
    2.    
    $ mkdir build; cd build; cmake ../; make
    
    3.  
    $ ./a.exe a1
    This a1
    $ ./a.exe a2
    This a2
    $ ./a.exe a3
    Not Found

    上記の例では、文字列でサブクラス名を指定するだけで、特定のサブクラスを作成できることがわかります.継承および実装された虚関数を呼び出すことによって、その特定の製品メソッドが実装されます.
    3.3新製品のサブクラスの作成方法
    3.2節では、サブクラス製品a 2の書き方はa 1と類似している.新しい製品サブクラスを追加するには、次の手順に従います.
  • a 3を作成する.h, a3.cppファイル
  • a 3クラスにa_を継承させるinterfaceクラス
  • 製品関数とclone関数
  • を実現
  • はa 3にある.cppファイルでマクロREGISTER_を使用TO_A_FACTORYは、当該製品のサブクラス
  • を登録する
  • はa 3を.cppはCMakeListsに追加する.txtは、以上の手順から、サブクラスを追加する必要がある場合は、サブクラスコードを記述するだけで、既存のコードを変更する必要はありません.

  • 3.3.1 a3.h
    #ifndef _A3_
    #define _A3_
    
    #include "a_interface.h"
    
    class a3 : public a_interface
    {
        public:
            virtual void output();
    
        public:
            virtual a_interface * clone();
    };
    
    
    #endif

    3.3.2 a3.cpp
    #include 
    #include "a_factory.h"
    #include "a3.h"
    
    using namespace std;
    
    REGISTER_TO_A_FACTORY(a3, "a3");
    
    void a3::output()
    {
        cout<<"This is a3
    "
    ; } a_interface * a3::clone() { return (a_interface *) new a3(); }

    3.3.3 CMakeLists.txt
    SET(DO_LIB_SRC ./a_factory.cpp
                    ./a1.cpp
                    ./a2.cpp)
      :
    SET(DO_LIB_SRC ./a_factory.cpp
                    ./a1.cpp
                    ./a2.cpp
                    ./a3.cpp)

    3.3.4コンパイル運転
    1.       
    $ ls
    a_factory.cpp  a_interface.h  a1.h    a2.h    a3.h            main.cpp
    a_factory.h    a1.cpp         a2.cpp  a3.cpp  CMakeLists.txt
    
    2.    
    $ mkdir build; cd build; cmake ../; make
    
    3.  
    $ ./a.exe a1
    This a1
    $ ./a.exe a2
    This a2
    $ ./a.exe a3
    This is a3

    新規のサブ製品クラスa 3が順調に稼働していることがわかります.
    4.注意事項
    工場クラスとそのすべての製品クラスを静的ライブラリとして使用する場合.静的ライブラリ全体をリンクする必要があります.そうしないと、登録はできません.登録用に明示および定義されたグローバル・オブジェクトは外部で使用されていないため、デフォルトのリンク方式を使用すると、これらのオブジェクトはエンド・プログラムに存在しません.したがって、-Wl、–whole-archiveパラメータを使用する必要があります.