使用boost :: future with"然后"延续

时间:2014-03-23 22:26:01

标签: c++ c++11 boost

C ++ 11 std::future lacks一种then方法,可以将续约添加到未来。

The Boost boost::future provides这个,还有一个example(我无法运行)

我只是无法编译

#include <iostream>
#include <string>
#include <boost/thread/future.hpp>

boost::future<int> join2(const std::string& realm) {
   boost::promise<int> p;
   p.set_value(23);
   return p.get_future();
}

int main () {
   boost::future<int> f = join2("realm1");

   // here, I'd like to use f.then(..)
   f.wait();
   std::cout << f.get() << std::endl;
}

编译时

clang++ -o test5.o -c -std=c++11 -stdlib=libc++ \
   -I/home/oberstet/boost_1_55_0 test5.cpp

这就是

test5.cpp:30:1: error: unknown type name 'future'
future<int> join(const std::string& realm) {
...

我感觉很愚蠢;)发生了什么事?我正在使用clang 3.4和libc ++以及Boost 1.55(来自Boost网站的未经修改的vanilla来源)。

很高兴获得提示,可能还有一个示例,说明如何使用.then(..)修改示例以打印出结果。

解决方案(kudos @dyp):

#define BOOST_THREAD_PROVIDES_FUTURE
#include <boost/thread/future.hpp>
在编译C ++ 11(提供未来)时似乎需要

,但是仍然想要使用Boost未来。

对于实际使用延续,需要另一个定义:BOOST_THREAD_PROVIDES_FUTURE_CONTINUATION

这是一个完整的例子

#include <iostream>
#include <string>

#define BOOST_THREAD_PROVIDES_FUTURE
#define BOOST_THREAD_PROVIDES_FUTURE_CONTINUATION

#include <boost/thread/future.hpp>

using namespace boost;


int main() {
   future<int> f1 = async([]() { return 123; });

   future<std::string> f2 = f1.then([](future<int> f) {

      std::cout << f.get() << std::endl; // here .get() won't block
      return std::string("sgfsdfs");
   });
}

4 个答案:

答案 0 :(得分:19)

Boost.Thread有多个版本,您可以通过BOOST_THREAD_VERSION宏选择。目前,默认值为2

Boost.Thread的版本2,名称boost::unique_future用于此类模板(与boost::shared_future比较)。可能由于std::future的标准化,更新的版本可以使用名称boost::future。从版本3开始,boost::future是默认名称。

选择使用哪个名称是通过预处理器宏完成的:

  

BOOST_THREAD_VERSION==2定义BOOST_THREAD_PROVIDES_FUTURE时   你想使用boost::future。当BOOST_THREAD_VERSION>=3定义时   BOOST_THREAD_DONT_PROVIDE_FUTURE如果您想使用   boost::unique_future

来自boost docs: unique_future vs future


因此,您可以使用boost::future明确启用BOOST_THREAD_PROVIDES_FUTURE,或者通过将BOOST_THREAD_VERSION设置为4来切换到更现代版本的Boost.Thread。 / p>

答案 1 :(得分:7)

如果您希望使用std::future代替boost::future,则可以use this

#include <iostream>
#include <thread>
#include <future>
#include <memory>

namespace later {
// infix operator boilerplate:
template<typename T> struct infix_tag {};

template<typename op, typename LHS>
struct partial {
  std::future<LHS>&& lhs;
};
// note: moves lhs!
template<typename LHS, typename Op>
partial<Op, LHS> operator*( std::future<LHS>& lhs, infix_tag<Op> ) {
  return { std::move(lhs) };
}
template<typename Op, typename LHS>
partial<Op, LHS> operator*( std::future<LHS>&& lhs, infix_tag<Op> ) {
  return { std::move(lhs) };
}
template<typename Op, typename LHS, typename RHS, typename=void>
struct continue_t;

template<typename Op, typename LHS, typename RHS>
std::future< typename continue_t<Op, LHS, RHS>::type >
operator*( partial<Op, LHS>&& lhs, RHS&& rhs )
{
  return continue_t<Op, LHS, RHS>()( std::move(lhs.lhs), std::forward<RHS>(rhs) );
}

// std::future<T> *then* lambda(T) support:
struct then_t:infix_tag<then_t> {};
static constexpr then_t then;

template<typename LHS, typename RHS>
struct continue_t<then_t, LHS, RHS, void> {
  typedef typename std::result_of< RHS( LHS ) >::type type;
  template<typename T, typename U>
  std::future<type> operator()( std::future<T>&& lhs_, U&& rhs_ ) const {
    auto lhs = std::make_shared<std::future<T>>( std::move(lhs_) );
    auto rhs = std::make_shared<typename std::remove_reference<U>::type>( std::forward<U>(rhs_) );
    return std::async( [lhs, rhs]()->type { return (*rhs)((*lhs).get()); });
  }
};
template<typename RHS>
struct continue_t<then_t, void, RHS, void> {
  typedef typename std::result_of< RHS() >::type type;
  template<typename T, typename U>
  std::future<type> operator()( std::future<T>&& lhs_, U&& rhs_ ) const {
    auto lhs = std::make_shared<std::future<T>>( std::move(lhs_) );
    auto rhs = std::make_shared<typename std::remove_reference<U>::type>( std::forward<U>(rhs_) );
    return std::async( [lhs, rhs]()->type { lhs->get(); return (*rhs)(); });
  }
};

// std::future<T> *as_well* lambda() support:
struct as_well_t:infix_tag<as_well_t> {};
static constexpr as_well_t as_well;

template<typename LHS, typename RHS>
struct continue_t<as_well_t, LHS, RHS, typename std::enable_if<!std::is_same<void, typename std::result_of< RHS() >::type>::value>::type> {
  typedef std::tuple< LHS, typename std::result_of< RHS() >::type> type;
  template<typename T, typename U>
  std::future<type> operator()( std::future<T>&& lhs_, U&& rhs_ ) const {
    auto lhs = std::make_shared<std::future<T>>( std::move(lhs_) );
    auto rhs = std::make_shared<typename std::remove_reference<U>::type>( std::forward<U>(rhs_) );
    return std::async( [lhs, rhs]()->type {
      auto&& r = (*rhs)();
      return std::make_tuple((*lhs).get(), std::forward<decltype(r)>(r));
    });
  }
};
template<typename LHS, typename RHS>
struct continue_t<as_well_t, LHS, RHS, typename std::enable_if<std::is_same<void, typename std::result_of< RHS() >::type>::value>::type> {
  typedef LHS type;
  template<typename T, typename U>
  std::future<type> operator()( std::future<T>&& lhs_, U&& rhs_ ) const {
    auto lhs = std::make_shared<std::future<T>>( std::move(lhs_) );
    auto rhs = std::make_shared<typename std::remove_reference<U>::type>( std::forward<U>(rhs_) );
    return std::async( [lhs, rhs]()->type {
      (*rhs)();
      return (*lhs).get();
    });
  }
};
template<typename RHS>
struct continue_t<as_well_t, void, RHS, typename std::enable_if<!std::is_same<void, typename std::result_of< RHS() >::type>::value>::type> {
  typedef typename std::result_of< RHS() >::type type;
  template<typename T, typename U>
  std::future<type> operator()( std::future<T>&& lhs_, U&& rhs_ ) const {
    auto lhs = std::make_shared<std::future<T>>( std::move(lhs_) );
    auto rhs = std::make_shared<typename std::remove_reference<U>::type>( std::forward<U>(rhs_) );
    return std::async( [lhs, rhs]()->type {
      auto&& r = (*rhs)();
      lhs->get();
      return std::forward<decltype(r)>(r);
    });
  }
};
template<typename RHS>
struct continue_t<as_well_t, void, RHS, typename std::enable_if<std::is_same<void, typename std::result_of< RHS() >::type>::value>::type> {
  typedef typename std::result_of< RHS() >::type type;
  template<typename T, typename U>
  std::future<type> operator()( std::future<T>&& lhs_, U&& rhs_ ) const {
    auto lhs = std::make_shared<std::future<T>>( std::move(lhs_) );
    auto rhs = std::make_shared<typename std::remove_reference<U>::type>( std::forward<U>(rhs_) );
    return std::async( [lhs, rhs]()->type {
      (*rhs)();
      lhs->get();
      return;
    });
  }
};

}

using later::then;
using later::as_well;

int main() {
  std::future<int> computation = std::async( [](){ return 7; })
  *then* [](int x) { return x+2; }
  *as_well* []() { std::cout << "step 2\n"; }
  *then* [](int x) { std::cout << x << "\n"; return x; }
  *as_well* []() { return 3; }
  *then* []( std::tuple<int, int> m ){ std::cout << std::get<0>(m) + std::get<1>(m) << "\n"; }
  *as_well* []() { std::cout << "bah!\n"; return 3; };
  computation.wait();
  // your code goes here
  return 0;
}

这是一个有点黑客攻击的中缀然后我刚写的库。

它远非完美,因为它不会继续then中的future任务:每个thenas_well都会产生新的任务。

此外,as_well不合并tuple s - 如果左侧std::futurestd::future<std::tuple<blah, blah>>,我应该与它合并,而不是制作std::tuple std::tuple。哦,以后的版本可以解决这个问题。

答案 2 :(得分:1)

使用延续的方法可能比std :: future或boost :: future更快,更容易。由于多种原因,他们都被批评为速度慢。参见例如this presentation

演示文稿中提出的解决方案已实现为in github作为仅标头的库。您可以链接任意数量的延续,并避免隐式堆分配。另外,可以正常捕获异常。

下面是两个连续并行运行的示例:

#include <iostream>
#include <cmath>
#include <string>

#include "Lazy.h"

int main()
{
    int iInput = 10;

    // Future #1: input an integer, then take square root, then convert double to string
    auto f = Lazy::future<std::string>(iInput).
                then([](auto x) { return std::sqrt(double(x)); }).
                then([](auto x) { return std::to_string(x); }).
                finalize();

    // Future #2: input an integer, then square it, then convert int to string
    auto g = Lazy::future<std::string>(iInput).
                then([](auto x){ return x*x; }).
                then([](auto x){ return std::to_string(x);}).
                finalize();

    // Launch the tasks...
    std::cout << "Calling f.run...\n";
    auto f_run = f.run();
    std::cout << "Calling g.run...\n";
    auto g_run = g.run();

    // Do something else while f and g are running...
    // ... then get the results.
    try {
        std::string strSqrt = f.get(f_run);
        std::string strSquare = g.get(g_run);

        std::cout << "Future f returned " << strSqrt << "\n";
        std::cout << "Future g returned " << strSquare << "\n";
    }
    catch (...) {
        // Deal with an exception here.
    }
}
 /* Output:
Calling f.run...
Calling g.run...
Future f returned 3.162278
Future g returned 100
*/

答案 3 :(得分:0)

这种宏的定义似乎适用于非常小的琐碎程序,但它对大型程序不起作用。特别是,include路径中的某些其他文件可能偶然包含boost / thread.hpp或boost / thread / future.hpp。这甚至可以来自第三方库中的包含。因此,在定义宏之前包含头部时,它会中断宏的使用。有没有办法在构建boost时告诉boost在其config.hpp文件中定义这些宏,以便可以避免这个问题?