2

I'm trying to use a variadic template function where the parameter pack is not the last parameter in the list. Note that there are two recursive calls--one dropping a parameter in front of the pack, the other call dropping a parameter after the pack.

  • My compiler appears to be: Apple LLVM version 8.1.0 (clang-802.0.42)
  • All the int's below will be a new T template parameter if I can get this working.

There's no point in using ... if the call site for Blender can't be clean. I could just expand several overloads of Blender myself in that case. I'd really rather not resort to this. I hope I'm just missing something.

int Blender( double t, int i)
{
    return i;
}

template <typename ...Args>
  int Blender( double t, int first, Args... more, int last)
{
    return (1-t)*Blender(t, first, more...) + t*Blender(t, more..., last);
}

static void tryit()
{
    Blender(.5, 23, 42, 89); //doesn't compile
}
max66
  • 65,235
  • 10
  • 71
  • 111
Mustang
  • 363
  • 2
  • 9
  • 6
    Long story short, it's not possible. Packs are "greedy" by design. – StoryTeller - Unslander Monica Dec 17 '17 at 19:50
  • Might be able to do it much like how one can do currying in C++. The twist being pulling out that last parameter and doing whatever special thing you want to do with it. https://stackoverflow.com/questions/152005/how-can-currying-be-done-in-c – Eljay Dec 17 '17 at 20:02
  • Where are those numbers coming from? It is possible only when you are working with compile-time constants. – Jodocus Dec 17 '17 at 20:38
  • @Jodocus, In reality they're not numbers at all. They're objects that can be added and multiplied by a double (linearly combined). And the values aren't known at compile time. – Mustang Dec 17 '17 at 20:43
  • Your version of xcode supports some [tag:C++14] features if you turn it on. That will help if you can do it; can you do it? – Yakk - Adam Nevraumont Dec 18 '17 at 16:41
  • Possible duplicate of [Variadic function template with pack expansion not in last parameter](https://stackoverflow.com/questions/14768951/variadic-function-template-with-pack-expansion-not-in-last-parameter) – underscore_d Dec 18 '17 at 16:50

3 Answers3

0

I propose another solution, really different from the code in the question, that I think is a lot more efficient (creation on only one std::array; blender obtained by template index sequence)

#include <array>
#include <utility>
#include <iostream>

template <typename T, std::size_t I0>
int blenderH (double t, T const & arr, std::index_sequence<I0> const &)
 { return arr[I0]; }

template <typename T, std::size_t I0, std::size_t ... Is>
auto blenderH (double t, T const & arr, 
               std::index_sequence<I0, Is...> const &)
   -> std::enable_if_t<0U != sizeof...(Is), int>
 { return   (1-t) * blenderH(t, arr, std::index_sequence<(Is-1U)...>{})
          +    t  * blenderH(t, arr, std::index_sequence<Is...>{}); }

template <typename ... Args>
int blender (double t, Args ... as)
 {
   static constexpr auto size = sizeof...(Args);

   return blenderH(t, std::array<int, size>{ { as... } },
                   std::make_index_sequence<size>{});
 }

int main()
 { std::cout << blender(.3, 23, 42, 89) << std::endl; }

Unfortunately also this solution works (std::index_sequence and std::make_index_sequence) starting from C++14.

-- EDIT --

Caleth say.

Some explanation of what is going on here would help. I am confused by the lack of I0 in the body of the second overload.

I try an explanation.

Suppose is called the recursive version of BlenderH() (the second overload) with a list of index in the std::index_sequence value. Say 5, 6, 7 and 8; so I0 is 5 and Is... is 6, 7, 8.

We have to recursive call blenderH() with indexes 5, 6, 7 first and with 6, 7, 8 next.

We can avoid to use I0 (5) because

  • 5, 6, 7 is obtained from 6, 7, 8 reducing by 1 every value (so the std::index_sequence<(Is-1U)...> for the first recursive call)

  • and 6, 7, 8 is Is... without modifications (so the std::index_sequence<Is...> in the second one.

From the practical point of view, I0 is declared only to be discarded; ther is no need to use it.

max66
  • 65,235
  • 10
  • 71
  • 111
  • Some explanation of what is going on here would help. I am confused by the lack of `I0` in the body of the second overload. – Caleth Dec 18 '17 at 16:53
  • 1
    @Caleth - I'm not good in explanations, but... answer modified. – max66 Dec 18 '17 at 21:09
-1
template<std::size_t I>
using count = std::integral_constant<std::size_t, I>;

namespace details {

  template<class...Args, std::size_t N,
    typename std::enable_if<sizeof...(Args)==N, bool>::type = true
  >
  int Blender( count<N> drop, double t, int i, Args...args ) {
    return i;
  }
  template<class...Args, std::size_t N,
    typename std::enable_if<sizeof...(Args)!=N, bool>::type = true
  >
  int Blender( count<N> drop, double t, int i, Args...args ) {
    return (1-t)*Blender( count<N+1>{}, t, i, args... ) + t*Blender( count<N>{}, t, args... );
  }
}

template <typename ...Args>
int Blender( double t, int first, Args... more)
{
  return details::Blender( count<0>{}, first, more... );
}


static void tryit()
{
  Blender(.5, 23, 42, 89); //doesn't compile
}

here count<N> counts the number of arguments at the end to ignore.

The two details overload cover the case where N is equal to the number of arguments in the pack (and hence we have 1 argument left), and when it is not. They are dispatched to using SFINAE.

Yakk - Adam Nevraumont
  • 262,606
  • 27
  • 330
  • 524
-1

This is actually really easy once you realize that the limitation is in the type pack deduction and not in the function call. We just need a way to use the type pack we can deduce and pass it explicitly avoiding deduction at the function that needs to pull off the last parameter:

int Blender( double t, int i)
{
    return i;
}

template <typename ...Args>
  int Blender( double t, int first, Args... more);

template <typename ...Args>
  int BlenderWithoutLast( double t, Args... more, int last)
{
    return Blender(t, more...);
}

template <typename ...Args>
  int Blender( double t, int first, Args... more)
{
    return (1-t)*BlenderWithoutLast<Args...>(t, first, more...) + t*Blender(t, more...);
    //    all the magic happens here ^^^^^
}

and now your test case compiles and runs

#include <iostream>
int main()
{
    std::cout << Blender(.5, 23, 42, 89);
}

For me this works with clang and --std=c++11

Ben Voigt
  • 277,958
  • 43
  • 419
  • 720
  • Wonderful! Ubi maior minor cessat. – max66 Dec 18 '17 at 22:14
  • @max66: Trying to figure out how to acknowledge you for thinking of the helper function first, because I wouldn't have found the passing-template-type-actual-parameter that makes it so much simpler without your hint in the direction of a helper function structured to do that. – Ben Voigt Dec 18 '17 at 22:37