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:
- forward your arguments as tuple
- create an implementation where the "slice" (here all but the last element) is accessed utilizing an index sequence
- 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)...));
}