Подтвердить что ты не робот

Использование boost:: future с "then" продолжениями

В С++ 11 std::future отсутствует метод then для добавления продолжений в будущее.

Boost 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 с libС++ и Boost 1.55 (немодифицированные источники ванили с сайта Boost).

Было бы здорово получить подсказку, возможно, также с примером того, как изменить пример с помощью .then(..), чтобы распечатать результат.

Решение (kudos @dyp):

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

по-видимому, требуется при компиляции для С++ 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");
   });
}
4b9b3361

Ответ 1

Boost.Thread поставляется в нескольких версиях, которые вы можете выбрать с помощью макроса BOOST_THREAD_VERSION. В настоящее время по умолчанию используется 2.

До версии 2 Boost.Thread для этого шаблона класса использовалось имя 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, установив, например, BOOST_THREAD_VERSION в 4.

Ответ 2

Если вы предпочитаете использовать 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;
}

который немного взломан вместе infix , а затем библиотека, которую я только что написал.

Это далеко не идеально, потому что он не продолжает задачу then в future: каждый then или as_well создает новую задачу.

Кроме того, as_well не объединяется tuple - если левая сторона std::future является std::future<std::tuple<blah, blah>>, я должен слиться с ней, а не делать std::tuple из std::tuple s, О, хорошо, позже пересмотр может справиться с этим.

Ответ 3

Это определение макросов, по-видимому, работает для очень маленьких тривиальных программ, но для больших программ это плохо работает. В частности, некоторые другие файлы в include include могут, кстати, включать boost/thread.hpp или boost/thread/future.hpp. Это может даже произойти из включения в стороннюю библиотеку. В результате это прерывает использование макросов, когда заголовок включается до того, как будут определены макросы. Есть ли способ при создании boost, чтобы сообщить boost, чтобы определить эти макросы в одном из файлов config.hpp, чтобы избежать этой проблемы?