2

I have an std::tuple and I want to split it at an arbitrary compile-time parameter N. I have seen solutions floating around for getting the head and the tail of an std::tuple like here , here or here , but these cannot solve my problem since for me this constant (like 1) that is being passed as a template argument is a runtime parameter and that defeats the purpose of an std::index_sequence. For example something like this, doesn't work:

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


template < typename T , typename... Ts >
auto head( std::tuple<T,Ts...> t )
{
   return  std::get<0>(t);
}

template <std::size_t... Ns , typename... Ts>
auto tail_impl( std::index_sequence<Ns...> , std::tuple<Ts...> t, std::size_t N)
{
   return  std::make_tuple( std::get<Ns + N>(t)... );
}

template < std::size_t N, typename... Ts>
auto tail( std::tuple<Ts...> t)
{
   return  tail_impl( std::make_index_sequence<sizeof...(Ts) - N>() , t, N);
}

int main()
{
   auto t = std::make_tuple( 2, 3.14 , 'c' , 5);
   std::cout << std::get<0>( tail<2>(t) ) << std::endl;
}

Therefore my question is: Is this possible? This would be simpler if I could have an std::index_sequence starting from an arbitrary N for example but I couldn't find how to do that.

max66
  • 65,235
  • 10
  • 71
  • 111
dkoutsou
  • 87
  • 5

1 Answers1

4

You're near.

Your error is to pass N as argument from tail() to tail_impl().

You should pass it as template parameter

template < std::size_t N, typename... Ts>
auto tail( std::tuple<Ts...> t)
{
   // ..............VVV
   return  tail_impl<N>( std::make_index_sequence<sizeof...(Ts) - N>() , t);
}

and tail_impl() become

// .......VVVVVVVVVVVVVV
template <std::size_t N, std::size_t... Ns , typename... Ts>
auto tail_impl( std::index_sequence<Ns...> , std::tuple<Ts...> t)
{
   return  std::make_tuple( std::get<Ns + N>(t)... );
}

The problem in your code is that if you pass N as a normal function argument, you can't use it in std::get<Ns + N> because the template parameter of std::get() (Ns + N, in your case) has to be known compile time but a normal function argument can be also a run-time known value.

This would be simpler if I could have an index_sequence starting from an arbitrary N for example but I couldn't find how to do that.

A std::index_sequence starting from an arbitrary N is possible; it's also possible a std::index_sequence with numbers without order.

The problem is that there isn't a standard std::make_index_sequence equivalent to create a std::index_sequence starting from N.

Obviously you can create it

template <std::size_t Shift, std::size_t ... Is>
constexpr std::index_sequence<Shift+Is...>
   misws_helper (std::index_sequence<Is...>);

template <std::size_t N, std::size_t Shift = 0u>
using make_index_sequence_with_shift
  = decltype(misws_helper<Shift>(std::make_index_sequence<N>{}));

// ...

using IS0 = std::index_sequence<3u, 4u, 5u, 6u, 7u>;
using IS1 = make_index_sequence_with_shift<5u, 3u>;

static_assert( std::is_same<IS0, IS1>::value, "!" );
max66
  • 65,235
  • 10
  • 71
  • 111