#Splitting parameter pack into two smaller parameter packs

25 messages · Page 1 of 1 (latest)

late girder
#

How could I split a parameter pack into two smaller packs using index sequences? This doesn't compile. Something with zero overhead would be nice. In this case, split a pack of 7 into packs of 4 and 3

template <typename T, size_t I>
concept true_concept = true;

template <typename... Ts> requires (sizeof...(Ts) == 7)
void test(Ts&&... args) {
  [&]<size_t... I1, size_t... I2>(std::index_sequence<I1...>, std::index_sequence<I2...>) {
    [&]<true_concept<I1>... Ts1, true_concept<I2>... Ts2>(Ts1&&... args1, Ts2&&... args2) {
      // Now we can use args1... and args2... separately (and also Ts1... and Ts2...)
    }(std::forward<Ts>(args)...);
  }(std::make_index_sequence<4>{}, std::make_index_sequence<3>{});
}
dawn foxBOT
#

When your question is answered use !solved to mark the question as resolved.

Remember to ask specific questions, provide necessary details, and reduce your question to its simplest form. For tips on how to ask a good question use !howto ask.

uncut vessel
#

easiest thing to do is to make tuples and then std::apply

#

do you have c++26?

late girder
#

not really the biggest fan of first putting all values into tuples, but I guess I could also put the types into a tuple type, split the tuple type into two smaller tuple types and then using partial template specialization I could access Ts1... and Ts2... and work from there

#

c++26 has direct access to pack elements at specified indices I think? It would be very nice but I wouldn't want to go that high yet

uncut vessel
#

hm

late girder
#

damn why couldn't they add such a good feature earlier

late girder
#

I guess this also works

template <typename... Ts> requires (sizeof...(Ts) == 7)
void test(Ts&&... args) {
  using tuple_t = std::tuple<Ts...>;
  [&]<size_t... I1>(std::index_sequence<I1...>) {
    [&]<typename... Ts2>(std::tuple_element_t<I1, tuple_t>&&... args1, Ts2&&... args2) {
      // ...
    }(std::forward<Ts>(args)...); 
  }(std::make_index_sequence<4>{});
}
indigo jetty
late girder
#

yeah there are index sequences

indigo jetty
#

okie ˘˘

#

yall metaprogramming people are very scary

uncut vessel
#

nice one

late girder
#
#include <iostream>

template <typename... Ts>
constexpr void test(Ts&&... args) {
}

template <typename... Ts>
void split_pack(Ts&&... args) {
    using tuple_t = std::tuple<Ts...>;
    [&]<size_t... I1>(std::index_sequence<I1...>) {
        [&]<typename... Ts2>(std::tuple_element_t<I1, tuple_t>&&... args1, Ts2&&... args2) {
            std::cout << "hi\n";
            [&]<typename... Ts1>()
            {
                // now I have separate access to args1... and args2..., also Ts1... and Ts2...
                test(std::forward<Ts1>(args1)...);
            }.template operator()<std::tuple_element_t<I1, tuple_t>...>();
        }(std::forward<Ts>(args)...);
    }(std::make_index_sequence<2>{});
}

int main() { split_pack<int, int, float, double, bool>(1, 2, 3.0f, 4.0, false); }
#

so...

#

this runs fine on clang

#

args1 is not captured? I captured though

#

and on MSVC it compiles fine, however it does not execute the std::cout for some reason? If I remove the function call to test it does execute it however?

wispy spade
wispy spade
#

ok great