protobuf 的递归与 extension

递归结构

protobuf 支持递归的结构定义,也就是说可以在 message Foo 里面继续定义一个子域为 Foo,如此一来我们可以获得一个 tree-like structure,这一点意味着可以通过它处理 linked list、tree 等等数据结构的串行化。

一个实际的例子:我们为了计算某个 metric 定义了一个 message,这个 metric 是由若干项加权相加而来,每一项我们也是使用同一个 message type 进行的处理,比较有意思的是一开始我们的 learning 算法只用最后的结果产生反馈,后来觉得这个不够,希望反馈能建立在每个子项上(信息更多的时候优化的策略更优),一开始以为需要大量改动程序,后来发现直接在 message 里面递归的加入 repeated 自己的类型,这时简单的为其记录子项即可。

extension 设计缺陷?

我们有一个类型 Foo 允许 extension,在 CookConfig 里面定义了 Foo 的 extension,本质上这个 extension 对应一个 number,通过 CookConfig::ext_name 获得。so far so good。我们后来发现处理 CookConfig 对应的问题的时候,仅仅在 Foo 里面加入 extension 是不够的,相关的 Bar 也得 extend,并且使用一样的 extension,看起来很自然会如下写

message CookConfig {
  optional int32 duration_in_sec = 1;

  extend Foo {
    optional CookConfig cook_config = 101;
  }

  extend Bar {
    optional CookConfig cook_config = 101;
  }
}

这个一定不会 work,至于为什么,我们可以看看生成代码:

  static ::google::protobuf::internal::ExtensionIdentifier< ::Foo,
      ::google::protobuf::internal::MessageTypeTraits< ::CookConfig >, 11, false >
    cook_config;

这里 cook_config 的类型里面通过 Foo 完成了类型的约束,也就是说通过 Set/Get/HasExtension 获得 extension 信息的时候传入的参数类型和 extend 的 message type 有关系,这样 Bar 通过 cook_config 获得的 type 不能 match 自己的 type 导致无法编译。同时这是一个 static variable 没有办法“重载”,意味着 Bar 和 Foo 不可能 share 这个 extension 的名字,哪怕其实他们都是用来做一件事情的。

并不是说不让这么写就是世界末日了,而是假想一下如果允许这样写,我们就可以写个简单的方法从 Foo/Bar 里面获得 CookConfig,而现在从 C++ 的角度,为了实现这么个简单的功能却要写得巨复杂:

template <typename T> 
struct cook_traits {
  static inline const CookConfig&
  config(const T& t) {
    static_assert(std::is_same<T, T*>::value,
                  "provided type doesn't provide a specialization");
  }
};

template <>
struct cook_traits<Foo> {
  static inline const CookConfig&
  config(const Foo& t) {
    return t.GetExtension(CookConfig::cook_config);
  }
};

template <>
struct cook_traits<Bar> {
  static inline const CookConfig&
  config(const Bar& t) {
    return t.GetExtension(CookConfig::config);
  }
};

template <typename T> inline const CookConfig&
get_config(const T& t) {
  return cook_traits<T>::config(t);
}

万一一个问题里面需要 extend 的不止一两个,这样真就是悲剧一个啊…

——————
These men are peaceable with us; therefore let them dwell in the land, and trade therein; for the land, behold, it is large enough for them; let us take their daughters to us for wives, and let us give them our daughters

Advertisements
protobuf 的递归与 extension

发表评论

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / 更改 )

Twitter picture

You are commenting using your Twitter account. Log Out / 更改 )

Facebook photo

You are commenting using your Facebook account. Log Out / 更改 )

Google+ photo

You are commenting using your Google+ account. Log Out / 更改 )

Connecting to %s