0

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

我正在使用 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 几何没有通用的基类。

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

我在想也许有一个 Serializer 类,它返回一个 boost.Any,然后可以使用将存储在(反)序列化器中的类型来投射几何图形?但是,序列化程序需要为每种几何类型提供一个保存方法吗?例如:保存(myPolygon),保存(myPoint)

有什么想法/经验吗?

4

2 回答 2

2

Boost's serialization supports non-invasive serialization if you do not wish to reimplement the wheel. You may even be able to find library support for their geometry types somewhere. The interface is somewhat complicated due to XML concerns unfortunately.

于 2011-09-07T18:15:53.060 回答
0

要将对象序列化到字节和从字节序列化,您最终需要 2 个函数来支持您必须支持的每种类型(原语、对象等)。它们是“Load()”和“Store()”。

理想情况下,您对字节使用固定接口——iostream、char*、一些缓冲区对象等。为了便于阅读,我们称它为“ByteBuffer”,因为从逻辑上讲,这就是它的作用。

我们现在有类似可序列化概念的模板函数:

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

为了检查错误,我们可以在派生构造函数可以设置的基类中提供一个受保护的标志“有效”。请注意,您的对象无论如何都必须支持工厂样式的构造,才能使 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()”,它会完全按照您的期望进行。此外,“加载”可以成为构建对象的唯一方式,允许您为只读文件等清理 API。

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

作为旁注,不要为此使用 boost 的 API。在一个好的设计中,可序列化对象应该一对一映射到磁盘上原始类型的打包结构——这是生成的文件真正可供其他程序或其他机器使用的唯一方式。Boost 为您提供了一个糟糕的 API,它主要使您能够做您以后会后悔的事情。

于 2013-11-12T23:01:33.037 回答