Germán Diago
Germán Diago

Reputation: 7673

Cannot expand a tuple pair in lockstep for a function call in index_sequence context

The question is simple (but not so the solution I guess...).

I want to apply a function to index 0 of n tuples with args, later to index 1 of tuple the same tuples with args... and so on.

Given this:


template <auto Func, class... Tuples, class... Fargs, std::size_t... Is>
auto applyInLockStep(std::index_sequence<Is...>,
                     Tuples &&... tups,
                     Fargs &&...args) {
  return (Func(std::get<Is>(tups), std::forward<Fargs>(args)...), ...);
}

And assuming this is the call:

using Tup = std::tuple<float, float, float>;
Tup t1, t2;

constexpr auto f = [&]<class T1E, class T2E>(T1E && t1E,
                                   T2E && t2E,
                                   float t) {
   return myf(std::forward<T1E>(t1E), std::forward<T2E>(t2E), t);
};

applyInLockStep<f>(make_index_sequence<tuple_size_v<Tup1>>{}, t1, t2, 1.0f);

I want this expansion:

Func(std::get<0>(tup0), std::get<0>(tup1), 1.0f), 
Func(std::get<1>(tup0), std::get<1>(tup1), 1.0f), 
...

The problem is in this expression, in std::get<Is>(tups) concretely:

Func(std::get<Is>(tups), std::forward<Fargs>(args)...), ...)

I want to expand tups for the same index, but not Is, but if I do this it will only expand tups inside std::get<0>(tup1, tup2), which I do not want:

Func(std::get<Is>(tups...), std::forward<Fargs>(args)...), ...)

And if I do this:

Func(std::get<Is>(tups)..., std::forward<Fargs>(args)...), ...)

it will expand tups and Is..., but I do not want Is to be expanded.

Not sure if it is doable or not and how.

Upvotes: 1

Views: 75

Answers (1)

Jarod42
Jarod42

Reputation: 217448

At first, in

// ..
auto applyInLockStep(std::index_sequence<Is...>,
                     Tuples &&... tups,
                     Fargs &&...args)

FArgs is non-deducible, Tuples would deduce all, so FArgs would be an empty pack.

I will bypass by grouping tups inside another std::tuple

// ..
auto applyInLockStep(std::index_sequence<Is...>,
                     std::tuple<Tuples...> tups,
                     Fargs &&...args)

then, you might create sub-function to allow variadic expansion in the direction you want:

template <auto Func,
          class... Tuples,
          class... Fargs,
          std::size_t I>
auto applyInLockStep(std::integral_constant<std::size_t, I>,
                     std::tuple<Tuples...> tups,
                     Fargs &&...args)
{
    return std::apply([&](auto&&... tup) {
         return Func(std::get<I>(tup)..., std::forward<Fargs>(args)...);
    }, tups);
}

template <auto Func,
          class... Tuples,
          class... Fargs,
          std::size_t... Is>
auto applyInLockStep(std::index_sequence<Is...>,
                     std::tuple<Tuples...> tups,
                     Fargs &&...args)
{
    return (applyInLockStep<Func>(std::integral_constant<std::size_t, Is>{},
                                  std::forward<std::tuple<Tuples...>>(tups),
                                  std::forward<Fargs>(args)...),
            ...);
}

Demo

Upvotes: 2

Related Questions