接近没有公共基类的序列化
Approaching serialization without common base class
我正在寻找一些关于以下问题的设计建议:
我正在使用boost几何,我有几个自定义几何类型与boost几何兼容(通过特征),但我使用的大多数类型都是typedef。
class MyPoint
{
// custom stuff
};
// declare traits for MyPoint for use wih boost geometry here
class MyTaggedPoint : public MyPoint
{
// more custom stuff
};
// declare traits for MyTaggedPoint for use wih boost geometry here
// example typedefs
typedef boost::geometry::model::polygon<MyPoint> Polygon;
typedef boost::geometry::model::polygon<MyTaggedPoint> TaggedPolygon;
我的问题是当我想序列化/反序列化我的几何图形。
假设所有的几何图形都存储在数据库的二进制字段中。如果我有一个基本的几何类,我可能只需要写g->type()(4字节)并调用g->save(some_outputstream)并将所有这些写入二进制字段。然后,当读取二进制字段时,我只需读取字节并将其转换为适当的几何类型。
但是Boost几何没有有一个公共基类。
当有多种类型可以存储为二进制并且没有共享基类时,你们通常如何实现序列化?
我想也许有一个序列化器类,返回一个提升。然后可以使用存储在(反)序列化器中的类型对几何形状进行强制转换?但是,串行化程序需要为每个几何类型提供保存方法吗?例如:Save(myPolygon), Save(myPoint)
思想/经验吗?
如果您不希望重新实现转轮,Boost的序列化支持非侵入式序列化。您甚至可以在某处找到支持它们的几何类型的库。不幸的是,由于XML问题,接口有些复杂。
要将对象序列化为字节,您最终需要为必须支持的每种类型(原语,对象等)提供2个函数。它们是"Load()"answers"Store()"。
理想情况下,您可以为字节使用固定接口——iostream、char*、某些缓冲区对象等。为了便于阅读,我们称它为"ByteBuffer",因为逻辑上这就是它的角色。
我们现在有了类似于Serializable概念的模板函数:
template<typename T>
ByteBuffer Store(const T& object) { // BUT, What goes here...? }
template<typename T>
T Load(const ByteBuffer& bytes);
好吧,除了基本类型之外,这是行不通的——即使我们创建了这些"访客"之类的东西,它们也必须知道对象内部的每一个细节才能完成工作。此外,"Load()"在逻辑上是一个构造函数(实际上是一个FACTORY,因为它很容易失败)。我们必须把这些和实际的对象联系起来。
要使Serializable成为基类,我们需要使用"奇怪的循环模板"模式。为此,我们要求所有派生类都有如下形式的构造函数:
T(const ByteBuffer& bytes);
为了检查错误,可以在基类中提供一个保护标志"valid",派生构造函数可以设置该标志。注意,你的对象必须支持工厂风格的构造,这样Load()才能很好地工作。
现在我们可以这样做了,提供"Load"作为一个工厂:
template<typename T>
class Serializable // If you do reference-counting on files & such, you can add it here
{
protected:
bool valid;
// Require derived to mark as valid upon load
Serializable() : valid(false) {}
virtual ~Serializable() { valid = false; }
public:
static T Load(const ByteBuffer& bytes); // calls a "T(bytes)" constructor
// Store API
virtual ByteBuffer Store() = 0; // Interface details are up to you.
};
现在,只要像这样从基类派生,你就可以选择你需要的一切:
class MyObject : public Serializable<MyObject>
{
protected:
// .. some members ...
MyObject(const ByteBuffer& bytes)
{
//... Actual load logic for this object type ...
// On success only:
valid = true;
}
public:
virtual ByteBuffer Store() {
//... store logic
}
};
很酷的是,你可以调用"MyObject::Load()",它会做你所期望的。此外,"Load"可以被设置为构建对象的唯一方式,允许您为只读文件等清理api。
扩展到完整的文件api需要更多的工作,即添加一个"Load()",可以从更大的缓冲区读取(保存其他东西)和"Store()",附加到现有的缓冲区。
作为旁注,不要使用boost的api。在良好的设计中,可序列化对象应该一对一地映射到磁盘上的基本类型的打包结构——这是生成的文件真正能够被其他程序或其他机器使用的唯一方法。Boost提供了一个糟糕的API,它让你做了很多以后会后悔的事情。
- 虚拟基类初始化
- 如何防止在基类初始化器中调用默认构造函数?
- 类序列化方法
- 如何在派生类中存储基类初始化期间获得的信息
- Boost:当缺少类型时,如何通过基指针序列化/反序列化泛型类型集合
- 是否可以在基类初始化器列表中传递成员对象
- 多重继承、复制构造函数和基类初始化
- 继承和复制构造函数-如何从基类初始化私有字段
- 从基类模板化列表中实例化子类对象
- 事件类序列化
- 从基类模板化虚拟方法,而不是使用重载
- 在使用虚拟继承时,我可以避免重复的基类初始化吗
- 用基类模板化的静态方法(aka.Factory)派生类
- 基类初始化器和成员变量初始化器的顺序重要吗
- 用自己的类序列化QHash
- 在初始化列表中基类初始化之前调用函数
- 虚拟基类初始化
- 尽管类名与C++完全匹配,但模板基类初始化构造函数失败
- 在Boost (c++)中没有类跟踪的派生类序列化
- 增强类序列化,更改成员类型