代码之家  ›  专栏  ›  技术社区  ›  oberstet

使用boost::future和“then”continuations

  •  30
  • oberstet  · 技术社区  · 11 年前

    C++11 std::future lacks then 方法将延续附加到未来。

    助推 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) {
    ...
    

    我觉得自己很愚蠢;)发生了什么事?我在libc++和Boost 1.55中使用clang 3.4(来自Boost网站的未修改的香草源代码)。

    最好能得到一个提示,可能还有一个如何使用 .then(..) 打印结果。

    解决方案 (荣誉@dyp):

    #define BOOST_THREAD_PROVIDES_FUTURE
    #include <boost/thread/future.hpp>
    

    在为C++11编译(这提供了未来)时似乎是必需的,但人们还是希望使用Boostfuture。

    对于实际使用延续,需要另一个定义: 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 回复  |  直到 11 年前
        1
  •  21
  •   dyp    9 年前

    Boost.Thread有几个版本,您可以通过 BOOST_THREAD_VERSION 宏。当前,默认值为 2 .

    Boost.Thread的最新版本2,名称 boost::unique_future 用于此类模板(与 boost::shared_future ). 可能是因为 std::future ,最新版本 可以 使用名称 boost::future 。从版本开始 3 , boost::未来 是默认名称。

    通过预处理器宏选择要使用的名称:

    什么时候 BOOST_THREAD_VERSION==2 定义 BOOST_THREAD_PROVIDES_FUTURE 如果 您想使用 boost::未来 什么时候 BOOST_THREAD_VERSION>=3 定义 BOOST_THREAD_DONT_PROVIDE_FUTURE 如果您想使用 boost::unique_future .

    从…起 boost docs: unique_future vs future


    因此,您可以显式启用 boost::未来 通过使用 BOOST_THREAD_PROVIDES未来 或切换到更现代版本的Boost.Thread by setting BOOST_THREAD_VERSION 4 例如

        2
  •  8
  •   Yakk - Adam Nevraumont    11 年前

    如果您愿意使用 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 :每个 然后 as_well 生成新任务。

    此外 作为井(_W) 不合并 tuple s——如果左手边 std::未来 是一个 std::future<std::tuple<blah, blah>> ,我应该与它合并,而不是 std::tuple 属于 std::元组 哦,好吧,以后的修订可以解决这个问题。

        3
  •  1
  •   Chappelle    10 年前

    这种宏的定义似乎适用于非常小的小程序,然而,它不适用于大程序。特别是,include路径中的某些其他文件可能附带包含boost/thread.hpp或boost/tthread/future.hpp。这甚至可能来自第三方库中的include。因此,当在定义宏之前包含标题时,它会中断宏的使用。在构建boost时,有没有一种方法可以告诉boost在其config.hpp文件中定义这些宏,从而避免这个问题?

        4
  •  1
  •   Keijo    4 年前

    可能有比std::future或boost::future更快更容易的方法来使用continuation。由于几个原因,他们两人都被批评行动迟缓。参见例如。 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
    */