接近没有公共基类的序列化

时间:2011-09-07 16:36:09

标签: c++ serialization boost boost-geometry

我正在为以下问题寻找一些设计建议:

我正在使用boost几何,我有几个与boost几何兼容的自定义几何类型(通过traits),但我使用的大多数类型都是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-&gt; type()(4个字节)并调用g-&gt; save(some_outputstream)并将所有这些写入二进制字段。然后,当读取二进制字段时,我只需读取字节并转换为适当的几何类型。

但是Boost几何有一个共同的基类。

当有多种类型可以存储为二进制并且您没有共享基类时,您通常如何进行序列化?

我在考虑可能有一个Serializer类,它会返回一个boost.Any,然后可以使用存储在(de)序列化器中的类型对几何体进行转换?但是,序列化器需要为每种几何类型设置一个保存方法吗?例如:保存(myPolygon),保存(myPoint)

任何想法/经历?

2 个答案:

答案 0 :(得分:2)

如果您不想重新实现轮子,

Boost's serialization支持非侵入式序列化。您甚至可以在某处找到其几何类型的库支持。遗憾的是,由于XML问题,界面有点复杂。

答案 1 :(得分:0)

要将对象与字节序列化,最终需要2个函数用于您必须支持的EACH类型(原始对象,对象等)。这些是&#34; Load()&#34;和&#34; Store()&#34;。

理想情况下,您使用固定接口作为字节 - iostream,char *,某些缓冲区对象等。 为了便于阅读,我们称之为“ByteBuffer&#34;”,从逻辑上说它的作用是什么。

我们现在有类似Serializable概念的模板函数:

template<typename T>
ByteBuffer Store(const T& object) { // BUT, What goes here...? }

template<typename T>
T Load(const ByteBuffer& bytes);

好吧,除了原始类型之外,它不会起作用 - 即使我们制作了这些&#34;访客&#34;或者他们确实需要了解对象内部的每一个细节来完成他们的工作。此外,&#34;加载()&#34;逻辑上是一个构造函数(真的,一个FACTORY,因为它很容易失败)。我们必须将这些与实际对象联系起来。

为了使Serializable成为基类,我们需要使用&#34;奇怪的重复模板&#34;图案。为此,我们要求所有派生类都具有以下形式的构造函数:

T(const ByteBuffer& bytes);

要检查错误,我们可以提供受保护的标记&#34;有效&#34;在派生构造函数可以设置的基类中。请注意,无论如何,您的对象必须支持工厂式构造,以使Load()与其良好协作。

现在我们可以做到这一点,提供&#34;加载&#34;作为工厂:

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 
   }
};

很酷的是你可以调用&#34; MyObject :: Load()&#34;它会完全符合你的期望。此外,&#34;加载&#34;可以成为构建对象的唯一方法,允许您清理只读文件的API等。

将其扩展为完整的文件API需要更多的工作,即添加&#34; Load()&#34;可以从更大的缓冲区读取(保存其他东西)和&#34; Store()&#34;附加到现有缓冲区。

作为旁注,请不要使用boost的API。在一个好的设计中,可序列化的对象应该将1对1映射到磁盘上的基本类型的打包结构 - 这是生成的文件真正可以被其他程序或其他机器使用的唯一方式。 Boost为您提供了一个可怕的API,主要是让您做一些您后来会后悔的事情。