最近在寫D3D9模擬D3D10接口的渲染系統(tǒng)中碰到大量的渲染狀態(tài)對象,不僅成員多,枚舉也多的要命。
struct CORE_API RasterizerState : ResourceHandle
{
eFillMode mFillMode;
eCullMode mCullMode;
bool mFrontFaceCCW;
float mDepthBias;
float mSlopeScaledDepthBias;
bool mDepthClipEnable;
bool mScissorEnable;
bool mMultisampleEnable;
RasterizerState();
};
而要從配置文件中讀取數(shù)據(jù)并填充到這個結(jié)構(gòu)體,對于C++來說完全就是吃力不討好的,寫出來的代碼也是極為過程,修改和擴(kuò)展極為麻煩的。
因此決定使用反射的方法來填充數(shù)據(jù),先總結(jié)一下我的C++反射系統(tǒng)
class RTTIObject // 動態(tài)類型識別對象基類,對象通過一些宏后可以很方便的通過字符串創(chuàng)建出類實例,并且可以查詢注冊時的類型和其他綁定信息
class NameRef // 名字表,類似于虛幻中的FName,可以定義Const和普通Name,比較和拷貝只是一個dword耗費的時間
value_parse,value_tostring,value_typename // 一系列類型模板函數(shù),提供對類型的ToString,Parse及類型名查詢
首先需要處理的是枚舉查詢,這里將枚舉通過宏做成一個個枚舉對象,并可以通過名字創(chuàng)建實例
#define DECLARE_ENUMOBJECT( TEnum ) \
struct EnumObject_##TEnum : EnumObject\
{\
DECLARE_RTTIOBJECT( EnumObject_##TEnum );\
EnumObject_##TEnum( );\
};
#define IMPLEMENT_ENUMOBJECT_BEGIN( TEnum, TEnum_prefixoffset, TMember_prefixoffset ) \
IMPLEMENT_RTTIOBJECT_STRING( EnumObject_##TEnum, #TEnum + TEnum_prefixoffset, #TEnum + TEnum_prefixoffset, "EnumObject" )\
EnumObject_##TEnum::EnumObject_##TEnum(){ const int member_prefixoffset = TMember_prefixoffset;
#define ENUMOBJECT_ADD( enumkey ) AddMember( #enumkey + member_prefixoffset, (dword)enumkey );
#define IMPLEMENT_ENUMOBJECT_END }
#define ENUMOBJECT_STATICINIT( TEnum ) EnumObject_##TEnum::StaticInit();
EnumObject 中通過宏將枚舉的名稱和值保存在這個對象中
IMPLEMENT_ENUMOBJECT_BEGIN( eFillMode, 1, 3 ) // 這里的1,3是將eFillMode及FM_Point轉(zhuǎn)成字符串后去掉前綴
ENUMOBJECT_ADD( FM_Point )
ENUMOBJECT_ADD( FM_Line )
ENUMOBJECT_ADD( FM_Fill )
IMPLEMENT_ENUMOBJECT_END
// 注冊到RTTIObject系統(tǒng)
ENUMOBJECT_STATICINIT( eFillMode )
// 通過枚舉對象可以查找到字符串對應(yīng)的值
dword v;
EnumObject::GetEnumValue( "FillMode", "Point", v )
下一步是將結(jié)構(gòu)體成員信息記錄
void SettingObject::BindMember( const NameRef& objname, void* instancePtr, void* dataPtr, SettingProxy* proxy )
{
proxy->mOffset = dword(dataPtr) - dword(instancePtr);
MemberList& memberlist = mSettingMap[ objname ];
memberlist[ proxy->mName ] = proxy;
}
這里記錄的是結(jié)構(gòu)體成員的內(nèi)存偏移
使用大量的宏,可以讓結(jié)構(gòu)體綁定變得漂亮
#define BIND_SETTINGOBJECT_BEGIN( TClass ) \
{ const NameRef& soname = TClass::StaticGetClassInfo()->mClassName;TClass soobj;
#define BIND_SO_MEMBER( TMemberType, TMember ) \
so.BindMember( soname, &soobj, &soobj.TMember, new TSettingElement<TMemberType>(#TMember + 1 ) );
#define BIND_SO_MEMBER_NAME( TMemberType, TMember, TName ) \
so.BindMember( soname, &soobj, &soobj.TMember, new TSettingElement<TMemberType>(TName) );
#define BIND_SO_ENUM( TEnumType, TMember ) \
so.BindMember( soname, &soobj, &soobj.TMember, new TSettingEnum(#TMember + 1, #TEnumType + 1) );
#define BIND_SO_ENUM_NAME( TEnumType, TMember, TName ) \
so.BindMember( soname, &soobj, &soobj.TMember, new TSettingEnum(TName, #TEnumType + 1) );
#define BIND_SETTINGOBJECT_END }
綁定代碼如下
BIND_SETTINGOBJECT_BEGIN( RasterizerState )
BIND_SO_ENUM ( eFillMode , mFillMode )
BIND_SO_ENUM ( eCullMode , mCullMode )
BIND_SO_MEMBER ( bool , mFrontFaceCCW )
BIND_SO_MEMBER ( float , mDepthBias )
BIND_SO_MEMBER ( float , mSlopeScaledDepthBias)
BIND_SO_MEMBER ( bool , mDepthClipEnable)
BIND_SO_MEMBER ( bool , mScissorEnable)
BIND_SO_MEMBER ( bool , mMultisampleEnable)
BIND_SETTINGOBJECT_END
所有結(jié)構(gòu)體的信息被記錄在SettingObject中,讀取配置文件填充結(jié)構(gòu)體的任務(wù)就變得異常的簡單了
SettingObject settings;
// 將所有的結(jié)構(gòu)體信息記錄
InitRenderStateObjectSetting( settings );
const NameRef& rzname = DepthStencilState::StaticGetClassInfo()->mClassName;
DepthStencilState a;
// 這里就是將配置文件的信息填充到結(jié)構(gòu)體
settings.SetMember( rzname, &a, "BackFace.StencilFunc", "Equal" );