Search code examples
c++11tuplesboost-fusion

Fusion adaped std_tuple views, conversion to another tuple


Boost Fusion has been designed in such a way that most of the transformations are "lazy", in the sense that they all generate "views" but not actual (Fusion) containers (http://www.boost.org/doc/libs/1_58_0/libs/fusion/doc/html/fusion/algorithm.html). So for example to actually reverse a vector one needs to use the conversion function as_vector (http://www.boost.org/doc/libs/1_58_0/libs/fusion/doc/html/fusion/container/conversion/functions.html).

boost::fusion::vector<int, double, std::string> vec;
auto view_rev = boost::fusion::reverse(vec); // view object
auto vec_rev = boost::fusion::as_vector(view_rev);

Now, I want to do this with adapted std::tuple:

#include<boost/fusion/adapted/std_tuple.hpp>
...
std::tuple<int, double, std::string> tup;
auto view_rev = boost::fusion::reverse(tup);
auto tup_rev = boost::fusion::???(view_rev); // type should be of type std::tuple<std::string, double, int>

How do I convert the resulting view back to a tuple?

I expected this ??? function to be called as_std_tuple (in analogy to boost::fusion::as_vector, but it doesn't exists (yet?).

There a few solutions for reversing tuples, in this case I want just to use what is already in Boost Fusion.


Solution

  • I am not aware of any built-in method to convert a Boost Fusion Sequence into a std::tuple, but using the indices trick it can be implemented rather easily:

    template <std::size_t... Is>
    struct indices {};
    
    template <std::size_t N, std::size_t... Is>
    struct build_indices
      : build_indices<N-1, N-1, Is...> {};
    
    template <std::size_t... Is>
    struct build_indices<0, Is...> : indices<Is...> {};
    
    template<typename Sequence, std::size_t ...Is>
    auto as_std_tuple_impl(const Sequence& s, indices<Is...>&&) -> decltype(std::tie(boost::fusion::at_c<Is>(s)...))
    {
        return std::tie(boost::fusion::at_c<Is>(s)...);
    }
    
    template <typename Sequence, typename Indices = build_indices<boost::fusion::result_of::size<Sequence>::value>>
    auto as_std_tuple(const Sequence& s) -> decltype(as_std_tuple_impl(s, Indices()))
    {
        return as_std_tuple_impl(s, Indices());
    }
    

    Here is a full example that reverses an adapted std::tuple using boost::fusion::reverse and converts it back into a std::tuple and prints both tuples:

    #include <tuple>
    #include <utility>
    
    #include<boost/fusion/adapted/std_tuple.hpp>
    #include <boost/fusion/algorithm/transformation/reverse.hpp>
    #include <boost/fusion/include/reverse.hpp>
    
    #include <boost/fusion/sequence/intrinsic/size.hpp>
    #include <boost/fusion/include/size.hpp>
    
    #include <iostream>
    
    template <std::size_t... Is>
    struct indices {};
    
    template <std::size_t N, std::size_t... Is>
    struct build_indices
      : build_indices<N-1, N-1, Is...> {};
    
    template <std::size_t... Is>
    struct build_indices<0, Is...> : indices<Is...> {};
    
    template<typename Sequence, std::size_t ...Is>
    auto as_std_tuple_impl(const Sequence& s, indices<Is...>&&) -> decltype(std::tie(boost::fusion::at_c<Is>(s)...))
    {
        return std::tie(boost::fusion::at_c<Is>(s)...);
    }
    
    template <typename Sequence, typename Indices = build_indices<boost::fusion::result_of::size<Sequence>::value>>
    auto as_std_tuple(const Sequence& s) -> decltype(as_std_tuple_impl(s, Indices()))
    {
        return as_std_tuple_impl(s, Indices());
    }
    
    
    template<class Tuple, std::size_t N>
    struct TuplePrinter
    {
        static void print(const Tuple& t) 
        {
            TuplePrinter<Tuple, N-1>::print(t);
            std::cout << ", " << std::get<N-1>(t);
        }
    };
    
    template<class Tuple>
    struct TuplePrinter<Tuple, 1> 
    {
        static void print(const Tuple& t) 
        {
            std::cout << std::get<0>(t);
        }
    };
    
    template<class... Args>
    void print(const std::tuple<Args...>& t) 
    {
        std::cout << "(";
        TuplePrinter<decltype(t), sizeof...(Args)>::print(t);
        std::cout << ")\n";
    }
    
    int main()
    {
        std::tuple<int, double, std::string> tup(1,2.5,"hello");
        auto view_rev = boost::fusion::reverse(tup);
        auto reversed_tup = as_std_tuple(view_rev);
    
        print(tup);
        print(reversed_tup);
        return 0;
    }
    

    output:

    (1, 2.5, hello)
    (hello, 2.5, 1)
    

    Live example on ideone