web-dev-qa-db-ja.com

"then"の継続でboost :: futureを使用する

C++ 11 _std::future_ lacks 将来に継続をアタッチするthenメソッド。

ブースト_boost::future_ 提供する これ、そして があります(これは実行できません)

私は単にコンパイルできません

_#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 Webサイトの未変更のバニラソース)。

ヒントを得るのは素晴らしいでしょう。おそらく.then(..)を使用して例を変更し、結果を出力する方法の例も示します。

ソリューション(kudos @dyp):

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

c ++ 11(futureを提供する)向けにコンパイルする場合は必須と思われますが、それでもBoost futureを使用したいと考えています。

実際に継続を使用するには、別の定義が必要です:_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");
   });
}
_
26
oberstet

Boost.Threadにはいくつかのバージョンがあり、BOOST_THREAD_VERSIONマクロで選択できます。現在、デフォルトは2です。

Boost.Threadのバージョン2までは、このクラステンプレートにboost::unique_futureという名前が使用されていました(boost::shared_futureと比較してください)。おそらくstd::futureの標準化により、より最近のバージョンcanboost::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を定義します。

From boost docs:unique_future vs future


したがって、boost::futureを使用してBOOST_THREAD_PROVIDES_FUTUREを明示的に有効にするか、たとえばBOOST_THREAD_VERSION4に設定して、Boost.Threadのより新しいバージョンに切り替えることができます。

19
dyp

std::futureの代わりにboost::futureを使用したい場合は、 これを使用

#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タスクを継続しないため、完全ではありません。各thenまたはas_wellは新しいタスクを生成します。

さらに、as_wellTuplesをマージしません-左側のstd::futurestd::future<std::Tuple<blah, blah>>の場合、std::Tuplestd::Tuplesにするのではなく、マージする必要があります。まあ、それ以降のリビジョンはそれを処理できます。

このマクロの定義は、非常に小さな些細なプログラムではうまくいくようですが、大きなプログラムではうまくいきません。特に、インクルードパス内の他のファイルには、偶発的にboost/thread.hppまたはboost/thread/future.hppが含まれる場合があります。これは、サードパーティのライブラリに含まれている場合でも発生します。その結果、マクロが定義される前にヘッダーが含まれるようになるため、マクロの使用が中断されます。この問題を回避できるように、ブーストをビルドして、これらのマクロをconfig.hppファイルの1つに定義するようにブーストを指示する方法はありますか?

0
Chappelle