How do I reverse the order of element types in a tuple type?
Asked Answered
N

7

20

How do I reverse the types in a tuple? For example, I want reverse_tuple<std::tuple<int, char, bool>>::type to be std::tuple<bool, char, int>. I tried doing the following but it didn't work. What did I do wrong?

#include <type_traits>
#include <tuple>

template <typename... Ts>
struct tuple_reverse;

template <typename T, typename... Ts>
struct tuple_reverse<std::tuple<T, Ts...>>
{
    using type = typename tuple_reverse<
                            std::tuple<
                               typename tuple_reverse<std::tuple<Ts..., T>>::type
                            >
                          >::type;
};

template <typename T>
struct tuple_reverse<std::tuple<T>>
{
    using type = std::tuple<T>;
};

int main()
{
    using result_type = std::tuple<int, bool, char>;
    static_assert(
        std::is_same<
            tuple_reverse<var>::type, std::tuple<char, bool, int>
        >::value, ""
    );
}

Here are my errors:

prog.cpp: In instantiation of ‘struct tuple_reverse<std::tuple<char, int, bool> >’:
prog.cpp:15:34: recursively required from ‘struct tuple_reverse<std::tuple<bool, char, int> >’
prog.cpp:15:34: required from ‘struct tuple_reverse<std::tuple<int, bool, char> >’
prog.cpp:29:31: required from here
prog.cpp:15:34: error: no type named ‘type’ in ‘struct tuple_reverse<std::tuple<int, bool, char> >’
prog.cpp: In function ‘int main()’:
prog.cpp:30:9: error: template argument 1 is invalid

Namedropping answered 18/6, 2013 at 20:15 Comment(1)
I don't think you need recursion to do this, tuple_cat, but why do you want to reverse a tupleCymbre
E
24

What you did wrong was here:

using type = typename tuple_reverse<
                        std::tuple<
                           typename tuple_reverse<std::tuple<Ts..., T>>::type
                        >
                      >::type;

Looking at it from the inside out, you reorder the tuple elements: tuple<Ts..., T>, then you try to reverse that, then you put the result in a tuple, then you try to reverse that ... huh?! :)

This means each time you instantiate tuple_reverse you give it a tuple of the same size, so it never finishes, and recursively instantiates itself forever. (Then, if that recursion even finished, you put the resulting tuple type into a tuple, so you have a single-element tuple containing an N-element tuple, and reverse that, which does nothing because reversing a single-element tuple is a no-op.)

You want to peel off one of the elements, then reverse the rest, and concatenate it back again:

using head = std::tuple<T>;
using tail = typename tuple_reverse<std::tuple<Ts...>>::type;

using type = decltype(std::tuple_cat(std::declval<tail>(), std::declval<head>()));

And you don't need to wrap it in a tuple and reverse it again :)

And you should also handle the empty tuple case, so the whole thing is:

template <typename... Ts>
struct tuple_reverse;

template <>
struct tuple_reverse<std::tuple<>>
{
    using type = std::tuple<>;
};

template <typename T, typename... Ts>
struct tuple_reverse<std::tuple<T, Ts...>>
{
  using head = std::tuple<T>;
  using tail = typename tuple_reverse<std::tuple<Ts...>>::type;

  using type = decltype(std::tuple_cat(std::declval<tail>(), std::declval<head>()));
};

I'd do it differently though.

To get just the type, using C++14

template<typename T, size_t... I>
struct tuple_reverse_impl<T, std::index_sequence<I...>>
{
  typedef std::tuple<typename std::tuple_element<sizeof...(I) - 1 - I, T>::type...> type;
};

// partial specialization for handling empty tuples:
template<typename T>
struct tuple_reverse_impl<T, std::index_sequence<>>
{
  typedef T type;
};

template<typename T>
struct tuple_reverse<T>
: tuple_reverse_impl<T, std::make_index_sequence<std::tuple_size<T>::value>>
{ };

Or you can write a function to reverse an actual tuple object, then use decltype(reverse(t)) to get the type. To reverse a tuple-like object in C++14:

template<typename T, size_t... I>
auto
reverse_impl(T&& t, std::index_sequence<I...>)
{
  return std::make_tuple(std::get<sizeof...(I) - 1 - I>(std::forward<T>(t))...);
}

template<typename T>
auto
reverse(T&& t)
{
  return reverse_impl(std::forward<T>(t),
                      std::make_index_sequence<std::tuple_size<T>::value>());
}

In C++11 use <integer_seq.h> and add return types and use remove_reference to strip references from the tuple type (because tuple_size and tuple_element don't work with references to tuples):

template<typename T, typename TT = typename std::remove_reference<T>::type, size_t... I>
auto
reverse_impl(T&& t, redi::index_sequence<I...>)
-> std::tuple<typename std::tuple_element<sizeof...(I) - 1 - I, TT>::type...>
{
    return std::make_tuple(std::get<sizeof...(I) - 1 - I>(std::forward<T>(t))...);
}

template<typename T, typename TT = typename std::remove_reference<T>::type>
auto
reverse(T&& t)
-> decltype(reverse_impl(std::forward<T>(t),
                        redi::make_index_sequence<std::tuple_size<TT>::value>()))
{
    return reverse_impl(std::forward<T>(t),
                        redi::make_index_sequence<std::tuple_size<TT>::value>());
}
Economical answered 18/6, 2013 at 20:34 Comment(5)
How do I become as good as you at this? I would have never thought to write it in all these amazing ways! :)Namedropping
Practise, practise, practise. Not just C++ though, for template metaprogramming it's useful to know some functional programming techniques. Today I'm writing a web app in Python and OSC just for fun, because I'm learning something new.Economical
Do you know how I can improve on functional programming? What would you recommend?Namedropping
@Jonathan : I am trying to compile ur code and I keep getting the following error error C2893: Failed to specialize function template 'unknown-type reverse(T &&)' 1> With the following template arguments: 1> 'T=std::tuple<int,int,float>' 1> 'TT=std::tuple<int,int,float>' Any ideas?Lepsy
Just a quick heads up. Intel compiler(at least icc 16) fails if you use index variable two times in the expression when you are expanding variadics.Lenorelenox
S
8

Untested.

template < typename Tuple, typename T >
struct tuple_push;

template < typename T, typename ... Args >
struct tuple_push<std::tuple<Args...>, T>
{
    typedef std::tuple<Args...,T> type;
};

template < typename Tuple >
struct tuple_reverse;

template < typename T, typename ... Args >
struct tuple_reverse<std::tuple<T, Args...>>
{
    typedef typename tuple_push<typename tuple_reverse<std::tuple<Args...>>::type, T>::type type;
};

template < >
struct tuple_reverse<std::tuple<>>
{
    typedef std::tuple<> type;
};

Something there abouts anyway.

This also only reverses the type, which seems to be what you're after. Reversing an actual tuple would involve functions, not metafunctions.

Supercharger answered 18/6, 2013 at 20:23 Comment(0)
J
3

I came across this question while working on the reversing template parameters for arbitrary types.

Jonathan Wakely's answer works great for tuples but in case anyone else ever needs to reverse any type, i.e. T<P1, P2, ..., Pn> to T<Pn, Pn-1, ..., P1>, here is what I came up with (Reversal logic taken from here).

namespace Details
{
    /// Get the base case template type `T<>` of a templated type `T<...>`
    template<typename>
    struct templated_base_case;

    template <template<typename...> class T, typename... TArgs>
    struct templated_base_case<T<TArgs...>>
    {
        using type = T<>;
    };

    /// Inner reverse logic.
    ///
    /// Reverses the template parameters of a templated type `T` such
    /// that `T<A, B, C>` becomes `T<C, B, A>`.
    ///
    /// Note that this requires `T<>` to exist.
    template<
        typename T,
        typename = typename templated_base_case<T>::type>
    struct reverse_impl;

    template<
        template <typename...> class T,
        typename... TArgs>
    struct reverse_impl<
        typename templated_base_case<T<TArgs...>>::type,
        T<TArgs...>>
    {
        using type = T<TArgs...>;
    };

    template<
        template<typename...> class T,
        typename first,
        typename... rest,
        typename... done>
    struct reverse_impl<
        T<first, rest...>,
        T<done...>>
    {
        using type = typename reverse_impl <T<rest...>, T<first, done...>>::type;
    };

    /// Swap template parameters of two templated types.
    ///
    /// `L<A, B, C> and R<X, Y, Z>` become `L<X, Y, Z> and R<A, B, C>`.
    template<typename L, typename R>
    struct swap_template_parameters;

    template<
        template<typename...> class L,
        template<typename...> class R,
        typename... x,
        typename... y>
    struct swap_template_parameters<L<x...>, R<y...>>
    {
        using left_type = L<y...>;
        using right_type = R<x...>;
    };
}

/// Parameter pack list of types
template <typename... Args>
struct type_list { };

/// Reverses the arguments of a templates type `T`.
///
/// This uses a `type_list` to allow reversing types like std::pair
/// where `std::pair<>` and `std::pair<T>` are not valid.
template<typename T>
struct reverse_type;

template<template<typename...> class T, typename... TArgs>
struct reverse_type<T<TArgs...>>
{
    using type = typename Details::swap_template_parameters<
        T<TArgs...>,
        typename Details::reverse_impl<type_list<TArgs...>>::type>::left_type;
};

Some of the implementation logic can be combined, but I tried to make it as clear as possible here.

reverse_type can be applied to tuples:

using my_tuple = std::tuple<int, bool, char>;

static_assert(
    std::is_same<
        typename reverse_type<my_typle>::type,
        std::tuple<char, bool, int>>::value,
    "");

Or other types:

/// Standard collections cannot be directly reversed easily
/// because they take default template parameters such as Allocator.
template<typename K, typename V>
struct simple_map : std::unordered_map<K, V> { };

static_assert(
    std::is_same<
        typename reverse_type<simple_map<std::string, int>>::type,
        simple_map<int, std::string>>::value,
    "");

Slightly more detailed explanation.

Jahncke answered 11/10, 2014 at 18:44 Comment(0)
B
1

I combined Faheem Mitha's SequenceHelper and Jonathan Wakely's tuple_reverse to reverse the tuple type and data as well. This solution works with c++11.

#include <tuple>
#include <algorithm>
#include <vector>

namespace TupleHelper {

namespace SequenceHelper {

// Tuple to parameter pack
// Original Author: Faheem Mitha https://stackoverflow.com/users/350713/faheem-mitha
// https://mcmap.net/q/638583/-tuple-to-parameter-pack
//
// License: Creative Commons Attribution-ShareAlike (CC-BY-SA)
// https://www.ictrecht.nl/en/blog/what-is-the-license-status-of-stackoverflow-code-snippets

template<int ...>
struct seq { };

template<int N, int ...S>
struct gens : gens<N-1, N-1, S...> {};

template<int ...S>
struct gens<0, S...> {
    typedef seq<S...> type;

#if __GNUC__
    virtual ~gens() {} // this is only to avoid -Weffc++ warning
#endif
};

// end of Faheem Mitha's code

} // namespace SequenceHelper

// reverses the tuple types
// How do I reverse the order of element types in a tuple type?
// Original Author: Jonathan Wakely https://stackoverflow.com/users/981959/jonathan-wakely
// https://stackoverflow.com/a/17178399
//
// License: Creative Commons Attribution-ShareAlike (CC-BY-SA)
// https://www.ictrecht.nl/en/blog/what-is-the-license-status-of-stackoverflow-code-snippets

template <typename... Ts>
struct tuple_reverse;

template <>
struct tuple_reverse<std::tuple<>>
{
    using type = std::tuple<>;
};

template <typename T, typename... Ts>
struct tuple_reverse<std::tuple<T, Ts...>>
{
  using head = std::tuple<T>;
  using tail = typename tuple_reverse<std::tuple<Ts...>>::type;

  using type = decltype(std::tuple_cat(std::declval<tail>(), std::declval<head>()));
};

// end of Jonathan Wakely's code

// reverses the content of the tuple too
namespace TupleReverseImpl {


// Helper functions to recursivly copy the source tuple to the target tuple
template <class ...Args>
void doCopyTypes( std::vector<void*>::size_type index,
                  std::vector<const void*> & content_source,
                  std::vector<void*> & content_target ) {}

template <class T, class ...Args>
void doCopyTypes( std::vector<void*>::size_type index,
                  std::vector<const void*> & content_source,
                  std::vector<void*> & content_target,
                  const T * t,
                  Args... args )
{
  // target and source vector do have the types in the same order
  // so we can cast here to the correct type, then copy it
  T * ptrSource = reinterpret_cast<T*>(const_cast<void*>(content_source.at(index)));
  T * ptrTarget = reinterpret_cast<T*>(content_target.at(index));

  *ptrTarget = *ptrSource;

  doCopyTypes( index+1, content_source, content_target, args... );
}

template <class Tuple, int ...S>
void copyTypes( std::vector<const void*> & content_source,
                std::vector<void*> & content_target,
                Tuple & tuple,
                SequenceHelper::seq<S...> )
{
  doCopyTypes( 0, content_source, content_target, &std::get<S>(tuple)... );
}

// Helper functions to fill a vector of pointers, to prepare copying

template <class V>
void dofillContent( V & content ) {}

template <class V, class T, class ...Args>
void dofillContent( V & content, T * t, Args... args )
{
  content.push_back( t );

  dofillContent( content, args... );
}

template <class V,class Tuple, int ...S>
void fill( V & content, Tuple & tuple, SequenceHelper::seq<S...> )
{
  dofillContent( content, &std::get<S>(tuple)... );
}

} // namespace TupleReverseImpl

/*
 * this reverses a tuple and its content
 *
 * std::tuple<int,bool,std::string> reverse = TupleHelper::reverseTuple( std::make_tuple( std::string( "one", true, 42 ) ) );
 */
template <class Tuple>
typename tuple_reverse<Tuple>::type reverseTuple( const Tuple & tuple )
{
    // declare return type
    typedef typename tuple_reverse<Tuple>::type REVERSE_TUPLE_TYPE;
    REVERSE_TUPLE_TYPE return_tuple;

    // create source and target pointer vectors for the copy action
    std::vector<const void*> contentSource;
    std::vector<void*> contentTarget;

    TupleReverseImpl::fill( contentSource, tuple, typename SequenceHelper::gens<std::tuple_size<Tuple>::value>::type() );
    TupleReverseImpl::fill( contentTarget, return_tuple, typename SequenceHelper::gens<std::tuple_size<REVERSE_TUPLE_TYPE>::value>::type() );

    // to be in the same order as contentTarget
    std::reverse(contentTarget.begin(), contentTarget.end() );


    // now copy everything
    TupleReverseImpl::copyTypes( contentSource, contentTarget, tuple, typename SequenceHelper::gens<std::tuple_size<Tuple>::value>::type() );

    return return_tuple;
}

} // namespace TupleHelper

int main()
{
    std::tuple<int,bool,std::string> reverse = TupleHelper::reverseTuple( std::make_tuple( std::string( "one", true, 42 ) ) );
}
Backspin answered 15/10, 2022 at 10:34 Comment(0)
A
1

I offer this version which is pretty much a direct translation of how you might reverse a list in Haskell.

This approach is short, direct, and requires no additional helpers. The parameter pack A... acts as an accumulator to collect up the result - a popular technique for working with immutable lists.


template <typename A, typename... B>
struct Reverse
{
};

template<template<typename...> typename TList, typename... A>
struct Reverse<TList<>, A...>
{
    using Type = TList<A...>;
};

template<template<typename...> typename TList, typename M, typename...N, typename... A>
struct Reverse<TList<M, N...>, A...>
{
    using Type = typename Reverse<TList<N...>, M, A...>::Type;
};

int main()
{
    static_assert(
        std::is_same<
            Reverse<std::tuple<char, int, bool>>::Type,
            std::tuple<bool, int, char>
            >::value,
        "Error"
        );
}```
Android answered 14/11, 2023 at 21:8 Comment(2)
The answer is almost perfect. I would only suggest a little change, which can increase the compilation performance, in particular with long lists of types: instead of implementing the second Reverse partial-specialization by defining the type, the recursion should be done through inheritance. This means that Reverse<TList<N...>, M, A...> should be inherited by the partial-specialization.Sleet
As a matter of style, both Reversed and Type should be lowercased to make them consistent with the C++ Standard library.Sleet
S
0

Out of interest, did you really want to reverse a tuple type, or just treat each element in reverse order (as is more often the case in my projects)?

#include <utility>
#include <tuple>
#include <iostream>

namespace detail {

    template<class F, class Tuple, std::size_t...Is>
    auto invoke_over_tuple(F &&f, Tuple &&tuple, std::index_sequence<Is...>) {
        using expand = int[];
        void(expand{0,
                    ((f(std::get<Is>(std::forward<Tuple>(tuple)))), 0)...});
    }


    template<class Sequence, std::size_t I>
    struct append;
    template<std::size_t I, std::size_t...Is>
    struct append<std::index_sequence<Is...>, I> {
        using result = std::index_sequence<Is..., I>;
    };

    template<class Sequence>
    struct reverse;

    template<>
    struct reverse<std::index_sequence<>> {
        using type = std::index_sequence<>;
    };

    template<std::size_t I, std::size_t...Is>
    struct reverse<std::index_sequence<I, Is...>> {
        using subset = typename reverse<std::index_sequence<Is...>>::type;
        using type = typename append<subset, I>::result;
    };
}

template<class Sequence>
using reverse = typename detail::reverse<Sequence>::type;

template
        <
                class Tuple,
                class F
        >
auto forward_over_tuple(F &&f, Tuple &&tuple) {
    using tuple_type = std::decay_t<Tuple>;
    constexpr auto size = std::tuple_size<tuple_type>::value;
    return detail::invoke_over_tuple(std::forward<F>(f),
                                     std::forward<Tuple>(tuple),
                                     std::make_index_sequence<size>());
};

template
        <
                class Tuple,
                class F
        >
auto reverse_over_tuple(F &&f, Tuple &&tuple) {
    using tuple_type = std::decay_t<Tuple>;
    constexpr auto size = std::tuple_size<tuple_type>::value;
    return detail::invoke_over_tuple(std::forward<F>(f),
                                     std::forward<Tuple>(tuple),
                                     reverse<std::make_index_sequence<size>>());
};

int main()
{
    auto t = std::make_tuple("1", 2, 3.3, 4.4, 5, 6, "7");
    forward_over_tuple([](auto &&x) { std::cout << x << " "; }, t);
    std::cout << std::endl;

    reverse_over_tuple([](auto &&x) { std::cout << x << " "; }, t);
    std::cout << std::endl;
}
Selfregulating answered 16/7, 2017 at 9:19 Comment(0)
F
0
#include <tuple>

template<typename T>
struct Reverse;

template<typename... Ts>
struct Reverse<std::tuple<Ts...>>
{
    using type = std::tuple<typename std::tuple_element<sizeof...(Ts) - 1 - i, std::tuple<Ts...>>::type...>;
};

// usage example
static_assert(std::is_same_v<typename Reverse<std::tuple<int, bool, double>>::type, std::tuple<double, bool, int>>);
using this method you can reverse the tuple type.
Flagwaving answered 17/3, 2023 at 15:56 Comment(1)
Please provide an explanation for how your code works and how it is an improvement on the other answers (some of which have been around for many years).Murage

© 2022 - 2024 — McMap. All rights reserved.