反射是指程序在运行时动态获取对象属性与方法的一种机制,即编译器需要将类型信息(属性类型与偏移地址以及成员函数的地址等信息)编译到程序文件中,当程序运行时将这些信息加载到内存中去,做到运行时只根据对象的地址或引用就可以获取到对象的类型信息,从而利用这些信息达到修改或重建对象的目标。
21世纪以前内存与带宽一直是非常昂贵的资源,编译器在生成可执行文件时要兼顾系统内存等硬件资源,为节省成本不会将类型信息加载到运行内存中去。网络传输也要考虑带宽与各节点的性能,往往需要各通讯节点间采用紧致、简单、高效的通讯协议,这期间编程世界是C/C++的天下,网络上传输的是C/C++语言中的结构体,把结构体当作通讯协议具有无需序列化与反序列化的优点,完美契合当时的软硬件环境。
随着时间的推移,硬件性能遵循“摩尔定律”发展,网络带宽也大幅提高,万维网验证了纯文本的http协议是一个简单、易理解的通讯协议,这时软件工程的主要矛盾已不是需求与硬件之间矛盾,而是开发者与不断变更的需求、逐渐多元化的系统之间矛盾,这时开发者需要一个不受语言制约、可读性强、易调试的通讯协议。
由于C/C++中的结构存在可读性差,难以调试,协议升级困难等缺点,导致xml与json等自注释文本协议得以发展普及。但文本协议与对象内存模型存在差异,服务在收到请求后需要将请求报文反序列化为具体对象,业务处理完成后将输出结果序列化为通讯报文在网络上传输,整个处理过程中序列化与反序列化跟业务没什么关系,但开发者需要写大量重复代码去完成序列化与反序列化的工作,Java为解决这个问题添加了反射机制,将类型信息编译到class文件中,程序启动时将类型信息加载到内存,运行时可以动态获取、修改对象的属性名称与取值,从而利用这些类型信息提供统一的序列化与反序列化功能。
一直以来,C++都未能支持反射机制,在C++中要实现类似Java等语言的反射机制需要另外写代码保存类型相关信息,然后在运行时使用。后来C++引入了typeid
运算符,可以在运行时获取类型相关信息,说明C++在编译时是会保存类型相关信息的,只是C++标准要求保存的运行时类型信息不足以支撑类似Java的反射机制。下面给出一个简单的例子,说明C++程序在运行时获取类型相关信息。
class Object
{
public:
//用于在运行时获取对象的真实类型名称
virtual string getClassName() const
{
#ifdef _MSC_VER
return typeid(*this).name() + 6;
#else
const char* name = typeid(*this).name();
while (*name >= '0' && *name <= '9') name++;
return name;
#endif
}
};
通过上述代码你会发现不同编译器中typeid
获取的信息格式是不一样的,C++标准并没有明确定义typeid
运算符返回的信息格式。typeid
是一个很特殊运算符,对于没有虚函数表指针的对象是在编译期间确定类型信息的,对于有虚函数表指针的对象在运行时才能准确的确定类型信息,所有typeid
行为一方面像运算符受编译器控制,另一方面也在编译器的掌控之外,需要在具体运行时才能确定类型信息。
typeid
运算符是C++支持反射的一个试水,后续C++肯定会借鉴Java等语言的反射功能,最终将反射机制纳入到C++标准。
C++编译器完全可以将类型信息与虚函数表一起保存,在现有虚函数表里面附加类型信息,然后可以通过对象的虚函数表针对找到对象的类型信息。这种方案不会破坏C++原有的对象内存模型,只是拓展了虚函数表的功能。
当然,我们也可以写额外的代码来保存类型信息,然后在运行时获取保存的类型信息来实现反射功能。下面我们实现一个ReflectHelper类,该类以map的方式管理类成员的名称、类型、偏移地址等信息,map键值为类的名称。
/*
* 反射属性管理类
* 以map方式存储相关类成员变量的类型、偏移地址等信息
*/
class ReflectHelper
{
protected:
static mutex& GetMutex()
{
static mutex mtx;
return mtx;
}
static map<string, set<ReflectItem>>& GetMap()
{
static map<string, set<ReflectItem>> map;
return map;
}
public:
//通过构造函数将相关类成员的信息记录在反射信息map中
ReflectHelper(Object* self, void* data, const char* type, const char* name)
{
static map<string, set<ReflectItem>>& attrmap = GetMap();
lock_guard<mutex> lk(GetMutex());
string key = self->getClassName();
if (type == "int" || type == "bool" || type == "float" || type == "double" || type == "string")
{
attrmap[key].insert(ReflectItem((char*)(data)-(char*)(self), type, name));
}
else
{
attrmap[key].insert(ReflectItem((char*)(data)-(char*)(self), "object", name));
}
}
public:
//根据类名称获取成员变量反射信息列表
static vector<ReflectItem> GetList(const string& key)
{
static map<string, set<ReflectItem>>& attrmap = GetMap();
vector<ReflectItem> vec;
lock_guard<mutex> lk(GetMutex());
auto it = attrmap.find(key);
if (it == attrmap.end()) return {};
for (auto& item : it->second) vec.push_back(item);
std::sort(vec.begin(), vec.end(), [](const ReflectItem& a, const ReflectItem& b){
return a.getOffset() < b.getOffset();
});
return std::move(vec);
}
//获取指定类成员变量的反射信息
static ReflectItem GetItem(const string& key, const string& name)
{
static map<string, set<ReflectItem>>& attrmap = GetMap();
ReflectItem item(0, NULL, name);
lock_guard<mutex> lk(GetMutex());
auto it = attrmap.find(key);
if (it == attrmap.end()) return item;
auto tmp = it->second.find(item);
if (tmp == it->second.end()) return item;
return *tmp;
}
};
接着我们定义以下宏,用于定义反射成员变量,这些宏可以在构造对象时将类成员的名称、类型、偏移地址等信息保存在上面所说的map中,以便程序运行时使用。
/*
* 以下宏用来定义需要反射的类成员
*/
#define reflect_attr(type, name) \
type name; \
const ReflectHelper __##name = ReflectHelper(this, &this->name, #type, #name)
#define reflect_int(name) reflect_attr(int, name)
#define reflect_bool(name) reflect_attr(bool, name)
#define reflect_float(name) reflect_attr(float, name)
#define reflect_double(name) reflect_attr(double, name)
#define reflect_string(name) reflect_attr(string, name)
下面的ReflectItem类用来记录、管理一个反射成员信息与取值。
/*
* 反射属性操作类
* set方法用于修改属性
* get方法用于获取属性
*/
class ReflectItem
{
protected:
int offset;
string name;
const char* type;
public:
ReflectItem() : offset(0), type(NULL)
{
}
ReflectItem(int _offset, const char* _type, const string& _name) : offset(_offset), type(_type), name(_name)
{
}
public:
bool canUse() const
{
return type ? true : false;
}
bool operator < (const ReflectItem& obj) const
{
return name < obj.name;
}
bool operator == (const ReflectItem& obj) const
{
return name == obj.name;
}
public:
//获取成员的偏移地址
int getOffset() const
{
return offset;
}
//获取成员名称
string getName() const
{
return type ? name : "";
}
//获取成员类型
string getType() const
{
return type ? type : "";
}
//获取成员的值
string get(const void* obj) const
{
char* dest = (char*)(obj) + offset;
if (type == NULL || type == "object") "";
if (type == "int") return to_string(*(int*)(dest));
if (type == "bool") return *(bool*)(dest) ? "true" : "false";
if (type == "float") return to_string(*(float*)(dest));
if (type == "double") return to_string(*(double*)(dest));
return *(string*)(dest);
}
//修改成员的值
bool set(void* obj, int val) const
{
char* dest = (char*)(obj) + offset;
if (type == NULL || type == "object") return false;
if (type == "int")
{
*(int*)(dest) = val;
}
else if (type == "bool")
{
*(bool*)(dest) = (val ? true : false);
}
else if (type == "float")
{
*(float*)(dest) = val;
}
else if (type == "double")
{
*(double*)(dest) = val;
}
else
{
*(string*)(dest) = to_string(val);
}
return true;
}
//修改成员的值
bool set(void* obj, bool val) const
{
return set(obj, val ? 1 : 0);
}
//修改成员的值
bool set(void* obj, float val) const
{
return set(obj, (double)(val));
}
//修改成员的值
bool set(void* obj, double val) const
{
char* dest = (char*)(obj) + offset;
if (type == NULL || type == "object") return false;
if (type == "int")
{
*(int*)(dest) = val;
}
else if (type == "bool")
{
*(bool*)(dest) = val < -0.000001 || val > 0.000001;
}
else if (type == "float")
{
*(float*)(dest) = val;
}
else if (type == "double")
{
*(double*)(dest) = val;
}
else
{
*(string*)(dest) = to_string(val);
}
return true;
}
//修改成员的值
bool set(void* obj, const char* val) const
{
char* dest = (char*)(obj) + offset;
if (val == NULL || type == NULL || type == "object") return false;
if (type == "string")
{
*(string*)(dest) = val;
}
else
{
if (*val == 0) return true;
if (type == "int")
{
*(int*)(dest) = atoi(val);
}
else if (type == "bool")
{
string tmp = val;
*(bool*)(dest) = (tmp == "true");
}
else if (type == "float")
{
*(float*)(dest) = atof(val);
}
else
{
*(double*)(dest) = atof(val);
}
}
return true;
}
//修改成员的值
bool set(void* obj, const string& val) const
{
return set(obj, val.c_str());
}
};
反射机制就说到这里,上面代码很简单,有兴趣的同学可以查看我的完整代码。点击查看完整代码。