项目地址:http://code.google.com/p/febird
DataIO_is_realdump用来推断一个对象是否可以直接通过dump内存来完成序列化,如果可以,在load/save时会有极大的性能提高。如果dump后,一些成员除了需要byte_swap,而不需要其它任何转化,也可以安全地先dump然后再byte_swap,这样比一个成员一个成员地load/save并且byte_swap要快得多。
但是可以引发一个问题,就是如果序列化声明的顺序和成员定义的顺序不同,并且推断结果表明DataIO_is_realdump为真,真正序列化的顺序就就是成员定义的顺序,和序列化声明的顺序不同,而序列化声明的顺序才是调用者真正的意图。
一开始没有仔细考虑这个问题,只是把它丢给使用者,但是这样的错误实在太微妙了,很多时候都不会出错(因为load和save一样),除非load/save一个优化一个没优化,问题才会暴露出来。
认真考虑一下,这个问题其实可以检测出来,但不是在编译时,只能在运行时。只要每次调用 operator& 的时候记录参数(某个成员)的地址,下个成员的地址如果小于等于这个地址,就表明出了错。再进一步,如果某个“成员”的地址出了那个对象的地址范围,更是一个错误,如果这个成员是个指向其他对象的引用,则有可能不是一个错误,这也是可以推断出来的,引用类型本来就不是dumpable的,可以用trait去除。以下是实现代码:
template<class DataIO, class Outer, int Size, bool Dumpable> struct DataIO_is_realdump { BOOST_STATIC_CONSTANT(int, size = Size); typedef boost::mpl::bool_<Dumpable> is_dump; #if (defined(_DEBUG) || !defined(NDEBUG)) && !defined(DATA_IO_DONT_CHECK_REAL_DUMP) const Outer* address; const char* prev_member; DataIO_is_realdump(const Outer* p, const void* prev_member) : address(p), prev_member((const char*)prev_member) {} DataIO_is_realdump(const Outer* p) : address(p), prev_member((const char*)p) {} #else DataIO_is_realdump(const Outer*, const void* = 0) {} #endif template<class T> void check_member_order(const T& x, ::boost::mpl::true_) { #if (defined(_DEBUG) || !defined(NDEBUG)) && !defined(DATA_IO_DONT_CHECK_REAL_DUMP) // if member declaration order of &a&b&c&d is different with where they defined in class // here will raise an assertion fail // // 如果成员序列化声明的顺序 &a&b&c&d 和它们在类中定义的顺序不同 // 这里会引发一个断言 // 如果序列化了一个非类的成员(比如 &a&b&c&d 中可能一个标识符引用了不是雷成员的某个变量),也会引发断言 assert((const char*)&x >= (const char*)address); assert((const char*)&x <= (const char*)address + sizeof(Outer) - sizeof(T)); if (prev_member != (const char*)address) assert(prev_member < (const char*)&x); #endif } template<class T> void check_member_order(const T&, ::boost::mpl::false_) { } template<class T> DataIO_is_realdump<DataIO, Outer, Size+sizeof(T), boost::mpl::bool_<Dumpable && DataIO_is_dump<DataIO, T>::value>::value> operator&(const T& x) { check_member_order(x, ::boost::mpl::bool_<Dumpable && DataIO_is_dump<DataIO, T>::value>()); return DataIO_is_realdump<DataIO, Outer, Size+sizeof(T), boost::mpl::bool_<Dumpable && DataIO_is_dump<DataIO, T>::value>::value>(address, &x); } };
其中,check_member_order只对“根据目前的推断,Outer对象如果是dumpable,就执行检查,反之则啥都不做。
当然,需要注意的就是,在外部初始化时,调用DataIO_is_realdump(const Outer* p),而 DataIO_is_realdump(const Outer* p, const void* prev_member)仅在递归(严格讲并非递归)推断时,才使用。因此初始化传入的那个p,必须就是operator&中的对象属主,这只能由调用者保证,幸好,这个调用者只是DataIO库。