我正在寻找以下问题的一些设计建议:
我正在使用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)
任何想法/经验?
如果您不想重新实现车轮,Boost's serialization支持非侵入式序列化。您甚至可以在某处找到其几何类型的库支持。遗憾的是,由于XML问题,界面有些复杂。
要将对象与字节序列化,您最终需要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,主要是让您做以后会后悔的事情。