项目地址: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库。