Context
First, some context: I use an empty struct called nothing to emulate something like "regular void " to prefix some interfaces that rely on combining several function objects together.
struct nothing { };
Usage example:
when_all([]{ return 0; }, []{ }, []{ return 'a'; }) .then([](int, char){ }); // result of lambda in the middle ignored
In the above example, what actually happens, I collect all the results of function objects passed to when_all in std::tuple , converting void to nothing (in this example: std::tuple<int, nothing, char> ), then I am using a helper function called apply_ignoring_nothing , which calls the function object, unpacking std::tuple , ignoring the nothing elements.
auto f_then = [](int, char){ }; auto args = std::tuple{0, nothing{}, 'a'}; apply_ignoring_nothing(f_then, args);
apply_ignoring_nothing is implemented in terms of call_ignoring_nothing .
Question
I have a call_ignoring_nothing function with the following signature:
template <typename F, typename... Ts> constexpr decltype(auto) call_ignoring_nothing(F&& f, Ts&&... xs);
This function will call f by flawlessly forwarding all xs... for which compile time is_nothing_v<T> returns false .
is_nothing_v defined as follows:
template <typename T> inline constexpr bool is_nothing_v = std::is_same_v<std::decay_t<T>, nothing>;
The way I implemented call_ignoring_nothing is recursive. The base register only accepts f and simply calls it:
#define FWD(x) ::std::forward<decltype(x)>(x) template <typename F> constexpr decltype(auto) call_ignoring_nothing(F&& f) { return returning_nothing_instead_of_void(FWD(f)); }
The recursive case takes f , x and xs... and conditionally binds x as one of the arguments to f if !is_nothing_v<decltype(f)> via lambda. Then it recurses on call_ignoring_nothing passing the newly created lambda as f :
template <typename F, typename T, typename... Ts> constexpr decltype(auto) call_ignoring_nothing(F&& f, T&& x, Ts&&... xs) { return call_ignoring_nothing( [&](auto&&... ys) -> decltype(auto) { if constexpr(is_nothing_v<T>) { return FWD(f)(FWD(ys)...); } else { return FWD(f)(FWD(x), FWD(ys)...); } }, FWD(xs)...); }
I would like to implement call_ignoring_nothing in iterative mode, possibly using a package extension to filter out arguments without recursion.
Is it possible to implement call_ignoring_nothing without recursion? I could not come up with any method to filter the arguments during package expansion.