如何将标准::未来<T>转换为标准::未来<void>?

How to convert std::future<T> to std::future<void>?

本文关键字:gt 未来 lt 标准 void 转换      更新时间:2023-10-16

我遇到这样一种情况:调用 API A 会导致std::future<some_type>,但需要向 API B 提供std::future<void>

std::future<some_type> api_a();
void api_b(std::future<void>& depend_on_this_event);

在没有建议的功能(如.then()when_all())的情况下,是否有任何有效的方法可以抛弃附加到std::future<T>的价值,只留下代表事件完成的基础std::future<void>

类似以下内容的方法可能有效,但可能效率低下:

auto f = api_a();
f.wait();
auto void_f = std::async(std::launch::defer, []{});
api_b(void_f);

你能得到的最好的可能是这个:

auto f = api_a();
auto void_f = std::async(std::launch::deferred,[fut = std::move(f)]{ fut.wait();});
api_b(void_f);
template<class U>
struct convert_future_t {
  template<class T>
  std::future<U> operator()( std::future<T>&& f ) const {
    return std::async(std::launch::deferred,
      [f=std::move(f)]()->U{ return f.get(); }
    );
  }
}
template<>
struct convert_future_t<void> {
  template<class T>
  std::future<void> operator()( std::future<T>&& f ) const {
    return std::async(std::launch::deferred,
      [f=std::move(f)]()->void{ f.get(); }
    );
  }
}
template<class U, class T>
std::future<U> convert_future( std::future<T>&& f ) {
  return convert_future_t<U>{}(std::move(f));
}

这是@sbabbi答案的通用版本。

api_b( convert_future<void>( api_a() ) );

这允许任何目标和目标类型透明地工作。

这种方法的最大缺点是,由此产生的未来是一个延迟的未来,包装了一个(可能是异步的)未来,这意味着.wait_for().ready() API 不像异步期货那样工作。 返回的未来永远不会准备好,直到等待。

因此,我们可以稍微改进这一点:

template<class T>
struct ready_future_t {
  template<class...Us>
  std::future<T> operator()( Us&&...us ) const {
    std::promise<T> p;
    p.set_value(T(std::forward<Us>(us)...));
    return p.get_future();
  }
};
template<>
struct ready_future_t<void> {
  using T=void;
  // throws away the Us&&...s
  template<class...Us>
  std::future<T> operator()( Us&&...us ) const {
    std::promise<T> p;
    p.set_value();
    return p.get_future();
  }
};
template<class T, class...Us>
std::future<T> ready_future(Us&&...us){
  return ready_future_t<T>{}(std::forward<Us>(us)...);
}
template<class U>
struct convert_future_t {
  template<class T>
  std::future<U> operator()( std::future<T>&& f ) const {
    if (f.wait_for(0ms)==std::future_status::ready)
      return ready_future<U>(f.get());
    return std::async(std::launch::deferred,
      [f=std::move(f)]()->U{ return f.get(); }
    );
  }
};
template<>
struct convert_future_t<void> {
  template<class T>
  std::future<void> operator()( std::future<T>&& f ) const {
    if (f.wait_for(0ms)==std::future_status::ready)
      return ready_future<void>();
    return std::async(std::launch::deferred,
      [f=std::move(f)]()->void{ f.get(); }
    );
  }
};

至少如果我们转换它时未来已经准备好了,那么返回的未来也准备好了。

现场示例