2

c++ 中代码生成的一大用途是支持消息序列化。通常,您希望支持在同一步骤中指定消息内容和布局,并为该消息类型生成代码,这些代码可以为您提供能够序列化到通信流/从通信流序列化的对象。在过去,这通常会导致代码如下所示:

class MyMessage : public SerialisableObject
{
  // message members
  int myNumber_;
  std::string myString_;
  std::vector<MyOtherSerialisableObject> aBunchOfThingsIWantToSerialise_;

public:
  // ctor, dtor, accesors, mutators, then:

  virtual void Serialise(SerialisationStream & stream)
  {
    stream & myNumber_;
    stream & myString_;
    stream & aBunchOfThingsIWantToSerialise_;
  }
};

使用这种设计的问题在于违反了良好架构的重要规则:您不应该两次指定设计的意图。意图重复,就像重复代码和其他常见的开发重复一样,会为代码中的一个地方与另一个地方产生分歧留下空间,从而导致错误。

在上面,重复的是成员列表。潜在的错误包括将成员添加到类但忘记将其添加到序列化列表中,将成员序列化两次(可能是由于不使用与成员声明相同的顺序,或者可能是由于类似成员的拼写错误等原因) ,或序列化不是成员的东西(这可能会产生编译器错误,除非名称查找在与匹配查找规则的对象不同的范围内找到某些东西)。这种错误与我们不再尝试将每个堆分配与删除(而不是使用智能指针)或使用关闭(使用 RAII ctor//dtor 机制)匹配打开文件的原因相同——我们不

因此,通常这是代码生成可以处理的事情之一。您可以创建一个文件 MyMessage.cg 以一步指定布局和成员

serialisable MyMessage
{
  int myNumber_;
  std::string myString_;
  std::vector<MyOtherSerialisableObject> aBunchOfThingsIWantToSerialise_;
};

这将通过代码生成实用程序运行并生成代码。

我想知道是否有可能在没有外部代码生成的情况下在 c++0x 中执行此操作。是否有任何新的语言机制可以将一个类指定为可序列化一次,并且其成员的名称和布局用于在序列化期间布局消息?

需要明确的是,我知道即使在 c++0x 之前的语言中,也有一些使用 boost 元组和融合的技巧可以接近这种行为。但是,这些用法基于对元组的索引而不是按成员名称访问,对于更改布局都很脆弱,因为代码中访问消息的其他位置也需要重新排序。为了不必在使用消息的代码中的位置复制布局规范,必​​须进行某种按成员名称访问。

另外,我知道将其提升到一个新的水平并要求指定何时不应该对某些成员进行序列化可能会很好。其他提供内置序列化的语言通常会提供某种属性来执行此操作,因此 int myNonSerialisedNumber_ [[noserialise]]; 可能看起来很自然。但是,我个人认为在所有内容都未序列化的情况下拥有可序列化对象是一种糟糕的设计,因为消息的生命周期是在往返于通信层的传输中,与其他数据生命周期分开。此外,您可以拥有一个对象,该对象具有纯可序列化的成员,因此该语言尚未提供此类功能。

这可能吗?还是标准委员会遗漏了这种内省能力?我不需要它看起来像上面的代码生成文件 - 任何简单的布局和成员的编译时规范在一个步骤中的简单方法都可以解决这个常见问题。

4

2 回答 2

2

这在 C++11 中既可行又实用——事实上在 C++03 中也是可行的,只是语法有点太笨拙了。我基于相同的想法编写了一个小型库 - 请参见以下内容:

www.github.com/molw5/framework

示例语法:

class Object : serializable <Object,
    value <NAME(“Field 1”), int>,
    value <NAME(“Field 2”), float>,
    value <NAME(“Field 3”), double>>
{
};

原则上,大多数底​​层代码都可以在 C++03 中重现——一些没有可变参数模板的实现细节会......很棘手,但我相信恢复核心功能是可能的。您无法在 C++03 中重现的是上面的 NAME 宏,并且语法在很大程度上依赖于它。该宏提供了从字符串生成唯一类型名所需的机制,如下所示:

NAME(“Field 1”)

扩展到

 type_string <'F', 'i', 'e', 'l', 'd', ' ', '1'>

通过使用一些常见的宏和 constexpr(用于字符提取)。回到 C++03 中,需要手动输入类似于上面的 type_string 的内容。

于 2012-12-24T04:47:49.513 回答
1

任何形式的 C++ 既不支持自省也不支持反射(在某种程度上它们是不同的)。

手动进行序列化的一件好事(即:没有自省或反射)是您可以提供对象版本控制。您可以支持旧形式的序列化,并简单地为旧版本中没有的数据创建合理的默认值。或者,如果新版本删除了一些数据,您可以简单地序列化并丢弃它。

在我看来,您需要的是 Boost.Serialization。

于 2011-08-23T02:45:34.860 回答