1

I would like to split a parameter pack in all the first and one last parameter, but C++ requires the parameter pack be the last in the function declaration, so this is not valid code.

template<typename... Ts, typename Last>
void func( Ts... args, Last last ) {
    cout << "{ ";
    ( (cout << args << ", "), ... ) << last << " }\n";
}

Now I can get there with a bit less nicer code like this:

template<typename T0, typename T1, typename Last>
pair< tuple<T0, T1>, tuple<Last> > slice( T0 t0, T1 t1, Last last ) {
    return { make_tuple( t0, t1 ), make_tuple( last ) };
}

template<typename T0, typename T1, typename T2, typename Last>
pair< tuple<T0, T1, T2>, tuple<Last> > slice( T0 t0, T1 t1, T2 t2, Last last ) {
    return { make_tuple( t0, t1, t2 ), make_tuple( last ) };
}

template<typename... Ts>
void func( Ts... ts ) {
    auto f = [](auto last, auto... args) {
        cout << "{ ";
        ( (cout << args << ", "), ... ) << last << " }\n";
    };
    apply( f, tuple_cat( slice(ts...).second, slice(ts...).first ) );
}

int main() {
    func( "Hello", 4, 5 );
    func( "Hello", 4, 5.4, "Mars"s );
}

But how do I make slice() properly?

https://godbolt.org/z/qbbP1YM9T

Generic Name
  • 1,083
  • 1
  • 12
  • 19
  • FWIW it's pretty easy to just use a stringsteam, add all elements even the unneeded `,` at the end and then just `erase` the last `,` in the string before outputting that string. – NathanOliver Aug 11 '22 at 19:06
  • 2
    Do you actually plan to use this for nice printing? If so, you can take the *first* argument instead, then add a comma *before* each remaining element. – HolyBlackCat Aug 11 '22 at 19:07
  • No. It is not really for printing. It is more about overcoming that I can easily write ( T t, Ts.. ts ) but not so easily ( Ts... ts, T t ). – Generic Name Aug 12 '22 at 10:57

5 Answers5

1

you can convert it to tuple them process it.

void f(){}; // empty pack

template<typename ...Ts>
void f(Ts&&... ts) {
    auto tuple = std::forward_as_tuple(std::forward<Ts>(ts)...);
    constexpr auto size = sizeof...(Ts);
    auto&& last = std::get<size-1>(tuple);
    
    [&]<std::size_t ...I>(std::index_sequence<I...>){
        ((std::cout << std::get<I>(tuple) << " , "), ...);
    }(std::make_index_sequence<size-1>());

    std::cout << last << '\n';
}

but in this case it's simpler to change the order (as @ HolyBlackCat said)

template<typename T, typename ...Ts>
void f(T&& t, Ts&&... ts) {
    std::cout << t;
    ((std::cout << " , " << ts),...);
    std::cout << '\n';
}
apple apple
  • 10,292
  • 2
  • 16
  • 36
1

Here is a slice function.

template<typename ...Ts>
auto slice_last(Ts&&... args){
    auto tuple = std::forward_as_tuple(std::forward<Ts>(args)...);
    constexpr auto size = sizeof...(Ts);
    auto without_last = 
        [&]<std::size_t ...I>(std::index_sequence<I...>){
            return std::forward_as_tuple(std::get<I>(std::move(tuple))...);
        }(std::make_index_sequence<size-1>());
    using last_type = std::tuple_element_t<size-1,decltype(tuple)>; 
    return std::pair<decltype(without_last),last_type>(
        std::move(without_last),
        std::get<size-1>(std::move(tuple))
    );
}

// use
template<typename... Ts>
void f(Ts&&... ts) {
    auto [pack,last] = slice_last(std::forward<Ts>(ts)...);
    
    cout << "{ ";
    std::apply(
        [](auto&&...ts){( (std::cout<<ts<<" ,"), ... );}
        ,pack
    );
    std::cout << last << " }\n";
}
apple apple
  • 10,292
  • 2
  • 16
  • 36
0

another option is to use the good old recursion

template<typename T>
void f_impl(T&& last){
    std::cout << last << " }\n";
}

template<typename T, typename... Ts>
requires (sizeof...(Ts)!=0)
void f_impl(T t, Ts&&... ts) {
    std::cout << t << ", ";
    f_impl(std::forward<Ts>(ts)...);
}

template<typename... Ts>
void f(Ts&&... ts) {
    std::cout << "{ ";
    f_impl(std::forward<Ts>(ts)...);
}
apple apple
  • 10,292
  • 2
  • 16
  • 36
-1

Here's a relatively simple implementation of args_filter_apply(), which calls a lambda with a runtime-filtered argument pack. It's not yet optimal, but you might get the idea:

#include <iostream>

template<typename Filter, typename Funct>
auto args_filter_apply(Filter filter, Funct funct)
{
    return funct();
}

template<typename Filter, typename Funct, typename Arg, typename... Args>
auto args_filter_apply(Filter&& filter, Funct&& funct, Arg& arg, Args&... args)
{
    if (filter(arg)) {
        return args_filter_apply(std::forward<Filter>(filter), [&](auto... args1) {
            return std::forward<Funct>(funct)(arg, args1...);
        }, args...);
    }
    return args_filter_apply(std::forward<Filter>(filter), std::forward<Funct>(funct), args...);
}


template<typename... Ts>
void func(Ts... args) {
    std::cout << "{ ";

    int i = 0;
    args_filter_apply([&](auto arg) {
        return ++i != sizeof...(args);
    }, [](auto... myArgs) {
        ( (std::cout << myArgs << ", "), ... );
    }, args...);

    i = 0;
    args_filter_apply([&](auto arg) {
        return ++i == sizeof...(args);
    }, [](auto... last) {
        ( (std::cout << last), ...);
        std::cout << " }\n";
    }, args...);
}

int main()
{
    func("Hello", 4, 5);
    func("Hello", 4, 5.4, "Mars");
}
lorro
  • 10,687
  • 23
  • 36
-1

It's easy to create any slice desired if you first pack your arguments in a sequence. Since the standard does not provide a dedicated facility, tuples are usually used for that purpose.

So this is the workflow:

  1. forward your arguments as tuple
  2. create an implementation where the "slice" (here all but the last element) is accessed utilizing an index sequence
  3. Use the last element as you please

Such an implementation is shown below:

template <std::size_t... Is, class Tuple>
void func_impl(std::index_sequence<Is...>, Tuple&& t)
{
    cout << "{ ";
    ( (cout << std::get<Is>(t) << ", "), ... ) 
    << std::get<std::tuple_size_v<Tuple> - 1>(t) 
    << " }\n";
}

template<typename... Ts>
void func(Ts&&... args)
{
    func_impl(
        std::make_index_sequence<sizeof...(Ts) - 1>{}, 
        std::forward_as_tuple(std::forward<Ts>(args)...));
}

Demo

Lorah Attkins
  • 5,331
  • 3
  • 29
  • 63