Home > Software design >  Can you interleave variadic parameters in call sites?
Can you interleave variadic parameters in call sites?

Time:12-24

Is it possible to interleave template parameters inside of function call sites?

I effectively want to do implement the following, but I don't know how (psuedo code):

template <size_t... indices, typename... Ts>
void foo(const Things *things)
{
    static_assert(sizeof...(indices) == sizeof...(Ts));
    constexpr n = sizeof...(Ts);
    bar(
      indices[0], parse<Ts[0]>(things[0]),
      indices[1], parse<Ts[1]>(things[1]),
      ...
      indices[n-1], parse<Ts[n-1]>(things[n-1]));
}

Note: I know the following can be done (psuedo code):

template <size_t... indices, typename... Ts>
void foo(const Things *things)
{
    static_assert(sizeof...(indices) == sizeof...(Ts));
    constexpr n = sizeof...(Ts);
    bar(
      indices[0], indices[1], ..., indices[n-1],
      parse<Ts[0]>(things[0]),
      parse<Ts[1]>(things[1]),
      ...
      parse<Ts[n-1]>(things[n-1]));
}

A partial solution I came up with is to add a swizzling component:

template <typename Func>
decltype(auto) swizzle()
{
    return Func();
}

template <typename Func, typename T0>
decltype(auto) swizzle(size_t i0, T0 &&t0)
{
    return Func(i0, std::forward<T0>(t0));
}

template <typename Func, typename T0, typename T1>
decltype(auto) swizzle(size_t i0, size_t i1, T0 &&t0, T1 &&t1)
{
    return Func(i0, std::forward<T0>(t0), i1, std::forward<T1>(t1));
}

but I think I have to manually write each case per arity I want to consider.

CodePudding user response:

Like this:

template <size_t... indices, typename... Ts>
void foo(const Things *things)
{
    std::apply([](auto...args) {
        bar(args...);
    }, std::tuple_cat(std::make_tuple(indices, parse<Ts>(*(things  )))...));
}

If bar is a lambda instead of a function template, you can just pass bar directly as the first argument to std::apply.

If you want to avoid copying the return values of parse<Ts>(*(things )), you can use std::forward_as_tuple instead of std::make_tuple.

One slightly more verbose alternative if the *(things ) makes you uncomfortable is to use std::index_sequence:

template <size_t... indices, typename... Ts>
void foo(const Things *things)
{
    [=]<auto... Is>(std::index_sequence<Is...>) {
        std::apply([](auto...args) {
            bar(args...);
        }, std::tuple_cat(std::make_tuple(indices, parse<Ts>(things[Is]))...));
    }(std::index_sequence_for<Ts...>());
}
  • Related