检查序列化声明的顺序和成员定义的顺序

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

 

项目地址:http://code.google.com/p/febird

你可能感兴趣的:(C++,c,C#,Google)