noxmetus noxmetus - 5 months ago 29
C++ Question

How to change the last argument in the parameter pack?

I have a function f1

template <typename... Args>
void f1(Args... args)
{
// the implementation is just an example, I don't really need a complicated
// way to sum numbers
boost::fusion::vector<Args...> v(args...);
std::cout << boost::fusion::accumulate(v, 0, [](auto i1, auto i2) { return i1 + i2; }) << std::endl;
}


I want to call it from a function f2, but with a different last argument. Is there a simple approach? I tried a naive one

template <typename... Args>
struct CallHelper;

template <>
struct CallHelper<>
{
template <typename... Args>
static void Apply(Args... args) { f1(args...); }
};

template <typename A0>
struct CallHelper<A0>
{
template <typename... Args>
static void Apply(Args ...args, A0 a0)
{
// substitute 10 to the last argument
CallHelper<>::Apply(args..., 10);
}
};

template <typename Head, typename ...TailArgs>
struct CallHelper<Head, TailArgs...>
{
template <typename... Args>
static void Apply(Args... args, Head head, TailArgs ...tailArgs)
{
CallHelper<TailArgs...>::Apply(args..., head, tailArgs...);
}
};

template <typename... Args>
void f2(Args... args)
{
CallHelper<Args...>::Apply(args...);
}


Of course it doesn't work, because
Head head
is not the first argument. Maybe there is a way to make
Head head
a parameter pack as well? Or there is something else I can do?

Answer Source

Unfortunately the ternary operator require the same type for both cases (otherwise the solution would be trivial and without the need of the following getVal class) but, with the help of index sequences...

#include <utility>
#include <iostream>

template <typename ... Args>
void f1 (Args ... args)
 {
   using unused=int[];

   (void)unused { 0, (std::cout << args << ", ", 0)... };

   std::cout << std::endl;
 }

template <std::size_t>
struct getVal
 {
   template <typename T1, typename T2>
   T2 operator() (T1 const &, T2 const & t2)
    { return t2; }
 };

template <>
struct getVal<0U>
 {
   template <typename T1, typename T2>
   T1 operator() (T1 const & t1, T2 const &)
    { return t1; }
 };

template <std::size_t ... Is, typename ... Args>
void f2_helper (std::index_sequence<Is...> const &, Args const & ... args)
 { f1 ( getVal<sizeof...(Is)-Is-1U>()(10, args)... ); }

template <typename ... Args>
void f2 (Args ... args)
 { f2_helper(std::make_index_sequence<sizeof...(Args)>{}, args...); }

int main()
 {
   f1(1, 2L, 3.3, "ten"); // print 1, 2, 3.3, ten,
   f2(1, 2L, 3.3, "ten"); // print 1, 2, 3.3, 10,
 }

It's a C++14 solution (require std::index_sequence and std::make_index_sequence) but should be simple create substitutes for C++11, if you need they.