C++ 杂谈(15)

我们这里接着前面两篇关于 extractor 设计谈一个有意思的地方。extractor 的设计就是让我们将某些 structure 的行为可以与逻辑性的行为分开,前者单独 test 并且成为一些可以 reuse 的 component,我们提到了顺序执行 composite、chain 和 contravariant 的 case,另一种对于 protobuf 进行 extraction 的常见问题是,我们往往会从某些特定的 field extract 需要的信息,我们需要 composition structural extractor。

很显然,protobuf 的 reflection API 为我们扫清了道路,我们很容易通过 foo.bar 这个字符串在 config 里面能表示清楚需要访问的 field 是什么,同时外层的 extractor 提供的 input_type 为我们提供了 reflection 的入口(从 input_type 去找 foo 这个 field,然后找下面的 bar 等等),内层的 extractor 提供的 input_type 为我们提供了访问的 field 到底是什么类型。要知道 protobuf 并没有为诸如 repeated field、message 或者 int/float 等等提供一致的 API 方便我们访问这些 field。那么我们是否有机会通过一个 extractor 来实现不同类型 field 的支持呢?

答案是肯定的。我们首先设计一个简单的 field_accessor,它的目标是通过这个字符串转换成为一个 FieldDescriptor* 序列,我们访问对应的 field 就需要这个序列。我们需要提供不同的方法供我们调用不同的 protobuf 的不同类型的 field,比如 message 最后返回的是 Message*,而 repeated message/string 返回的是 RepeatedPtrField<T>。问题是我们怎么根据内层的 extractor 的 input_type 调用不同的 API 呢?很显然我们需要模板的特化,这个并不能用函数重载。

下面是一个实现的 outline,大家请自行脑补。

template <typename Child, typename FieldExtractor>
class field_extractor : public Child {
public:
  bool extract(...) const {
    auto field = field_access<typename FieldExtractor::input_type>(accessor, input);
    if (field) {
      return extractor->extract(*field, output);
    }
  }

private:
  field_accessor<typename Child::input_type> accessor;
  std::unique_ptr<FieldExtractor> extractor;
};

template <typename T>
class field_accessor {
public:
  const Message* get(const T& t) const;
  Message& get(T& t) const;

  template <V>
  const RepeatedPtrField<V>* get(const T& t) const;
private:
  std::vector<FieldDescriptor*> fields;
};

template <typename V, typename T>
struct helper {
  const Message* get(const field_accessor<T>& acc, const T& t) {
    return acc.get(t);
  }
};

template <typename V, typename T>
struct helper<RepeatedPtrField<V>, T> {
  const RepeatedPtrField<V>* get(const field_accessor<T>& acc, const T& t) {
    return acc.get(t);
  }
};

template <typename V, typename T>
auto field_access(const field_accessor<T>& acc, const T& t) -> decltype(helper<V, T>::get(acc, t)) {
  return helper<V, T>::get(acc, t));
}

这个地方看起来最傻的莫过于就为了有 type inference 而绕的巨大的弯子了,不知道有没什么办法减少这种 boilerplates。

——————
And Joseph was brought down to Egypt; and Potiphar, an officer of Pharaoh, captain of the guard, an Egyptian, bought him of the hands of the Ishmeelites, which had brought him down thither

Advertisements
C++ 杂谈(15)

发表评论

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