使用特定于环境的状态序列化多态 类
Serializing polymorphic classes with environment-specific state
我有一些 class 大致如下所示:
class Base {
public:
virtual ~Base() {}
...
};
class Derived1 {
OuterState1& outerState;
InnerState1 innerState;
...
template <typename Ar>
void serialize(Ar& ar, const unsigned int /*version*/) {
ar & innerState;
}
public:
Derived1(OuterState1& outerState) : outerState(outerState) {}
...
};
class Derived2 {
OuterState2& outerState;
InnerState2 innerState;
...
template <typename Ar>
void serialize(Ar& ar, const unsigned int /*version*/) {
ar & innerState;
}
public:
Derived1(OuterState2& outerState) : outerState(outerState) {}
...
};
基本上,classes有一个依赖于外部环境的状态,我不想序列化它。对于不同的 subclasses,此状态可能不同。我想连载这个class。一件好事是 boost::serialization 可以很好地处理多态 classes,但在我看来这对我来说还不够好。我可以找到以下方法来序列化这些对象,但我都不喜欢:
外部状态使用全局变量。现在我们要么在 class 中使用这些全局变量,要么重载 load_construct_data()
并从这个全局变量创建对象。这个解决方案的问题是它需要全局变量,这通常是一个糟糕的设计,如果程序需要处理多个这样的状态就会中断。
不要使用 Boost 的多态序列化功能。相反,将实际类型保存在枚举中,然后以非多态方式保存对象。加载时,加载类型枚举,然后在开关中创建具有适当外部状态的适当类型的对象,然后以非多态方式加载对象。这个问题是我必须做很多手动编码,这些编码会由 Boost 自动完成,如果我想序列化这些对象的集合,它就不起作用了。
这个问题有没有更好更优雅的解决方案?
解决方案是将特定于环境的状态保存为 class 中的指针。然后 save/load 它在实际 classes 之前的存档中,并修改它的值。所以基础 class 的序列化看起来像这样:
class Derived1 {
OuterState1& outerState;
InnerState1 innerState;
...
template <typename Ar>
void serialize(Ar& ar, const unsigned int /*version*/) {
ar & innerState;
}
public:
Derived1(OuterState1& outerState) : outerState(outerState) {}
const OuterState1& getOuterState() const { return outerState; }
...
};
template <typename Ar>
void save_construct_data(Ar& ar, const Derived1* object, const unsigned int /*version*/) {
ar << &object.getOuterState();
}
template <typename Ar>
void load_construct_data(Ar& ar, Derived1* object, const unsigned int /*version*/) {
OuterState1* state;
ar >> state;
::new(object)Derived1{*state};
}
将特定于环境的对象序列化为 NOP。
template <typename Ar>
void serialize(Ar&, OuterState1&, const unsigned int) {
}
保存的时候,这样做:
OuterState1 outerState1;
OuterState2 outerState2;
std::shared_ptr<Base> object = getObject();
...
// saving starts now
archive << outerState1 << outerState2 << object;
...
加载时,加载状态,然后覆盖它们。假设 outerState1
和 outerState2
位于与之前相同的位置,并且已经有了新的所需值。
std::shared_ptr<Base> object;
archive >> outerState1 >> outerState2 >> object;
另一方面,问题中提供的代码并不完整。基础 class 序列化需要完成,但现在这不是重点。
我有一些 class 大致如下所示:
class Base {
public:
virtual ~Base() {}
...
};
class Derived1 {
OuterState1& outerState;
InnerState1 innerState;
...
template <typename Ar>
void serialize(Ar& ar, const unsigned int /*version*/) {
ar & innerState;
}
public:
Derived1(OuterState1& outerState) : outerState(outerState) {}
...
};
class Derived2 {
OuterState2& outerState;
InnerState2 innerState;
...
template <typename Ar>
void serialize(Ar& ar, const unsigned int /*version*/) {
ar & innerState;
}
public:
Derived1(OuterState2& outerState) : outerState(outerState) {}
...
};
基本上,classes有一个依赖于外部环境的状态,我不想序列化它。对于不同的 subclasses,此状态可能不同。我想连载这个class。一件好事是 boost::serialization 可以很好地处理多态 classes,但在我看来这对我来说还不够好。我可以找到以下方法来序列化这些对象,但我都不喜欢:
外部状态使用全局变量。现在我们要么在 class 中使用这些全局变量,要么重载
load_construct_data()
并从这个全局变量创建对象。这个解决方案的问题是它需要全局变量,这通常是一个糟糕的设计,如果程序需要处理多个这样的状态就会中断。不要使用 Boost 的多态序列化功能。相反,将实际类型保存在枚举中,然后以非多态方式保存对象。加载时,加载类型枚举,然后在开关中创建具有适当外部状态的适当类型的对象,然后以非多态方式加载对象。这个问题是我必须做很多手动编码,这些编码会由 Boost 自动完成,如果我想序列化这些对象的集合,它就不起作用了。
这个问题有没有更好更优雅的解决方案?
解决方案是将特定于环境的状态保存为 class 中的指针。然后 save/load 它在实际 classes 之前的存档中,并修改它的值。所以基础 class 的序列化看起来像这样:
class Derived1 {
OuterState1& outerState;
InnerState1 innerState;
...
template <typename Ar>
void serialize(Ar& ar, const unsigned int /*version*/) {
ar & innerState;
}
public:
Derived1(OuterState1& outerState) : outerState(outerState) {}
const OuterState1& getOuterState() const { return outerState; }
...
};
template <typename Ar>
void save_construct_data(Ar& ar, const Derived1* object, const unsigned int /*version*/) {
ar << &object.getOuterState();
}
template <typename Ar>
void load_construct_data(Ar& ar, Derived1* object, const unsigned int /*version*/) {
OuterState1* state;
ar >> state;
::new(object)Derived1{*state};
}
将特定于环境的对象序列化为 NOP。
template <typename Ar>
void serialize(Ar&, OuterState1&, const unsigned int) {
}
保存的时候,这样做:
OuterState1 outerState1;
OuterState2 outerState2;
std::shared_ptr<Base> object = getObject();
...
// saving starts now
archive << outerState1 << outerState2 << object;
...
加载时,加载状态,然后覆盖它们。假设 outerState1
和 outerState2
位于与之前相同的位置,并且已经有了新的所需值。
std::shared_ptr<Base> object;
archive >> outerState1 >> outerState2 >> object;
另一方面,问题中提供的代码并不完整。基础 class 序列化需要完成,但现在这不是重点。