La méthode C++ 11 std::future
missings a then
permet d’attacher des suites au futur.
Le Boost boost::future
fournit this, et il y a un exemple (que je ne parviens pas à faire fonctionner)
Je suis simplement incapable de compiler :
#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;
}
Lors de la compilation
clang++ -o test5.o -c -std=c++11 -stdlib=libc++ \
-I/home/oberstet/boost_1_55_0 test5.cpp
cela sauve avec
test5.cpp:30:1: error: unknown type name 'future'
future<int> join(const std::string& realm) {
...
Je me sens stupide;) Qu'est-ce qui se passe? J'utilise Clang 3.4 avec libc ++ et Boost 1.55 (sources non modifiées de Vanilla disponibles sur le site Web de Boost).
Ce serait bien d’obtenir un indice, probablement aussi avec un exemple sur la façon de modifier cet exemple en utilisant .then(..)
pour imprimer le résultat.
Solution (kudos @dyp):
#define BOOST_THREAD_PROVIDES_FUTURE
#include <boost/thread/future.hpp>
semble être nécessaire lors de la compilation pour C++ 11 (qui fournit future), mais on souhaite néanmoins utiliser Boost future.
Pour utiliser réellement les continuations, une autre définition est nécessaire: BOOST_THREAD_PROVIDES_FUTURE_CONTINUATION
.
Voici un exemple complet
#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");
});
}
Boost.Thread est disponible en plusieurs versions, que vous pouvez choisir via la macro BOOST_THREAD_VERSION
. Actuellement, la valeur par défaut est 2
.
Jusqu'à la version 2 de Boost.Thread, le nom boost::unique_future
était utilisé pour ce modèle de classe (à comparer avec boost::shared_future
). Probablement à cause de la normalisation de std::future
, les versions les plus récentes can utilisent le nom boost::future
. À partir de la version 3
, boost::future
est le nom par défaut.
La sélection du nom à utiliser se fait via une macro de préprocesseur:
Lorsque
BOOST_THREAD_VERSION==2
définissezBOOST_THREAD_PROVIDES_FUTURE
si Vous souhaitez utiliserboost::future
. LorsqueBOOST_THREAD_VERSION>=3
, définissezBOOST_THREAD_DONT_PROVIDE_FUTURE
si vous souhaitez utiliserboost::unique_future
.
Depuis boost docs: unique_future
vs future
Ainsi, vous pouvez soit activer explicitement boost::future
en utilisant BOOST_THREAD_PROVIDES_FUTURE
, soit basculer vers une version plus moderne de Boost.Thread en définissant BOOST_THREAD_VERSION
sur 4
, par exemple.
Si vous préférez utiliser std::future
au lieu de boost::future
, vous pouvez simplement utiliser ceci :
#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;
}
qui est un peu piraté ensemble infixe alors bibliothèque que je viens d'écrire.
Il est loin d'être parfait, car il ne poursuit pas la tâche then
au sein de la future
: chaque then
ou as_well
génère une nouvelle tâche.
De plus, as_well
ne fusionne pas Tuple
s - si le côté gauche std::future
est un std::future<std::Tuple<blah, blah>>
, je devrais fusionner avec elle plutôt que de créer un std::Tuple
de std::Tuple
s. Eh bien, une révision ultérieure peut gérer cela.
Cette définition des macros semble fonctionner pour de très petits programmes triviaux, cependant, elle ne fonctionne pas bien pour les gros programmes. En particulier, certains autres fichiers du chemin d’inclusion peuvent par ailleurs inclure boost/thread.hpp ou boost/thread/future.hpp. Cela peut même provenir d'une inclusion dans une bibliothèque tierce. En conséquence, l'utilisation des macros est interrompue car l'en-tête est inclus avant la définition des macros. Existe-t-il un moyen, lors de la création de boost, d'indiquer à boost de définir ces macros dans l'un de ses fichiers config.hpp afin d'éviter ce problème?