Best way to apply a void function to a parameter pack - c++

I would like to write a function that applies a function to each element of a parameter pack. The functions returns a std::tuple with the results of each invocation.
However, if the applied function returns void, I have to do something else, so I have a different overload for this case. But, almost all the ways I've found to expand the parameter pack do not work with void expressions, so I had to resort to what you see below, which seems a weird trick.
template<typename F, typename ...Args>
requires requires { std::tuple{std::declval<F>(Args)...}; }
auto for_each(F f, Args ...args) {
return std::tuple{f(args)...};
}
template<typename F, typename ...Args>
auto for_each(F f, Args ...args)
[[maybe_unused]] int a[] = {(f(Members), 0)...};
}
Note that I have to declare a unused variable and mark it with the attribute.
Which is the best way to obtain the expected result here?

This trick is the way to go pre-C++17, except that you need an extra , 0 in the array to support zero-length packs.
In C++17 and newer, use a fold expression: (f(args), ...);.
Note that you forgot perfect forwarding. You should be doing F &&f, Args &&... args, and then (f(std::forward<Args>(args)), ...);, and similarly for the first function.
I also question the value of having such a function. I'd understand doing this to a tuple, but if you already have a pack, you can do this manually at the call site.

Related

How to understand this C++ syntax? [duplicate]

template <class F, class... Args>
void for_each_argument(F f, Args&&... args) {
[](...){}((f(std::forward<Args>(args)), 0)...);
}
It was recently featured on isocpp.org without explanation.
The short answer is "it does it not very well".
It invokes f on each of the args..., and discards the return value. But it does so in a way that leads to unexpected behavior in a number of cases, needlessly.
The code has no ordering guarantees, and if the return value of f for a given Arg has an overloaded operator, it can have unfortunate side effects.
With some white space:
[](...){}(
(
f(std::forward<Args>(args)), 0
)...
);
We will start from the inside.
f(std::forward<Args>(args)) is an incomplete statement that can be expanded with a .... It will invoke f on one of args when expanded. Call this statement INVOKE_F.
(INVOKE_F, 0) takes the return value of f(args), applies operator, then 0. If the return value has no overrides, this discards the return value of f(args) and returns a 0. Call this INVOKE_F_0. If f returns a type with an overriden operator,(int), bad things happen here, and if that operator returns a non-POD-esque type, you can get "conditionally supported" behavior later on.
[](...){} creates a lambda that takes C-style variadics as its only argument. This isn't the same as C++11 parameter packs, or C++14 variadic lambdas. It is possibly illegal to pass non-POD-esque types to a ... function. Call this HELPER
HELPER(INVOKE_F_0...) is a parameter pack expansion. in the context of invoking HELPER's operator(), which is a legal context. The evaluation of arguments is unspecified, and due to the signature of HELPER INVOKE_F_0... probably should only contain plain old data (in C++03 parlance), or more specifically [expr.call]/p7 says: (via #T.C)
Passing a potentially-evaluated argument of class type (Clause 9) having a nontrivial copy constructor, a non-trivial move constructor, or a non-trivial destructor, with no corresponding parameter, is conditionally-supported with implementation-defined semantics.
So the problems of this code is that the order is unspecified and it relies on well behaved types or specific compiler implementation choices.
We can fix the operator, problem as follows:
template <class F, class... Args>
void for_each_argument(F f, Args&&... args) {
[](...){}((void(f(std::forward<Args>(args))), 0)...);
}
then we can guarantee order by expanding in an initializer:
template <class F, class... Args>
void for_each_argument(F f, Args&&... args) {
int unused[] = {(void(f(std::forward<Args>(args))), 0)...};
void(unused); // suppresses warnings
}
but the above fails when Args... is empty, so add another 0:
template <class F, class... Args>
void for_each_argument(F f, Args&&... args) {
int unused[] = {0, (void(f(std::forward<Args>(args))), 0)...};
void(unused); // suppresses warnings
}
and there is no good reason for the compiler to NOT eliminate unused[] from existance, while still evaluated f on args... in order.
My preferred variant is:
template <class...F>
void do_in_order(F&&... f) {
int unused[] = {0, (void(std::forward<F>(f)()), 0)...};
void(unused); // suppresses warnings
}
which takes nullary lambdas and runs them one at a time, left to right. (If the compiler can prove that order does not matter, it is free to run them out of order however).
We can then implement the above with:
template <class F, class... Args>
void for_each_argument(F f, Args&&... args) {
do_in_order( [&]{ f(std::forward<Args>(args)); }... );
}
which puts the "strange expansion" in an isolated function (do_in_order), and we can use it elsewhere. We can also write do_in_any_order that works similarly, but makes the any_order clear: however, barring extreme reasons, having code run in a predictable order in a parameter pack expansion reduces surprise and keeps headaches to a minimum.
A downside to the do_in_order technique is that not all compilers like it -- expanding a parameter pack containing statement that contains entire sub-statements is not something they expect to have to do.
Actually it calls function f for each argument in args in unspecified order.
[](...){}
create lambda function, that does nothing and receives arbitrary number of arguments (va args).
((f(std::forward<Args>(args)), 0)...)
argument of lambda.
(f(std::forward<Args>(args)), 0)
call f with forwarded argument, send 0 to lambda.
If you want specified order you can use following thing:
using swallow = int[];
(void)swallow{0, (f(std::forward<Args>(args)), 0)...};

Using n arguments from Args... starting from position m

Say I have different functions, which have a variable number of arguments. The first argument is always a pointer obtained through other means. All other arguments I obtain through another template using template pack expansion.
Template that I use for calling these functions is as follows:
template<typename RT, typename... Args>
inline RT call(RT(*function)(Args...))
{
return function(pointer_from_somewhere, bind_argument<Args>::get_arg()...);
}
This obviously doesn't compile, as it performs template expansion for all arguments, thus there being too many arguments.
Since I always obtain the first argument through other means, how would I do template pack expansion for sizeof...(Args) - 1 arguments, starting from the 2nd argument?
EDIT:
While the template is slimmed down for demonstration purposes, it might be relevant, that the 1st argument (the pointer) is reinterpret_cast'ed always to the type of first argument. I use std::tuple_element<0, std::tuple<Args...>>::type for finding out the type of the 1st argument.
Is this what you are looking for?
template<typename RT, typename A0, typename... Args>
inline RT call(RT(*function)(A0, Args...))
{
return function(pointer_from_somewhere, bind_argument<Args>::get_arg()...);
}

What does this variadic template code do?

template <class F, class... Args>
void for_each_argument(F f, Args&&... args) {
[](...){}((f(std::forward<Args>(args)), 0)...);
}
It was recently featured on isocpp.org without explanation.
The short answer is "it does it not very well".
It invokes f on each of the args..., and discards the return value. But it does so in a way that leads to unexpected behavior in a number of cases, needlessly.
The code has no ordering guarantees, and if the return value of f for a given Arg has an overloaded operator, it can have unfortunate side effects.
With some white space:
[](...){}(
(
f(std::forward<Args>(args)), 0
)...
);
We will start from the inside.
f(std::forward<Args>(args)) is an incomplete statement that can be expanded with a .... It will invoke f on one of args when expanded. Call this statement INVOKE_F.
(INVOKE_F, 0) takes the return value of f(args), applies operator, then 0. If the return value has no overrides, this discards the return value of f(args) and returns a 0. Call this INVOKE_F_0. If f returns a type with an overriden operator,(int), bad things happen here, and if that operator returns a non-POD-esque type, you can get "conditionally supported" behavior later on.
[](...){} creates a lambda that takes C-style variadics as its only argument. This isn't the same as C++11 parameter packs, or C++14 variadic lambdas. It is possibly illegal to pass non-POD-esque types to a ... function. Call this HELPER
HELPER(INVOKE_F_0...) is a parameter pack expansion. in the context of invoking HELPER's operator(), which is a legal context. The evaluation of arguments is unspecified, and due to the signature of HELPER INVOKE_F_0... probably should only contain plain old data (in C++03 parlance), or more specifically [expr.call]/p7 says: (via #T.C)
Passing a potentially-evaluated argument of class type (Clause 9) having a nontrivial copy constructor, a non-trivial move constructor, or a non-trivial destructor, with no corresponding parameter, is conditionally-supported with implementation-defined semantics.
So the problems of this code is that the order is unspecified and it relies on well behaved types or specific compiler implementation choices.
We can fix the operator, problem as follows:
template <class F, class... Args>
void for_each_argument(F f, Args&&... args) {
[](...){}((void(f(std::forward<Args>(args))), 0)...);
}
then we can guarantee order by expanding in an initializer:
template <class F, class... Args>
void for_each_argument(F f, Args&&... args) {
int unused[] = {(void(f(std::forward<Args>(args))), 0)...};
void(unused); // suppresses warnings
}
but the above fails when Args... is empty, so add another 0:
template <class F, class... Args>
void for_each_argument(F f, Args&&... args) {
int unused[] = {0, (void(f(std::forward<Args>(args))), 0)...};
void(unused); // suppresses warnings
}
and there is no good reason for the compiler to NOT eliminate unused[] from existance, while still evaluated f on args... in order.
My preferred variant is:
template <class...F>
void do_in_order(F&&... f) {
int unused[] = {0, (void(std::forward<F>(f)()), 0)...};
void(unused); // suppresses warnings
}
which takes nullary lambdas and runs them one at a time, left to right. (If the compiler can prove that order does not matter, it is free to run them out of order however).
We can then implement the above with:
template <class F, class... Args>
void for_each_argument(F f, Args&&... args) {
do_in_order( [&]{ f(std::forward<Args>(args)); }... );
}
which puts the "strange expansion" in an isolated function (do_in_order), and we can use it elsewhere. We can also write do_in_any_order that works similarly, but makes the any_order clear: however, barring extreme reasons, having code run in a predictable order in a parameter pack expansion reduces surprise and keeps headaches to a minimum.
A downside to the do_in_order technique is that not all compilers like it -- expanding a parameter pack containing statement that contains entire sub-statements is not something they expect to have to do.
Actually it calls function f for each argument in args in unspecified order.
[](...){}
create lambda function, that does nothing and receives arbitrary number of arguments (va args).
((f(std::forward<Args>(args)), 0)...)
argument of lambda.
(f(std::forward<Args>(args)), 0)
call f with forwarded argument, send 0 to lambda.
If you want specified order you can use following thing:
using swallow = int[];
(void)swallow{0, (f(std::forward<Args>(args)), 0)...};

Demystifying Sean Parent's for_each_argument [duplicate]

template <class F, class... Args>
void for_each_argument(F f, Args&&... args) {
[](...){}((f(std::forward<Args>(args)), 0)...);
}
It was recently featured on isocpp.org without explanation.
The short answer is "it does it not very well".
It invokes f on each of the args..., and discards the return value. But it does so in a way that leads to unexpected behavior in a number of cases, needlessly.
The code has no ordering guarantees, and if the return value of f for a given Arg has an overloaded operator, it can have unfortunate side effects.
With some white space:
[](...){}(
(
f(std::forward<Args>(args)), 0
)...
);
We will start from the inside.
f(std::forward<Args>(args)) is an incomplete statement that can be expanded with a .... It will invoke f on one of args when expanded. Call this statement INVOKE_F.
(INVOKE_F, 0) takes the return value of f(args), applies operator, then 0. If the return value has no overrides, this discards the return value of f(args) and returns a 0. Call this INVOKE_F_0. If f returns a type with an overriden operator,(int), bad things happen here, and if that operator returns a non-POD-esque type, you can get "conditionally supported" behavior later on.
[](...){} creates a lambda that takes C-style variadics as its only argument. This isn't the same as C++11 parameter packs, or C++14 variadic lambdas. It is possibly illegal to pass non-POD-esque types to a ... function. Call this HELPER
HELPER(INVOKE_F_0...) is a parameter pack expansion. in the context of invoking HELPER's operator(), which is a legal context. The evaluation of arguments is unspecified, and due to the signature of HELPER INVOKE_F_0... probably should only contain plain old data (in C++03 parlance), or more specifically [expr.call]/p7 says: (via #T.C)
Passing a potentially-evaluated argument of class type (Clause 9) having a nontrivial copy constructor, a non-trivial move constructor, or a non-trivial destructor, with no corresponding parameter, is conditionally-supported with implementation-defined semantics.
So the problems of this code is that the order is unspecified and it relies on well behaved types or specific compiler implementation choices.
We can fix the operator, problem as follows:
template <class F, class... Args>
void for_each_argument(F f, Args&&... args) {
[](...){}((void(f(std::forward<Args>(args))), 0)...);
}
then we can guarantee order by expanding in an initializer:
template <class F, class... Args>
void for_each_argument(F f, Args&&... args) {
int unused[] = {(void(f(std::forward<Args>(args))), 0)...};
void(unused); // suppresses warnings
}
but the above fails when Args... is empty, so add another 0:
template <class F, class... Args>
void for_each_argument(F f, Args&&... args) {
int unused[] = {0, (void(f(std::forward<Args>(args))), 0)...};
void(unused); // suppresses warnings
}
and there is no good reason for the compiler to NOT eliminate unused[] from existance, while still evaluated f on args... in order.
My preferred variant is:
template <class...F>
void do_in_order(F&&... f) {
int unused[] = {0, (void(std::forward<F>(f)()), 0)...};
void(unused); // suppresses warnings
}
which takes nullary lambdas and runs them one at a time, left to right. (If the compiler can prove that order does not matter, it is free to run them out of order however).
We can then implement the above with:
template <class F, class... Args>
void for_each_argument(F f, Args&&... args) {
do_in_order( [&]{ f(std::forward<Args>(args)); }... );
}
which puts the "strange expansion" in an isolated function (do_in_order), and we can use it elsewhere. We can also write do_in_any_order that works similarly, but makes the any_order clear: however, barring extreme reasons, having code run in a predictable order in a parameter pack expansion reduces surprise and keeps headaches to a minimum.
A downside to the do_in_order technique is that not all compilers like it -- expanding a parameter pack containing statement that contains entire sub-statements is not something they expect to have to do.
Actually it calls function f for each argument in args in unspecified order.
[](...){}
create lambda function, that does nothing and receives arbitrary number of arguments (va args).
((f(std::forward<Args>(args)), 0)...)
argument of lambda.
(f(std::forward<Args>(args)), 0)
call f with forwarded argument, send 0 to lambda.
If you want specified order you can use following thing:
using swallow = int[];
(void)swallow{0, (f(std::forward<Args>(args)), 0)...};

variadic template arguments unpacking

For each argument I need apply two nested function:
obj.apply(someFilter(arg)); // arg is one argument, but here
// should be an unpacking of args
I don't know how to write unpacking for such case.
I saw this:
pass{([&]{ std::cout << args << std::endl; }(), 1)...};
on wiki, but again don't know how to apply this for my case.
It's actually quite simple:
You can put arbitrary expression inside the unpack of an variadic templates argument pack:
obj.apply(someFilter(arg))...
This will give you the result of obj.apply as a coma seperated list. You can then pass it to a dummy function:
template<typename... Args> swallow (Args&&...) {}
swallow(obj.apply(someFilter(arg))...);
To swallow the comma seperated list.
Of course, this assumes that obj.apply returns some kind of object. If not you can use
swallow((obj.apply(someFilter(arg)), 0)...);
to make actual (non void) arguments
If you don't know what obj.apply` returns (result might have overloaded the comma operator), you can disable the use of custom comma operators by using
swallow((obj.apply(someFilter(arg)), void(), 0)...);
Should you actually need to evaluate the items in order (which doesn't seem very likely from the question), you can abuse array initialization syntax instead of using a function call:
using Alias=char[];
Alias{ (apply(someFilter(args)), void(), '\0')... };
Here is a robust way to do an arbitrary set of actions on a parameter pack. It follows the principle of least surprise, and does the operations in order:
template<typename Lambda, typename Lambdas>
void do_in_order( Lambda&& lambda, Lambdas&& lambdas )
{
std::forward<Lambda>(lambda)();
do_in_order( std::forward<Lambdas>(lambdas)... );
}
void do_in_order() {}
template<typename Args>
void test( Args&& args ) {
do_in_order( [&](){obj.apply(someFilter(std::forward<Args>(args)));}... );
}
Basically, you send a pile of lambdas at do_in_order, which evaluates them front to back.
I assume that the code has multiple args as a parameter pack? Try:
obj.apply( someFilter( arg )... );
as parameter unpacking applies to the expression, so each element of the parameter pack get's expanded into someFilter( arg ).