Reflection in C++
前幾天看一個java演示的設計模式講解視頻,裏面提到了java中的反射機制,感覺反射這個東西很有意思,於是心血來潮想寫一個C++中的反射。
反射機制可以簡單地理解爲:當需要一個類型的實例時,根據類型的名稱就能得到一個實例。如下面的函數,
void* CreateClassInstance(const char *name);
這個函數可以根據傳入的類型名稱創建一個實例。
設計這樣一個系統,我們需要對類型的名稱和創建實例的方法(如構造函數)進行存儲。當然,函數的返回值最好有實際意義,而不是一個無意義的void*。我們給可以被反射的類型取名叫ReflectableClass,ReflectableClass作爲可被反射類型的基類。在這個class中需要對類型的名稱和創建實例的方法進行存儲,以便需要創建實例時可以進行查找。這裏我們用map存儲一個“鍵/值”對,“鍵”爲類型的名稱,“值”爲創建實例的方法。
static std::map<std::string, FUNC_CREATE_INSTANCE> s_reflectors;
FUNC_CREATE_INSTANCE的定義如下,它是一個函數指針,所指向的函數不帶參數。
typedef ReflectableClass* (*FUNC_CREATE_INSTANCE)();
這樣便有了如下的類型聲明:
class ReflectableClass { public: ReflectableClass() {} virtual ~ReflectableClass() {}
virtual const char* GetClassName() = 0;
static ReflectableClass* CreateClassInstance(const char* name); static bool AddInstanceCreator(const char*, FUNC_CREATE_INSTANCE);
private: static std::map<std::string, FUNC_CREATE_INSTANCE> s_reflectors; }; |
GetClassName用來返回每種可被反射的類型的名稱,將其聲明爲純虛函數,因爲ReflectableClass被設計爲一個抽象的類型。
CreateClassInstance用來根據傳入的類型名稱創建並返回一個實例。
AddInstanceCreator用來註冊一個新類型。
當我們定義一個新的類型時,如定義一個Human類型,需要有種方式將類型的相關信息自動註冊到ReflectableClass中。我們可以爲新類型定義一個static的成員,在這個成員被初始化時將類型的信息註冊制ReflectableClass中。定義一個新的類ClassInfo來完成這個工作。
class ClassInfo { public: ClassInfo(const char* name, FUNC_CREATE_INSTANCE creator); ~ClassInfo(); };
ClassInfo::ClassInfo(const char* name, FUNC_CREATE_INSTANCE creator) { ReflectableClass::AddInstanceCreator(name, creator); }
ClassInfo::~ClassInfo() {} |
Human的定義如下:
class Human: public ReflectableClass { public: Human(); virtual ~Human();
virtual const char* GetClassName(); static ReflectableClass* CreateInstance();
private: static ClassInfo s_classInfoHuman;
}; |
GetClassName是從ReflectableClass中繼承並override的虛函數。CreateInstance用來創建Human的實例,即一個FUNC_CREATE_INSTANCE指向的函數。s_classInfoHuman用來註冊Human的信息至ReflectableClass中。實現部分的代碼如下:
ClassInfo Human::s_classInfoHuman(“Human”, Human::CreateInstance);
const char* Human::GetClassName() { return “Human”; }
ReflectableClass* Human::CreateInstance() { return new(std::nothrow) Human; } |
這樣我們就完成了一個Human的定義和註冊。當需要對Human進行反射時,只需調用“ReflectableClass::CreateClassInstance(“Human”)”即可。這裏需要注意一個問題,需確保“ReflectableClass:: s_reflectors”的初始化工作在“Human::s_classInfoHuman”初始化前完成,否則會產生問題。我們可以將ReflectableClass和ClassInfo的定義和實現放在一個動態連接庫中,當定義新類型時加載該庫文件。
爲方便在新類型中聲明和實現static ClassInfo的對象及GetClassName、CreateInstance兩個函數,可以定義一些自動化的宏。
完整的代碼如下:
ReflectableClass.h
ReflectableClass.cpp
Human.h
Human.cpp