Inspired by this Stack Oveflow question, I've implemented a function that, when called with a desired arity, a callable object, and a variadic amount of arguments, calls the passed callable object forwarding the passed arguments in groups of the desired arity.
Example:
auto printAll([](auto... xy){ (std::cout << ... << xy); std::cout << " "; });
forNArgs<2> // `2` is the desired arity
(
// `printAll` is the callable object.
printAll,
// Variadic arguments.
0, 1, 2, 3, 4, 5, 6, 7
);
// ...will result in code equivalent to...
printAll(0, 1);
printAll(2, 3);
printAll(4, 5);
printAll(6, 7);
I'm trying to simplify the original code using fold expressions. This is what I have so far:
// "Third step": call `mFn` once getting the correct elements from the forwarded tuple.
template<std::size_t TIStart, typename TF, typename TTpl, std::size_t... TIs>
void forNArgsStep(TF&& mFn, TTpl&& mTpl, std::index_sequence<TIs...>)
{
mFn(std::get<TIStart + TIs>(mTpl)...);
}
// "Second step": use a comma operator fold expression to call the "third step" `numberOfArgs / TArity` times.
template<std::size_t TArity, typename TF, typename TTpl, std::size_t... TIs>
void forNArgsExpansion(TF&& mFn, TTpl&& mTpl, std::index_sequence<TIs...>)
{
using SeqGet = std::make_index_sequence<TArity>;
(forNArgsStep<TIs * TArity>(mFn, mTpl, SeqGet{}), ...);
}
// "First step / interface".
template<std::size_t TArity, typename TF, typename... Ts>
void forNArgs(TF&& mFn, Ts&&... mXs)
{
constexpr auto numberOfArgs(sizeof...(Ts));
static_assert(numberOfArgs % TArity == 0,
"Invalid number of arguments");
auto&& asTpl(std::forward_as_tuple(std::forward<Ts>(mXs)...));
// We need to "convert" the `Ts...` pack into a "list" of packs
// of size `TArity`.
using SeqCalls = std::make_index_sequence<numberOfArgs / TArity>;
forNArgsExpansion<TArity>(mFn, asTpl, SeqCalls{});
}
More examples:
int main()
{
// Prints "01 23 45 67":
forNArgs<2>
(
[](auto... xy){ (std::cout << ... << xy); std::cout << " "; },
0, 1, /**/ 2, 3, /**/ 4, 5, /**/ 6, 7
);
std::cout << "\n";
// Prints "abc def ghi":
forNArgs<3>
(
[](auto... xyz){ (std::cout << ... << xyz); std::cout << " "; },
"a", "b", "c", /**/ "d", "e", "f", /**/ "g", "h", "i"
);
std::cout << "\n";
}
I think my perfect forwarding is incorrect.
- When should I forward the tuple?
- When should I forward the function?
How can I simplify the code further?
- Is there any way of avoiding the two extra helper functions
forNArgsExpansionandforNArgsStep? - Is there any way of avoiding the creation of a tuple? Can the arguments be forwarded in a better way?