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

问题描述 投票:0回答:2

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

我正在使用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-> type()(4个字节)并调用g-> save(some_outputstream)并将所有这些写入二进制字段。然后,当读取二进制字段时,我将简单地读取字节并转换为适当的几何类型。

但是Boost几何体没有共同的基类。

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

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

任何想法/经验?

c++ serialization boost boost-geometry
2个回答
2
投票

如果您不想重新实现车轮,Boost's serialization支持非侵入式序列化。您甚至可以在某处找到其几何类型的库支持。遗憾的是,由于XML问题,界面有些复杂。


0
投票

要将对象与字节序列化,您最终需要2个函数用于您必须支持的EACH类型(原始对象,对象等)。这些是“Load()”和“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()能够很好地与它配合使用。

现在我们可以做到这一点,提供“加载”作为工厂:

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()”,它会完全符合你的期望。此外,“加载”可以成为构建对象的唯一方式,允许您清理只读文件的API等。

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

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

© www.soinside.com 2019 - 2024. All rights reserved.