C++反射を実現(クラス名に基づいてオブジェクトを動的に作成)
5586 ワード
ネット上のほとんどのインプリメンテーションと同様に、ここでもファクトリメソッドを使用してオブジェクトの動的作成を実現します.大まかな原理は、map(クラス名->オブジェクト作成関数)を維持する単一のファクトリクラスを作成することです.オブジェクトを作成すると、クラス名が入力され、その名前に基づいて作成関数がクエリーされ、最後にオブジェクトが作成されます.
この方法では、工場にクラス名を登録することが重要な問題です.各クラス(Class)に対して、クラス(Class)を定義し、クラス(ClassReg)のコンストラクション関数にクラス(Class)を登録し、クラスのグローバルオブジェクトを定義します.このグローバルオブジェクトが初期化されると、登録コードが実行されて登録が完了します.
ここまで怠け者が嫌がっているのを見て、私たちはクラスを書くたびに、相応の登録クラスを書くのではないでしょうか.そこで,対応するコードをマクロで置き換えることが提案され,重複コード量が大幅に減少した.
マクロを採用するのはもちろんですが、一部の怠け者を満たすしかありません.一部の人はもっと怠け者で、彼らはこのマクロを使って登録を完成することさえしたくありません.そうですね.クラスを書くたびに、クラスの後ろでマクロを使って登録しなければなりません.これは確かに面倒です.また、このようなコードは美しくなく、メンテナンスも容易ではありません.同時にマクロを採用してクラス名を変換し、ネーミングスペースに遭遇し、クラスをネストするのは面倒です.登録するとき、名前を全部書かなければなりません.例えばREG_CLASS(MyNameSpace::MyClass::MyStruct).
では、もっと良い方法はありませんか?もちろん、クラステンプレートを使用してこの機能を実現することができます.この方法を想定して、クラスMyClassを定義し、このようなサポートを動的に作成するには、class MyClass:public DynamicCreate{}を定義するだけでよい.これでずっとはっきりしたのではないでしょうか.コードを参照してください.
DynamicFactory.hファイル
コードが少ないので、説明しません.
テストコードcpp
出力:
上で省略したReadTypeName関数コードを次に示します.
この方法では、工場にクラス名を登録することが重要な問題です.各クラス(Class)に対して、クラス(Class)を定義し、クラス(ClassReg)のコンストラクション関数にクラス(Class)を登録し、クラスのグローバルオブジェクトを定義します.このグローバルオブジェクトが初期化されると、登録コードが実行されて登録が完了します.
ここまで怠け者が嫌がっているのを見て、私たちはクラスを書くたびに、相応の登録クラスを書くのではないでしょうか.そこで,対応するコードをマクロで置き換えることが提案され,重複コード量が大幅に減少した.
マクロを採用するのはもちろんですが、一部の怠け者を満たすしかありません.一部の人はもっと怠け者で、彼らはこのマクロを使って登録を完成することさえしたくありません.そうですね.クラスを書くたびに、クラスの後ろでマクロを使って登録しなければなりません.これは確かに面倒です.また、このようなコードは美しくなく、メンテナンスも容易ではありません.同時にマクロを採用してクラス名を変換し、ネーミングスペースに遭遇し、クラスをネストするのは面倒です.登録するとき、名前を全部書かなければなりません.例えばREG_CLASS(MyNameSpace::MyClass::MyStruct).
では、もっと良い方法はありませんか?もちろん、クラステンプレートを使用してこの機能を実現することができます.この方法を想定して、クラスMyClassを定義し、このようなサポートを動的に作成するには、class MyClass:public DynamicCreate{}を定義するだけでよい.これでずっとはっきりしたのではないでしょうか.コードを参照してください.
DynamicFactory.hファイル
#ifndef __DYNAMIC_FACTORY_H__
#define __DYNAMIC_FACTORY_H__
#ifdef __GNUC__
#include
#endif
#include
#include
#include
#include
コードが少ないので、説明しません.
テストコードcpp
#include
#include "DynamicFactory.h"
class Test1 : public DynamicCreate
{
public:
// : gcc, ,
Test1() {}
};
namespace OK {
struct Test2 : public DynamicCreate
{
Test2() {}
class Test3 : public DynamicCreate
{
public:
Test3() {}
};
};
struct Test4 : public DynamicCreate
{
Test4() {}
};
}
using namespace OK;
//
int main()
{
Test1 * p1 = DynamicFactory::Instance().Create("Test1");
printf("Create Test1 %s
", (p1 ? "success" : "failure"));
OK::Test2 * p2 = DynamicFactory::Instance().Create<:test2>("OK::Test2");
printf("Create OK::Test2 %s
", (p2 ? "success" : "failure"));
OK::Test2::Test3 * p3 = DynamicFactory::Instance().Create<:test2::test3>("OK::Test2::Test3");
printf("Create OK::Test2::Test3 %s
", (p3 ? "success" : "failure"));
OK::Test4 * p4 = DynamicFactory::Instance().Create<:test4>("OK::Test4");
printf("Create OK::Test4 %s
", (p4 ? "success" : "failure"));
return 0;
}
出力:
[programmer@localhost test]$ ./test
Create Test1 success
Create OK::Test2 success
Create OK::Test2::Test3 success
Create OK::Test4 success
上で省略したReadTypeName関数コードを次に示します.
// ( A::B::C )
// GCC type_info::name() , , ( ), ( )、 、
static std::string ReadTypeName(const char * name)
{
#ifndef __GNUC__
const char * p = strstr(name, " ");
if (p)
{
size_t prev_len = (size_t)(p - name);
if (memcmp(name, "class", prev_len) == 0 ||
memcmp(name, "struct", prev_len) == 0 ||
memcmp(name, "enum", prev_len) == 0 ||
memcmp(name, "union", prev_len) == 0)
{
p += 1;
return std::string(p);
}
}
return std::string(name);
#else
char * real_name = abi::__cxa_demangle(name, nullptr, nullptr, nullptr);
std::string real_name_string(real_name);
free(real_name);
return real_name_string;
#endif
}