Search code examples
c++templatesc++17

Compile time std::index_sequence with custom values


I would like to create an std::index_sequence which holds multiple of given number.

Let's say I want to hold values like 0, 3, 6, 9, ... or 0, 2, 4, 6, 8, 10, ....

How to create such a custom sequence?

I want to use it by giving number of values to generate and step between consecutive values.


Solution

  • I'd use a "normal" index sequence and multiply it by M as below:

    template <std::size_t M, std::size_t N>
    auto make_index_sequence_as_multiple_of() {
        return []<std::size_t... I>(std::index_sequence<I...>) {
            return std::index_sequence<(I * M)...>{};
        }(std::make_index_sequence<N>{});
    }
    

    Example: You could create a sequence of 5 indices as multiples of 3 like so:

    int main() {
        auto is = make_index_sequence_as_multiple_of<3, 5>();
        static_assert(
            std::is_same_v<std::index_sequence<0, 3, 6, 9, 12>, decltype(is)>);
    }
    

    Pre C++20 version:

    namespace detail {
        template<std::size_t M, std::size_t... I>
        auto helper(std::index_sequence<I...>) {
            return std::index_sequence<(I * M)...>{};    
        }
    }  // namespace detail
    
    template <std::size_t M, std::size_t N>
    auto make_index_sequence_as_multiple_of() {
        return detail::helper<M>(std::make_index_sequence<N>{});
    }
    

    If you want the type instead of an object, you could just hide the creation and use decltype to figure out the type. Here's pre C++20 version of that:

    namespace detail {
    template <std::size_t M, std::size_t... I>
    auto helper(std::index_sequence<I...>) {
        return std::index_sequence<(I * M)...>{};
    }
    }  // namespace detail
    
    template <std::size_t M, std::size_t N>
    using make_index_sequence_as_multiple_of =
        decltype(detail::helper<M>(std::make_index_sequence<N>{}));