围绕std :: future :: then有这个便利包装的名称吗?

时间:2015-08-31 20:33:53

标签: c++ future naming

jQuery version of "Deferreds"(C ++调用"期货")中,.then()方法将一个函数作为其参数,其签名不包含任何未来。鉴于,为C ++ 17(see N3721)提议的.then()在其签名中采用future的函数。

即如果我想在背景中计算f(g(x))"",N3721要我写

extern int f(int);
extern int g(int);

auto async_fg(int x) {
    return std::async(f, x).then([](std::future<int> mid) {
        return g(mid.get());
    });
}

我很想写一个包装器,就像这样:

template<class F>
auto futurize(F&& f) {
    return [](auto mid) {
        return std::forward<F>(f)(mid.get());
    };
}

auto async_fg(int x) {
    return std::async(f, x).then(futurize(g));
}

但这两种解决方案似乎都很尴尬;最重要的是,我不知道正确的名称,因为我正在呼叫&#34;未来&#34;这里。如果之前已经提出过这个操作,人们怎么称呼它?

显然,如果future<T>具有带语义的新成员函数,那么更好的界面

template<class T, class F>
auto future<T>::then_futurize(F&& f)
{
    return this->then(futurize(std::forward<F>(f)));
}

auto async_fg(int x) {
    return std::async(f, x).then_futurize(g);
}

构造是否有(现有)名称? (我刚刚编写了then_futurize这个名字,我不喜欢它。)

我接受来自任何现有语言或库的答案(Javascript,Python,Ruby,C ++ Boost,C ++ Folly ......);他们不必专门提出C ++标准提案,尽管这显然是最好的。

我已经注意到Folly's version of .then()带有签名的功能 X(future<Y>) X(Y);但我想这会在一些极端情况下变得模棱两可。此外,未来的.then()和&#34;便利包装&#34; .then()似乎是完全不同的操作,我不喜欢不同的名称。

2 个答案:

答案 0 :(得分:2)

啊哈! N3865与我称之为.next()的原语相同; N3865称之为auto async_fg(int x) { return std::async(f, x).next(g); }

.recover()

N3865还提供了对应的std::future<std::string> f2 = f1.next( [](int v) { return v.to_string(); } ).recover( [](exception_ptr ex) { return "nan"; } ); ,允许Javascript风格的延续构造:

.has_value()

它还提供了许多其他很酷的便利包装,例如.value_or()futurize()

(我没有看到.then_futurize()的任何名称,但.next()肯定被命名为{{1}}。)

答案 1 :(得分:0)

do_next怎么样?

template<class F>
auto do_next( F&& f )
{
  return [f = std::forward<F>(f)]( auto&& fut ) {
    return f( decltype(fut)(fut).get() );
  };
}

auto async_fg(int x) {
  return std::async(f, x).then(do_next(g));
}

如上所述,将未来视为Haskell monad,我认为then(do_next>>=又名bind,但这并不实际。

您还可以从命名运算符中获取页面:

namespace named_operator {
  template<class D>struct make_operator{make_operator(){}};

  template<class T, char, class O> struct half_apply { T&& lhs; };

  template<class Lhs, class Op>
  half_apply<Lhs, '*', Op> operator*( Lhs&& lhs, make_operator<Op> ) {
      return {std::forward<Lhs>(lhs)};
  }

  template<class Lhs, class Op, class Rhs>
  auto operator*( half_apply<Lhs, '*', Op>&& lhs, Rhs&& rhs )
  -> decltype( invoke( std::forward<Lhs>(lhs.lhs), Op{}, std::forward<Rhs>(rhs) ) )
  {
      return invoke( std::forward<Lhs>(lhs.lhs), Op{}, std::forward<Rhs>(rhs) );
  }
}

namespace my_operator {
  struct do_next_t{};
  static const named_operator<do_next_t> do_next;

  template<class T, class F,
    class R = std::result_of_t<std::decay_t<F>(T)>
  >
  auto invoke( std::future<T>&& lhs, do_next_t, F&& f )
  -> std::future< R >
  {
    return lhs.then( [f = std::forward<F>(f)](std::future<T> fut)mutable->R {
      return std::move(f)(std::forward<F>(fut.get()));
    });
  };
}
using my_operator::do_next;

给你:

auto async_fg(int x) {
  return std::async(f, x) *do_next* g;
}

或其他名称,如*then_do*

live example(在调用.then时失败)。

...

解码Haskell,我认为.then对于仿函数是fmap.then(futurize在monad上是>>=bind,这意味着futurize是关于仿函数的lift

>>=bind通常被认为比fmap / lift更容易使用;将这两者组合成一个概念。