编辑:在下面添加了新答案以解决实际问题。
您的问题意味着您反复反序列化到同一个对象。这是否干净是间接的。例如,如果你有一个棋盘,你会想要
同步棋子的初始位置(从上次保存的游戏继续)。为了在游戏进行时传达动作,发送单个动作可能是一个更好的主意
作为单独的对象(然后在收到后应用于板对象),而不是传输整个板对象,如果它已经“初始化”,则只会传输已更改的内容。
这样,您可以先验证输入并忽略无效的移动。无论如何,我只是想提一下,让我们继续。
如果你有一个对象可能会被同步多次,而成员数据只需要传输一次,那么让对象来决定它是否被“初始化”
(因此,如果它需要传输所有内容或只是一个子集)通过使用标志(未序列化)。
然后你可以检查对象的序列化代码中的标志,就像在你发布的代码中一样(除了标志不是序列化方法的参数,
但是您要反序列化的对象的成员变量)。如果设置了标志,则反序列化所有内容并重置标志。客户端和服务器必须具有相同的标志状态,否则序列化中断。
或者,您可以先序列化标志,以告诉接收器必须如何执行反序列化(例如,每个成员数据组一个位)。
请记住,反序列化必须与序列化匹配;您必须按照序列化的顺序提取相同的对象。
但是,您可以序列化多态类,因为它们是通过类层次结构中的同一级别进行序列化的,因为它们是反序列化的
(如有疑问,在发送时强制转换为基指针并通过基指针反序列化)。
关于您的第二个问题,您要查找的是non-intrusive serialization。
非侵入式序列化调用独立函数并将要序列化的对象作为参数传递(这就是 std::vector 和 boost::shared_ptr 的序列化方式)。
您可以使用BOOST_SERIALIZATION_SPLIT_FREE 将独立的serialize() 函数拆分为save() 和load()。对于侵入式序列化,它是BOOST_SERIALIZATION_SPLIT_MEMBER。
要编写通用的反序列化函数(例如通过网络传输对象),您可以使用模板:
template<typename T>
void transmit( const T& data ) {
// ...
archive << data
socket << archive_stream;
}
这种方法的限制是接收者必须知道发送了什么样的对象。如果要发送随机对象,请将它们设为多态:
IData* data = 0;
archive >> data;
switch( data->type() ) {
case TYPE_INIT:
return dispatch( static_cast<Board*>(data) );
case TYPE_MOVE:
return dispatch( static_cast<Move*>(data) );
case TYPE_CHAT:
return dispatch( static_cast<ChatMsg*>(data) );
}
更新:如果您需要控制(自定义)序列化方法/函数的行为方式,基于被序列化类型未知的状态,您可以实现自己的存档类来保存状态.然后序列化函数可以查询状态并采取相应的行动。
此状态(或适当的替换)也必须序列化,以指示数据必须如何反序列化。例如,序列化函数的这种“不同行为”可能是某种压缩,而状态是使用的压缩类型。
这是一个自定义输出存档的最小示例。欲了解更多信息,您可以阅读Derivation from an Existing Archive 并挖掘提升资源。
给定一个你不能修改的类:
struct Foo {
Foo() : i(42), s("foo") {}
int i;
std::string s;
};
您希望基于类未知的条件序列化 i 和/或 s。您可以创建一个包装器来序列化它并添加状态,但如果对象位于向量(或其他类)内,这将不起作用。
让存档了解状态可能更容易:
#include <boost/archive/text_oarchive.hpp>
// using struct to omit a bunch of friend declarations
struct oarchive : boost::archive::text_oarchive_impl<oarchive>
{
oarchive(std::ostream& os, unsigned flags=0)
: boost::archive::text_oarchive_impl<oarchive>(os,flags),mask(0){}
// forward to base class
template<class T> void save( T& t ) {
boost::archive::text_oarchive_impl<oarchive>::save(t);
}
// this is the 'state' that can be set on the archive
// and queried by the serialization functions
unsigned get_mask() const { return mask; }
void set_mask(unsigned m) { mask = m; }
void clear_mask() { mask = 0; }
private:
unsigned mask;
};
// explicit instantiation of class templates involved
namespace boost { namespace archive {
template class basic_text_oarchive<oarchive>;
template class text_oarchive_impl<oarchive>;
template class detail::archive_serializer_map<oarchive>;
} }
// template implementations (should go to the .cpp)
#include <boost/archive/impl/basic_text_oarchive.ipp>
#include <boost/archive/impl/text_oarchive_impl.ipp>
#include <boost/archive/impl/archive_serializer_map.ipp>
现在要设置和查询的状态:
enum state { FULL=0x10, PARTIAL=0x20 };
还有一个设置状态的方法(这只是一个非常基本的例子):
oarchive& operator<<(oarchive& ar, state mask) {
ar.set_mask(ar.get_mask()|mask);
return ar;
}
最后是(非侵入式)序列化函数:
namespace boost { namespace serialization {
template<class Archive>
void save(Archive & ar, const Foo& foo, const unsigned int version)
{
int mask = ar.get_mask(); // get state from the archive
ar << mask; // serialize the state! when deserializing,
// read the state first and extract the data accordingly
if( mask & FULL )
ar << foo.s; // only serialize s if FULL is set
ar << foo.i; // otherwise serialize i only
ar.clear_mask(); // reset the state
}
} } // boost::serialization
BOOST_SERIALIZATION_SPLIT_FREE(Foo)
这可以如下使用:
int main() {
std::stringstream strm;
oarchive ar(strm);
Foo f;
ar << PARTIAL << f << FULL << f;
std::cout << strm.str();
}
这个例子的目的只是为了说明原理。它对于生产代码来说太基础了。