1. 程式人生 > >C++實現反射(根據類名動態建立物件)

C++實現反射(根據類名動態建立物件)

   和網上大多數實現一樣,這裡也是採用工廠方法來實現物件的動態建立。大致原理為,建立一個單例工廠類,其中維護一個map(類名->物件建立函式)。建立物件時,傳入類名,然後根據此類名查詢出建立函式,最後建立物件。

    採用這種方式,有一個關鍵問題,便是在工廠中註冊類名。我們的辦法是針對於每一個類(Class),定義一個註冊類(ClassReg),在註冊類(ClassReg)的建構函式中註冊此類(Class),然後再定義一個註冊類的全域性物件,在該全域性物件初始化時,便會執行註冊程式碼完成註冊。

    看到到這裡懶人不樂意了,難道我們每寫一個類,還要寫一個相應的註冊類?於是有人提出了使用巨集來替換相應的程式碼,這樣便大大減少了重複程式碼量。

    採用巨集當然可以,但是隻能滿足部分懶人。還有一部分人更懶,他們連使用這個巨集完成註冊都不想幹,是呀,每寫完一個類,還要在類後面使用巨集來註冊,這樣確實還是比較麻煩。而且這樣程式碼不夠美觀,也不便於維護,同時採用巨集來轉換類名,遇到名稱空間,巢狀類,會非常麻煩,我們必須在註冊的時候,把名字寫全,比如REG_CLASS(MyNameSpace::MyClass::MyStruct)。

    那麼有沒有更好的方法呢?當然有,我們可以用類模板來實現這種功能。設想這種方式,若我們要定義一個類MyClass,並且想讓此類支援動態建立,那麼我們只需這樣定義即可class MyClass : public DynamicCreate<MyClass>{};。這樣是不是清晰多了?下面請看程式碼:
 

    DynamicFactory.h檔案

#ifndef __DYNAMIC_FACTORY_H__
#define __DYNAMIC_FACTORY_H__

#ifdef __GNUC__
#include <cxxabi.h>
#endif

#include <assert.h>
#include <string.h>
#include <stdlib.h>
#include <map>
#include <string>
#include <typeinfo>

// 動態物件基類
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<std::string, CreateFunction>::iterator it = _create_function_map.find(type_name);
		if (it == _create_function_map.end())
		{
			return NULL;
		}

		return it->second();
	}

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

public:

	std::map<std::string, CreateFunction> _create_function_map;
};

// 動態物件建立器
template<typename T>
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 T>
typename DynamicCreate<T>::Registor DynamicCreate<T>::s_registor;

#endif

 

    程式碼不多,就不做解釋了。

    測試程式碼mian.cpp

 

#include <stdio.h>
#include "DynamicFactory.h"

class Test1 : public DynamicCreate<Test1>
{
public:
	// 注意:使用gcc,一定要顯示申明建構函式,否則不會執行註冊程式碼
	Test1() {}
};

namespace OK {
	struct Test2 : public DynamicCreate<Test2>
	{
		Test2() {}

		class Test3 : public DynamicCreate<Test3>
		{
		public:
			Test3() {}
		};
	};

	struct Test4 : public DynamicCreate<Test4>
	{
		Test4() {}
	};
}

using namespace OK;

//測試程式碼
int main()
{
	Test1 * p1 = DynamicFactory::Instance().Create<Test1>("Test1");
	printf("Create Test1 %s\n", (p1 ? "success" : "failure"));

	OK::Test2 * p2 = DynamicFactory::Instance().Create<OK::Test2>("OK::Test2");
	printf("Create OK::Test2 %s\n", (p2 ? "success" : "failure"));

	OK::Test2::Test3 * p3 = DynamicFactory::Instance().Create<OK::Test2::Test3>("OK::Test2::Test3");
	printf("Create OK::Test2::Test3 %s\n", (p3 ? "success" : "failure"));

	OK::Test4 * p4 = DynamicFactory::Instance().Create<OK::Test4>("OK::Test4");
	printf("Create OK::Test4 %s\n", (p4 ? "success" : "failure"));

	return 0;
}


  輸出:

 

 

[[email protected] 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
	}