Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Split parameter pack in 0 ... N-1 and Nth element

I would like to split a parameter pack into the first N - 1 and the Nth parameters without using the typical index_sequence & tuple trick but cannot seem to wrap my head around it, yet I'm pretty sure it should be doable? (getting the last item is easy enough with recursion).

The final function to call looks like

void Fun( Foo a, Bar b );

and a is acquired from a variadic one in turn:

template< class... T >
Foo CalcFoo( T... args );

My current implementation:

//get the last item of the pack
template< class T >
T split_last( T t ){ return t; }

template< class... T >
T split_last( T, T... t ){ return split_last( t... ); }

//helper
template< class... T, size_t... Indices >
Foo CalcFoo( const std::tuple< T... >& args, index_sequence< Indices... > )
{
  return CalcFoo( std::get< Indices >( args )... );
}

//split and call
template< class... T >
void MoreFun( T... args )
{
  //make a tuple containing all, then get n -1 items out of it
  const auto tup = std::make_tuple< T... >( args... );
  Fun( CalcFoo( tup, make_index_sequence< sizeof...( T ) - 1 >() ),
       split_last( args... ) ); 
}

update apart from wanting to know how to do this without a tuple just for the sake of it I also asked this because I somehow thought maybe a tuple would cause overhead. Thereby ignoring the premature optimization mantra, which, as usual, turned out to be correct once again. Compiled in release mode with VS2013 both my and Horstling's code yield the exact same assembly code. Everything including CalcFoo is inlined up until the call to Fun. In other words: the tuple is completely gone. So I'll probably stick with this implementation anyway because it's pretty clear.

like image 888
stijn Avatar asked Sep 05 '14 12:09

stijn


1 Answers

Ok, let's be creative. I`m sure there is a more "standard" way to do this, but I kinda like this solution ;)

http://coliru.stacked-crooked.com/a/25a3fa276e56cd94

The core idea is to recursively rotate the arguments until we can split out the (formerly) last argument.

template <size_t N>
struct MoreFunHelper
{
    template <class Head, class... Tail>
    static void RotateLeft(Head head, Tail... tail)
    {
        MoreFunHelper<N - 1>::RotateLeft(tail..., head);
    }
};

template <>
struct MoreFunHelper<0>
{
    template <class Head, class... Tail>
    static void RotateLeft(Head head, Tail... tail)
    {
        Fun(CalcFoo(tail...), head);
    }
};

template< class... T >
void MoreFun(T... args)
{
    MoreFunHelper<sizeof...(T) - 1>::RotateLeft(args...);
}

So if we start with the arguments

1 2 3 4 5

it rotates them 4 times:

2 3 4 5 1
3 4 5 1 2
4 5 1 2 3
5 1 2 3 4

Now we can split them smoothly into [5] and [1 2 3 4], which is exactly what we want. At this point, the recursion stops and just calls the functions CalcFoo and Fun.

like image 66
Horstling Avatar answered Nov 08 '22 12:11

Horstling