编辑:下面添加了新答案以解决实际问题。
您的问题意味着您反复反序列化到同一个对象。这是否干净是间接的。例如,如果您有一个棋盘,您可能希望同步棋子的初始位置(从上次保存的游戏继续)。为了在玩游戏时传达动作,最好将单个动作作为单独的对象发送(然后在收到后应用于棋盘对象),而不是传输整个棋盘对象,后者只会传输已更改的内容如果它已经“初始化”。这样,您可以先验证输入并忽略无效的移动。无论如何,我只是想提一下,让我们继续。
如果您有一个可能被多次同步的对象,而成员数据只需要传输一次,则让该对象决定它是否“初始化”(因此,它是否需要传输所有内容或只是一个子集)通过使用标志(未序列化)。
然后您可以检查对象的序列化代码中的标志,就像在您发布的代码中一样(除了标志不是序列化方法的参数,而是您正在反序列化/序列化的对象的成员变量)。如果设置了标志,则反序列化所有内容并重置标志。客户端和服务器必须具有相同的标志状态,否则序列化中断。
或者,您可以先序列化标志,以告诉接收器必须如何执行反序列化(例如,每个成员数据组一个位)。
请记住,反序列化必须与序列化匹配;您必须以与序列化相同的顺序提取相同的对象。
但是,您可以序列化多态类,因为它们在反序列化时通过类层次结构中的同一级别进行序列化(如有疑问,在发送时强制转换为基指针并通过基指针反序列化)。
关于您的第二个问题,您正在寻找的是非侵入式序列化。非侵入式序列化调用独立函数并将要序列化的对象作为参数传递(这就是 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) );
}
更新:如果您需要控制(自定义)序列化方法/函数的行为方式,基于被序列化类型未知的状态,您可以实现自己的保存状态的存档类。然后,序列化函数可以查询状态并采取相应的行动。
此状态(或适当的替换)也必须序列化,以指示数据必须如何反序列化。例如,序列化函数的这种“不同行为”可能是某种压缩,而状态是使用的压缩类型。
这是自定义输出存档的最小示例。有关更多信息,您可以阅读来自现有存档的派生并挖掘提升源。
给定一个你不能修改的类:
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();
}
这个例子的目的只是为了说明原理。它对于生产代码来说太基础了。