caller & callee 问题

这个问题如此的常见,比如我们希望实现一个如下的 API

struct example {
  virtual void extract(const foo& f, bar& b) = 0;
};

假设这个问题里面存在一个子问题我们使用 example 的一族子类来解决,foo 里面存在一些信息我们需要转换到 0 到多个 string,通过 bar::add 或者 bar::plus 这两个 api进入 b。但是我们希望或者都使用 add 或者都使用 plus。

struct example1 : public example {
  example1 () {
    method = &bar::add;
  }

  void extract(const foo& f, bar& b) override {
    // do something on f
    b.*method(value);
  }

  void (bar::*) method(const std::string&);
};

这个技巧大概对一个实现管用,如果我们需要一族子类都具有类似的功能,我们是否能设计一个公共的父类呢?我们称之为 example_mid,对于 example_mid 而言它需要将对 add/plus 的选择放在自己内部的话,如果不小心并不能强迫子类使用这个接口:比如我们继承 example1,子类可以通过 method 来写入 b,但是子类也可以回避。因此从这个意义上来说这个父类并不是十分的有用。

如果我们在 C++ 里面能够很容易实现类似 stream/iterator 的话,我们可以要求

struct example2 : public example {
  example2 () {
    method = &bar::add;
  }

  void extract(const foo& f, bar& b) final {
    for (const auto& value : get(f))
      b.*method(value);
  }
protected:
  virtual stream<string> get(const foo& f) = 0;

private:
  void (bar::*) method(const std::string&);
};

这里继承 example2 就不能覆盖 extract 而只能继而实现 get。实际上实现 stream/iterator 非常的麻烦,我们知道这种情况下如果 example_mid 是 caller 代码很容易写,但是 callee 的 get 就很繁复了。如果子类的 extract 是 caller,却不能够强制实现父类需要强制的东西。这时候有个 language support 的 coroutine 就好很多,比如 python 里面直接 yield 之即可。

我们其实还可以这样把 caller 提供给子类

struct example_int : public example {
  example_int () {
    method = &bar::add;
  }

  void extract(const foo& f, bar& b) final {
    get(f, [this, &b](const std::string& value) {
      b.*method(value);
    });
  }

protected:
  virtual void get(const foo& f, const std::function<void(const std::string&)>& emitter) = 0;

private:
  void (bar::*) method(const std::string&);
};

这里我们尽量避免直接返回 std::vector 的原因是这个临时变量不能 move 到需要的结果里,如果子类返回这个就会导致内存不断的分配和释放。以上通过 get 能够转变 caller/callee 同时每次 extract 都只需要创建一个对象(栈上创建的 std::function?)

Advertisements
caller & callee 问题

发表评论

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