How to use std :: tuple types with boost :: mpl algorithms?

The boost::mpl algorithms cannot seem to work with the std::tuple types out of the box, for example, the following does not compile (boost-1.46.0, g ++ snapshot 2011-02-19):

 #include <tuple> #include <boost/mpl/vector.hpp> #include <boost/mpl/contains.hpp> namespace mpl=boost::mpl; typedef mpl::vector<int,float,bool> types; static_assert(mpl::contains<types, float>::value, "vector contains bool"); typedef std::tuple<int,float,bool> types2; // the following does not compile: // error: no class template named 'apply' in 'struct boost::mpl::contains_impl<boost::mpl::non_sequence_tag>' static_assert(mpl::contains<types2, float>::value, "tuple contains bool"); 

What is the easiest way to get boost::mpl algorithms to work on std::tuple ?

  • Is there evtl. boost::fusion provide this functionality (how is it done for boost::tuple )?
  • If not, is it possible to easily port the merge implementation for boost::tuple to std::tuple ?
  • If this is not the case, do I really need to implement all of the internal metafiles listed in the MPL documentation, or which ones will be sufficient? (The documents only say that "many of the built-in metapounds offer a default implementation that will work in most cases," but it’s not clear which ones. And some tests with just the beginning and the end did not lead anywhere).
+6
c ++ c ++ 11 tuples boost-mpl
source share
3 answers

Converting from std :: tuple for type escalation and return seems to be the easiest way.

 #include <iostream> #include <tuple> #include <type_traits> #include <boost/mpl/if.hpp> #include <boost/mpl/not.hpp> #include <boost/mpl/vector.hpp> namespace mpl = boost::mpl; template<typename Sequence, typename T> struct push_front; template<template<typename...> class Sequence, typename T, typename ... Args> struct push_front< Sequence<Args...>,T> { typedef Sequence<T, Args...> type; }; template<template<typename...> class To, typename From> struct tuple_change; template<template<typename...> class To, template<typename...> class From, typename ... Args> struct tuple_change<To, From<Args...>> { typedef To<Args...> type; }; template<typename Sequence, size_t N> struct at : std::tuple_element<N,Sequence> { }; template<typename Sequence> struct empty; template<template<typename...> class Sequence, typename ... Args> struct empty<Sequence<Args...>> { typedef Sequence<> type; }; template< size_t N, typename Sequence, template<typename> class Pred, typename ... Args > struct while_impl { typedef typename mpl::if_c< Pred< typename at<Sequence, sizeof...(Args) - N -1>::type >::value, typename push_front< typename while_impl<N-1, Sequence, Pred, Args...>::type, typename at<Sequence,sizeof...(Args)-N-1>::type >::type, typename empty< Sequence > ::type >::type type; }; template< typename Sequence, template<typename> class Pred, typename ... Args > struct while_impl<-1, Sequence, Pred, Args...> : empty<Sequence> { }; template< typename Sequence, template<typename> class Pred> struct while_; template< template<typename...> class Sequence, template<typename> class Pred, typename ... Args > struct while_< Sequence<Args...>, Pred > { typedef typename while_impl<sizeof...(Args)-1, Sequence<Args...>, Pred, Args...>::type type; }; template<typename T> struct not_na : mpl::not_< std::is_same<mpl_::na, T> > { }; template<template<typename...> class To, typename From> struct to_boost; template<template<typename...> class To, typename...Args > struct to_boost<To, std::tuple<Args...> > : tuple_change< mpl::vector, std::tuple<Args...> > { }; template< typename From > struct to_std; template<template<typename...> class From, typename...Args > struct to_std< From<Args...> > : while_<typename tuple_change< std::tuple, From<Args...> >::type, not_na> { }; static_assert( std::is_same< mpl::vector< char, int, bool>, typename to_boost<mpl::vector, std::tuple<char, int, bool> >::type >::value, "tuple_change to boost failed"); static_assert( std::is_same< std::tuple< char, int, bool>, typename to_std< mpl::vector<char, int, bool> >::type >::value, "tuple_change from boost failed"); int main(){ return 0;} 

* verified:
boost_1_46_0 and g ++ - 4.5 on MacOSx
boost_1_45_0 and g ++ - 4.5 on Ubuntu 10.10

+3
source share

If you want to not convert std :: tuple to mpl type, you can overload sending the boost tag. mpl uses:

 #include <tuple> #include <boost/mpl/sequence_tag.hpp> #include <boost/mpl/pop_front_fwd.hpp> #include <boost/mpl/push_front_fwd.hpp> #include <boost/mpl/push_back_fwd.hpp> #include <boost/mpl/front_fwd.hpp> #include <boost/mpl/empty_fwd.hpp> #include <boost/mpl/size_fwd.hpp> #include <boost/mpl/at_fwd.hpp> #include <boost/mpl/back_fwd.hpp> #include <boost/mpl/clear_fwd.hpp> #include <boost/mpl/pop_back_fwd.hpp> #include <boost/mpl/iterator_tags.hpp> #include <boost/mpl/next_prior.hpp> #include <boost/mpl/deref.hpp> #include <boost/mpl/begin_end_fwd.hpp> namespace boost { namespace mpl { namespace aux { struct std_tuple; } template<class ... Args> struct sequence_tag<std::tuple<Args...> > { typedef aux::std_tuple type; }; template<> struct front_impl< aux::std_tuple > { template< typename Tuple > struct apply : std::tuple_element<0, Tuple> { }; }; template<> struct empty_impl< aux::std_tuple > { template< typename Tuple > struct apply : std::integral_constant<bool, std::tuple_size<Tuple>::value == 0> { }; }; template<> struct pop_front_impl< aux::std_tuple > { template< typename Tuple > struct apply; template< class First, class ... Types > struct apply<std::tuple<First, Types...>> { typedef std::tuple<Types...> type; }; }; template<> struct push_front_impl< aux::std_tuple > { template< typename Tuple, typename T > struct apply; template< typename T, typename ... Args > struct apply<std::tuple<Args...>, T> { typedef std::tuple<T, Args...> type; }; }; template<> struct push_back_impl< aux::std_tuple > { template< typename Tuple, typename T > struct apply; template< typename T, typename ... Args > struct apply<std::tuple<Args...>, T> { typedef std::tuple<Args..., T> type; }; }; template<> struct size_impl< aux::std_tuple > { template< typename Tuple > struct apply : std::tuple_size<Tuple> { }; }; template<> struct at_impl< aux::std_tuple > { template< typename Tuple, typename N > struct apply : std::tuple_element<N::value, Tuple> { }; }; template<> struct back_impl< aux::std_tuple > { template< typename Tuple > struct apply : std::tuple_element<std::tuple_size<Tuple>::value - 1, Tuple> { }; }; template<> struct clear_impl< aux::std_tuple > { template< typename Tuple > struct apply { typedef std::tuple<> type; }; }; template<> struct pop_back_impl< aux::std_tuple > { template<int ...> struct tuple_seq {}; template<int N, int ...S> struct tuple_gens : tuple_gens<N-1, N-1, S...> {}; template<int ...S> struct tuple_gens<0, S...>{ typedef tuple_seq<S...> type; }; template < class Tuple, class Index> struct apply_impl; template < class Tuple, int ... S> struct apply_impl<Tuple, tuple_seq<S...>> { typedef std::tuple<typename std::tuple_element<S, Tuple>::type...> type; }; template< typename Tuple > struct apply : apply_impl<Tuple, typename tuple_gens<std::tuple_size<Tuple>::value - 1>::type> { }; }; template< class ... Args > struct tuple_iter; template< class ... Args > struct tuple_iter<std::tuple<Args...>> { typedef aux::std_tuple tag; typedef forward_iterator_tag category; }; template<> struct begin_impl< aux::std_tuple > { template< class Tuple > struct apply { typedef tuple_iter<Tuple> type; }; }; template<> struct end_impl< aux::std_tuple > { template< typename > struct apply { typedef tuple_iter<std::tuple<>> type; }; }; template< typename First, class ... Args > struct deref< tuple_iter<std::tuple<First, Args...> > > { typedef First type; }; template< typename First, class ... Args > struct next< tuple_iter<std::tuple<First, Args...>> > { typedef tuple_iter< std::tuple<Args...> > type; }; } } 

And related test:

 #include <boost/mpl/pop_front.hpp> #include <boost/mpl/push_front.hpp> #include <boost/mpl/push_back.hpp> #include <boost/mpl/front.hpp> #include <boost/mpl/empty.hpp> #include <boost/mpl/size.hpp> #include <boost/mpl/at.hpp> #include <boost/mpl/back.hpp> #include <boost/mpl/clear.hpp> #include <boost/mpl/pop_back.hpp> #include <boost/mpl/contains.hpp> #include <boost/mpl/aux_/test.hpp> MPL_TEST_CASE() { typedef std::tuple<int, char, bool> Tuple; MPL_ASSERT((is_same<front<Tuple>::type, int>)); MPL_ASSERT_RELATION( size<Tuple>::type::value, ==, 3 ); MPL_ASSERT(( is_same< pop_front<Tuple>::type, std::tuple<char, bool> > )); MPL_ASSERT(( is_same< push_front<Tuple, unsigned>::type, std::tuple<unsigned, int, char, bool> > )); MPL_ASSERT(( is_same< push_back<Tuple, unsigned>::type, std::tuple<int, char, bool, unsigned> > )); MPL_ASSERT_RELATION( empty<Tuple>::type::value, ==, false ); MPL_ASSERT(( is_same< at_c<Tuple, 0>::type, int > )); MPL_ASSERT(( is_same< at_c<Tuple, 1>::type, char > )); MPL_ASSERT(( is_same< back<Tuple>::type, bool > )); MPL_ASSERT(( is_same< clear<Tuple>::type, std::tuple<> > )); MPL_ASSERT(( is_same< pop_back<Tuple>::type, std::tuple<int, char> > )); MPL_ASSERT(( contains<Tuple, int> )); MPL_ASSERT(( contains<Tuple, char> )); MPL_ASSERT(( contains<Tuple, bool> )); MPL_ASSERT_NOT(( contains<Tuple, unsigned> )); } 

I tested this with gcc 4.7.2 and clang 3.2. It should contain everything you need to use anything in mpl (actually it is more than necessary). You can think of the tuple as mpl :: list (an iterator forward compilation type). Therefore, to make it play well, you need to implement what boost :: mpl :: list does. A quick search in the boost / mpl / list / aux_ directory: begin_end.hpp empty.hpp iterator.hpp pop_front.hpp push_back.hpp size.hpp clear.hpp front.hpp push_front.hpp tag.hpp are the relevant files that you need to implement.

+6
source share

This is my version for converting std :: tuple and boost types, but as said in the comment above, the conversion is probably not very efficient for compilation, i.e. will result in (unnecessary) long compilation times. A solution that avoids conversion would be preferable ...

 #include <tuple> #include <boost/mpl/vector.hpp> #include <boost/mpl/size.hpp> #include <boost/mpl/at.hpp> namespace mpl=boost::mpl; //_ 1. vector_size and vector_at for std::tuple and mpl sequences template <typename SEQ> struct vector_size : mpl::size<SEQ> {}; template <typename... TYPES> struct vector_size<std::tuple<TYPES...>> : std::tuple_size<std::tuple<TYPES...>> {}; template <typename SEQ, size_t N> struct vector_at : mpl::at_c<SEQ, N> {}; template <typename... TYPES, size_t N> struct vector_at<std::tuple<TYPES...>, N> : std::tuple_element<N, std::tuple<TYPES...>> {}; //_ 2. convert template <template <typename...> class COLLECT, typename SEQ, size_t N, typename... ARGS> struct convert_helper : convert_helper<COLLECT, SEQ, N-1, typename vector_at<SEQ, N-1>::type, ARGS...> {}; template <template <typename...> class COLLECT, typename SEQ, typename... ARGS> struct convert_helper<COLLECT, SEQ, 0, ARGS...> { typedef COLLECT<ARGS...> type; }; template <template <typename...> class COLLECT, typename SEQ> struct convert : convert_helper<COLLECT, SEQ, vector_size<SEQ>::value> {}; //_ 3. tests typedef std::tuple<int, float, bool> types; typedef mpl::vector<int, float, bool> types_v; static_assert(std::is_same<convert<std::tuple, types_v>::type, types>::value, "boost2std works"); static_assert(std::is_same<convert<mpl::vector,types>::type, types_v>::value, "std2boost works"); int main() {} 
+2
source share

All Articles