明天就要返校了,学校安装校园网估计又要等一个星期,郁闷,今天下午无聊,给C++实现了一个“反射”机制,仅限于对属性的反射。包括:枚举属性,访问属性。
在C++中原本不存在属性这个概念的,这么优秀的思想没有实现其实是很可惜的。
其实给C++实现反射,本身无形之中就会带来效率的降低,以及元数据的存在导致的体积的增大,但是使用得当,却可以减轻很多问题,总的来说,也有好的一面,应该积极开发与利用。C/C++的RTTI虽然支持的很少,但强大的宏依然是一把利剑。好好利用这把利剑,再强大的神都无法阻碍它的前进。
软件工业发展这么多年来,都普遍知道了大量使用全局变量不是好事。而面向对象程序设计的发明引入了一个概念,将数据封装到类中,从而能够更加方便的管理数据。但是,通常的C/C++之类的通过成员函数来修改/访问数据使得数据不便于管理。对数据的访问不够一致,这样和使用全局变量大同小异。而Delphi率先采用了一种“智能数据”封装技术,将对数据的访问提供了一种统一的方法,以便防止数据的滥用。
而在C++的成员函数中,通过方法提供对数据统一的访问也不是不可以实现,例如如下代码:
DWORD& CTest::ID()
{
return m_dwID;
}
这样,通过函数ID即可随时访问m_dwID,修改都可以。但是这样只是一个简单的实现,相对于C#中的getter, setter而言,还是幼稚了一点。而在managed C++中,引入了新的关键字__property,以便能让其生成的MSIL代码和其他语言生成的能够兼容,其实看来,似乎没有什么实际作用,只是在编译成managed code的时候加入一些元数据来描述其为属性的getter, setter:
__property DWORD get_ID()
{
return m_dwID;
}
所有支持强大RTTI的语言,都无一例外使用了一定元数据来描述一个属性。所以,依照这个思路,要给C++安装属性,就需要设计一个元数据结构,这么一个元数据结构至少要包含这些特性:属性名,属性类型,Getter函数,Setter函数,由于属性可能不止一个,所以采用链表来保存多个属性元数据结构,还要在元数据中加入指向下一个元数据的地址字段,由于属性可以不同为一个类型,所以这里使用模板结构,而这里假设属性方法的原形何Delphi中的属性read/write方法一样,setter有一个参数没返回值,getter没有参数有返回值。由于模板主要在编译期间给一些类或函数函数确定使用参数类型,在运行期间没有区别,所以作为链表节点,还需要一个关键部位和该结构一模一样的结构:
typedef struct tagCPropertyBase
{
LPCSTR szName;
LPCSTR szType;
tagCPropertyBase *lpNext;
}CPropertyBase;
所以模板结构开头也这个样子:
template<typename C,typename T>
struct CPropertyInfo
{
LPCSTR szName;
LPCSTR szType;
CPropertyBase *lpNext;
void (C::*Setter)(T);
T (C::*Getter)();
CPropertyInfo(LPCSTR szName,LPCSTR szType,void (C::*Setter)(T),T (C::*Getter)())
{
this->szName=szName;
this->szType=szType;
this->Setter=Setter;
this->Getter=Getter;
this->lpNext=NULL;
//初始化含有属性类的链表头节点
if(C::lpFirstProperty==NULL)
{
C::lpFirstProperty=(CPropertyBase*)this;
}
//将当前属性信息添加到链表结尾
if(C::lpLastProperty!=NULL)
{
C::lpLastProperty->lpNext=(CPropertyBase*)this;
}
C::lpLastProperty=(CPropertyBase*)this;
};
};
然后就是定义类属性的相关的宏和相关的函数,今天多写点代码少说点屁话,看我Blog的都差不多是看的懂的人,外行话就少说点:
用来确定链表头尾:
#define REFLECT_DECLARE() static CPropertyBase* lpFirstProperty; static CPropertyBase* lpLastProperty;
初始化链表头尾:
#define REFLECT_IMPLEMENT(C) CPropertyBase* C::lpFirstProperty=NULL; CPropertyBase* C::lpLastProperty=NULL;
定义属性元数据:
#define PROPERTY_DECLARE(C,T,P) static struct CPropertyInfo<C,T> Prop_##P; void set_##P(T); T get_##P();
初始化属性元数据:
#define PROPERTY_IMPLEMENT(C,T,P) struct CPropertyInfo<C,T> C::Prop_##P(#P,#T,C::set_##P,C::get_##P);
访问属性元数据:
#define PROPERTY(C,P) &(C::Prop_##P)
修改属性:
#define SET_PROP(O,P,V) (O->*P->Setter)(V)
读取属性:
#define GET_PROP(O,P) (O->*P->Getter)()
根据属性名查找属性:
template<typename C,typename T>
CPropertyInfo<C,T>* FindProperty(LPCSTR szName)
{
CPropertyBase* Item=C::lpFirstProperty;
while(Item)
{
if(!strcmp(Item->szName,szName))
return reinterpret_cast<CPropertyInfo<C,T>*>(Item);
Item=Item->lpNext;
}
return NULL;
}
修改属性:
template<typename C,typename T>
inline void SetProperty(C*Instance,CPropertyInfo<C,T>*Prop,T __value)
{
(Instance->*Prop->Setter)(__value);
}
读取属性:
template<typename C,typename T>
inline T GetProperty(C*Instance,CPropertyInfo<C,T>*Prop)
{
if(Prop->Getter)
return (Instance->*Prop->Getter)();
return (T)0;
}
来测试一下,定义一个包含属性的类:
class CTest
{
private:
int m_ID;
char m_Name[100];
public:
REFLECT_DECLARE();
PROPERTY_DECLARE(CTest,int,ID);
PROPERTY_DECLARE(CTest,LPSTR,Name);
};
REFLECT_IMPLEMENT(CTest);
PROPERTY_IMPLEMENT(CTest,int,ID);
PROPERTY_IMPLEMENT(CTest,LPSTR,Name);
实现:
void CTest::set_ID(int __value)
{
m_ID=__value;
}
int CTest::get_ID()
{
return m_ID;
}
void CTest::set_Name(LPSTR __value)
{
strcpy(m_Name,__value);
}
LPSTR CTest::get_Name()
{
return m_Name;
}
然后测试一下:
void main()
{
CTest t;
CPropertyInfo<CTest,int>* ID=FindProperty<CTest,int>("ID");
CPropertyInfo<CTest,LPSTR>* Name=PROPERTY(CTest,Name);
//修改属性
SET_PROP(&t,ID,1234);
SetProperty(&t,Name,(LPSTR)"hello");
//读取属性
cout<<GetProperty(&t,ID)<<endl<<GET_PROP(&t,Name)<<endl;
//枚举所有属性,和属性类型
CPropertyBase* Item;
for(Item=CTest::lpFirstProperty;Item;Item=Item->lpNext)
{
cout<<Item->szType<<" "<<Item->szName<<endl;
}
}
输出结果:
1234
hello
int ID
LPSTR Name