Home > Enterprise >  Slice parameter pack parameters
Slice parameter pack parameters

Time:08-12

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

CodePudding user response:

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';
}

CodePudding user response:

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");
}

CodePudding user response:

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

  • Related