C++反射を実現(クラス名に基づいてオブジェクトを動的に作成)

5586 ワード

ネット上のほとんどのインプリメンテーションと同様に、ここでもファクトリメソッドを使用してオブジェクトの動的作成を実現します.大まかな原理は、map(クラス名->オブジェクト作成関数)を維持する単一のファクトリクラスを作成することです.オブジェクトを作成すると、クラス名が入力され、その名前に基づいて作成関数がクエリーされ、最後にオブジェクトが作成されます.
この方法では、工場にクラス名を登録することが重要な問題です.各クラス(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 
#include 
#include 

//       
class DynamicObject
{
public:
	DynamicObject() {}
	virtual ~DynamicObject() {}
};

//         
class DynamicFactory
{
public:

	typedef DynamicObject* (*CreateFunction)();

	static DynamicFactory & Instance()
	{
		static DynamicFactory fac;
		return fac;
	}

	//       (    A::B::C    )
	static std::string ReadTypeName(const char * name)
	{
                 //     ,         
                 ...
	}

	bool Regist(const char * name, CreateFunction func)
	{
		if (!func)
		{
			return false;
		}
		std::string type_name = ReadTypeName(name);
		return _create_function_map.insert(std::make_pair(type_name, func)).second;
	}

	DynamicObject * Create(const std::string & type_name)
	{
		if (type_name.empty())
		{
			return NULL;
		}

		std::map<:string createfunction="">::iterator it = _create_function_map.find(type_name);
		if (it == _create_function_map.end())
		{
			return NULL;
		}

		return it->second();
	}

	template
	T * Create(const std::string & type_name)
	{
		DynamicObject * obj = Create(type_name);
		if (!obj)
		{
			return NULL;
		}
		T * real_obj = dynamic_cast(obj);
		if (!real_obj)
		{
			delete obj;
			return NULL;
		}
		return real_obj;
	}

public:

	std::map<:string createfunction=""> _create_function_map;
};

//        
template
class DynamicCreate : public DynamicObject
{
public:
	static DynamicObject * CreateObject()
	{
		return new T();
	}

	struct Registor
	{
		Registor()
		{
			if (!DynamicFactory::Instance().Regist(typeid(T).name(), CreateObject))
			{
				assert(false);
			}
		}

		inline void do_nothing()const { }
	};

	static Registor s_registor;

public:
	DynamicCreate()
	{
		s_registor.do_nothing();
	}

	virtual ~DynamicCreate()
	{
		s_registor.do_nothing();
	}
};

template 
typename DynamicCreate::Registor DynamicCreate::s_registor;

#endif

コードが少ないので、説明しません.
テストコード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
	}