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");
});
}
答案 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
任务:每个then
或as_well
都会产生新的任务。
此外,as_well
不合并tuple
s - 如果左侧std::future
是std::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文件中定义这些宏,以便可以避免这个问题?