Search code examples
c++templatesc++14constexprtemplate-meta-programming

How do I reverse the order of the integers in a `std::integer_sequence<int, 4, -5, 7, -3>`?


Sometimes I want to reverse the values in an index_sequence and use the result to reverse the values in something tuple-like, like in this illustration which reverses the values in a constexpr std::array at compile time.

#include <array>
#include <cstdint>
#include <utility>

namespace detail {
template <class T, std::size_t N, std::size_t... I>
    constexpr std::array<T, N> rev_arr_helper(const std::array<T, N>& arr,
                                              std::index_sequence<I...>) {
        return {arr[sizeof...(I) - I - 1]...};

        //     {arr[4-0-1], arr[4-1-1], arr[4-2-1], arr[4-3-1]}
        //     =>
        //     {arr[3], arr[2], arr[1], arr[0]}
    }
}  // namespace detail

template <class T, std::size_t N>
constexpr std::array<T, N> rev_arr(const std::array<T, N>& arr) {
    return detail::rev_arr_helper(arr, std::make_index_sequence<N>{});
}

int main() {
    constexpr std::array<int, 4> arr{11, 22, 33, 44};
    constexpr auto rev = rev_arr(arr);    
    static_assert(rev[0] == 44 && rev[1] == 33 && rev[2] == 22 && rev[3] == 11, "");
}

Now, this approach does not work for any integer_sequence, like the one in the title. It only works for those ordered 0, 1, 2, ..., N-1 and I would like to be able to make this compile using C++14:

#include <type_traits>
#include <utility>

int main() {
    std::integer_sequence<int, 4, -5, 7, -3> iseq;
    std::integer_sequence<int, -3, 7, -5, 4> itarget;

    auto irev = reverse_sequence(iseq);

    static_assert(std::is_same<decltype(irev), decltype(itarget)>::value, "");
}

How can that be done?


Here's a somewhat related Q&A collected from the comments:
How do I reverse the order of element types in a tuple type?


Solution

  • A solution that doesn't use std::tuple is to convert to an std::array and access the corresponding indices with the help of std::make_index_sequence.

    namespace detail {
        template<typename T, T... N, std::size_t... Indices>
        constexpr auto reverse_sequence_helper(std::integer_sequence<T, N...> sequence,
                                               std::index_sequence<Indices...>) {
            constexpr auto array = std::array<T, sizeof...(N)> { N... };
            return std::integer_sequence<T, array[sizeof...(Indices) - Indices - 1]...>();
        }
    }
    template<typename T, T... N>
    constexpr auto reverse_sequence(std::integer_sequence<T, N...> sequence) {
        return detail::reverse_sequence_helper(sequence,
                                               std::make_index_sequence<sizeof...(N)>());
    }
    

    if C++20 is available, it can be reduced to this function with the help of templated lambdas:

    template<typename T, T... N>
    constexpr auto reverse_sequence(std::integer_sequence<T, N...> sequence) {
        constexpr auto array = std::array { N... };
     
        auto make_sequence = [&]<typename I, I... Indices>(std::index_sequence<Indices...>) {
            return std::integer_sequence<T, array[sizeof...(Indices) - Indices - 1]...>();
        };
        return make_sequence(std::make_index_sequence<sizeof...(N)>());
    }