web-dev-qa-db-fra.com

Utiliser boost :: future avec des suites "then"

La méthode C++ 11 std::futuremissings a then permet d’attacher des suites au futur.

Le Boost boost::futurefournit 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");
   });
}
24
oberstet

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éfinissez BOOST_THREAD_PROVIDES_FUTURE si Vous souhaitez utiliser boost::future. Lorsque BOOST_THREAD_VERSION>=3, définissez BOOST_THREAD_DONT_PROVIDE_FUTURE si vous souhaitez utiliser boost::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.

19
dyp

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 Tuples - 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::Tuples. Eh bien, une révision ultérieure peut gérer cela.

7

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?

0
Chappelle