Использование 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");
});
}
Ответы
Ответ 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, чтобы избежать этой проблемы?