I'm sure this question has been asked before, but I can't seem to find something as simple as what I'm trying to do. Essentially, I just want to make sure the code triggers each parameter, calling any functions that may be sent as inputs. My biggest worry here is that optimizations may remove some of the calls, changing the result.
I was using the following syntax. It seemed to trigger the function calls the way I want, but it has the strange result of triggering the arguments in reverse order - the last argument is called first, and the first argument is called last:
template <typename... PARMS> uint PARMS_COUNT(PARMS&& ... parms) { return static_cast<uint>( sizeof...(parms) ); }
This was my first guess as to how to do the same thing (edit: this does not change the order - it still happens in reverse, because the order is being determined by the function parameter evaluation, rather than what order the function uses them in):
template <typename FIRST>
constexpr uint PARMS_EXPAND(FIRST &&first)
{
return static_cast<uint>( sizeof(first) > 0 ? 1 : 0 );
}
template <typename FIRST,typename... PARMS>
constexpr uint PARMS_EXPAND(FIRST &&first,PARMS&& ... parms)
{
return static_cast<uint>( sizeof(first) > 0 ? 1 : 0 ) + PARMS_EXPAND( std::forward<PARMS>(parms)... );
}
I tested this in a few places, but then realized that regardless of how much testing I do, I'll never know if this is a safe way to do it. Is there a standard or well known method to pull this logic off? Or even better, some built in system to iterate over the arguments and "access them" in the correct order?
To better explain why I would want to trigger code like this, consider a function that can add new objects to a parent:
void AddObject(/*SINGLE UNKNOWN INPUT*/)
{
...
}
template <typename... PARMS> AddObjects(PARMS&& ... parms)
{
PARAMS_EXPAND( AddObject(parms)... );
}
When you write
void AddObject(T);
template <typename... PARMS> AddObjects(PARMS&& ... parms)
{
PARAMS_EXPAND( AddObject(parms)... );
}
you're making a single top-level function call to PARAMS_EXPAND with sizeof...(PARMS) arguments.
What happens inside PARAMS_EXPAND is essentially irrelevant because, like every other function call, the arguments are evaluated before the function is entered. And, like every other function call, the argument expressions are intederminately sequenced with respect to each other.
So, your goal of evaluating AddObject(parms) for each parameter in order has failed before control reaches inside PARAMS_EXPAND at all.
Fortunately, C++17 gave us a way of evaluating operations on parameter packs in order without relying on function call semantics: the fold expression:
(AddObject(parms), ...);
Although this looks a bit like a function call, it's actually a right fold over the comma operator, expanded like
(AddObject(p0) , (... , (AddObject(pN-1) , AddObject(PN))));
which does indeed evaluate its arguments in left-to-right order, exactly as you want.
I answer the original question because it's not clear what you're asking for (see comment) later. If you want n arguments to be called in order, i.e. call operator() on them (e.g. for lambdas, functors), you can do:
constexpr void callEach() {}
template<typename F, typename... Fs>
constexpr void callEach(F f, Fs&&... fs)
{
f();
callEach(std::forward<Fs>(fs)...);
}
Any way that doesn't take lambdas cannot guarantee evaluation order.
In this line:
PARAMS_EXPAND( AddObject(parms)... );
the order of evaluation is not specified because there is no sequence point between each item. Also I'm confused as to why PARAMS_EXPAND does anything if you are going to discard the result. It might as well be this:
template <typename... PARMS>
void PARAMS_EXPAND(PARMS&&...) {}
If you just want to call AddObject with each parms in order, then you need to pass the function and the parameters separately:
void print(int i) {
std::cout << i << " ";
}
template <typename F, typename Head>
constexpr void call_for_each(F&& f, Head&& head)
{
f(head);
}
template <typename F, typename Head, typename... Tail>
constexpr void call_for_each(F&& f, Head&& head, Tail&&... tail)
{
f(std::forward<Head>(head));
call_for_each(std::forward<F>(f), std::forward<Tail>(tail)...);
}
template <typename... Args>
void print_all(Args&&... args)
{
call_for_each(print, std::forward<Args>(args)...);
}
Demo
Here the calls are correctly sequenced: first call f with head, then make the recursive call for the rest of the arguments in tail.
In C++17, you can use a fold expression instead:
template <typename F, typename... Args>
constexpr void call_for_each(F&& f, Args&&... args)
{
(f(std::forward<Args>(args)), ...);
}
Demo
This basically generates a line like so:
(f(std::forward<Args0>(args0), f(std::forward<Args1>(args1), ..., f(std::forward<ArgsN>(argsN));
where ArgsI and argsI are the I-th element of Args and args in the correct order.
Here, the calls are again correctly sequenced because of the comma operator.
Related
The code below doesn't compile (see error below the code). Can you please explain why?
template <class F, class... Arg>
void for_each_argument(F f, Arg&&... arg)
{
f(std::forward<Arg>(arg...));
}
int main()
{
for_each_argument(
[](const auto& a){std::cout<< a;}, "Aa", 3, 4);
return 0;
}
Here is an error message:
7:4: error: expression contains unexpanded parameter pack
'Arg'
f(std::forward(arg...));
You have several issues in your code. First of, your original line
f(std::forward<Arg>(arg...));
Is not correct syntax at all - you are expanding arg without properly expanding Arg in template. Now, you can fix at least that by
f(std::forward<Arg>(arg)...);
This would be better, but still wrong - you will call your lambda once with 3 arguments, while it only accepts one - and instead, you want to call lambda 3 times with a single argument.
There are several ways to do this. First, and the least preferred, is to call the function recursively, as other answer suggests. This prompts ugly syntax, and also adds burden on compiler for recursive template instantiation. Much better solution is to expand the argument using array trick, for example (ignoring the forward for simplicity):
auto lam = [&f](const auto& a) { f(a); return true;}
bool arr[] = { lam(std::forward<ARG>(arg))... };
(void)arr;
In C++ 17 you can use fold expression to achieve even more neat syntax:
(f(std::forward<ARG>(arg)), ...);
Expanding a parameter pack works in contexts that expect a comma separated list.
That is, your code:
f(std::forward<Arg>(arg...));
It attempting to expand into:
f( "Aa", 3, 4 );
And the lambda you have supplied does not support such a call.
To expand a parameter pack into multiple function calls, use a recursive function.
template <class F>
void for_each_argument(F f)
{
// (No args)
}
template <class F, class FirstArg, class... MoreArgs>
void for_each_argument(F f, FirstArg&& first_arg, MoreArgs&&... more_args)
{
f( std::forward<FirstArg>(first_arg) );
for_each_argument( f, std::forward<MoreArgs>(more_args)... );
}
Following with the tuple continuation monad, say I define a functor std_tuple to go from the cathegory of the monad-tuple to std::tuple:
auto std_tuple = [](auto... args)
{
return [=](auto f){ return f(std::make_tuple(args...)); };
};
Now we can use monad-tuples in contexts expecting std::tuple:
template<typename... ARGS>
void f( const std::tuple<ARGS...>& t ){}
int main()
{
tuple(1,2,3)(std_tuple)(f);
}
So far so good. Except this doesn't compile. Clang 3.4.1 complains:
note: candidate template ignored: couldn't infer template argument '$auto-1-0'
on the f(t) call inside the std_tuple functor.
Is this correct, are not those template argumments deducible? In case afirmative, why?
A simple case that reproduces your problem:
void f(int) {}
void f(double) {}
template<class T> void call_with_3( T t ) { t(3); }
int main() {
call_with_3( f );
}
Here we can see that which f to call cannot be determined at the point where we pass it to call_with_3. Now, you seemingly don't have multiple overloads (you only have one f!), but...
A template is not an instance. A template function is a factory of functions, not a function.
There is no object or value there to pass around.
When you pass a function name as an argument, overload resolution kicks in. If the target type is known (as a function reference or pointer) it is used to do overload resolution on the function name.
In this case, you are passing a function name to a template (auto argument), so there is no overload resolution that can be done, so no particular value can be found, so you get an error.
You can create an object whose effect is to do overload resolution on the invoked arguments with a given function name. I call them overload set objects.
static struct f_overload_set_t {
template<class... Args>
auto operator()(Args&&... args) const {
return f(std::forward<Args>(args)...);
}
} f_overload_set;
in C++11 you need a ->decltype( f( std::declval<Args>()... ) ) after the const.
Now f_overload_set(blah) will, when invoked will (almost) do what happens when you f(blah), but f_overload_set is an actual object. So you can pass it around.
Macros that generate such overload sets are relatively easy to write. They can also use lambdas, as the above is a lot like a stateless lambda if you think about it.
The nice thing about the stateless lambda based macro overload set generator is that it can be created at point-of-use. From #dyp's comment above:
#define OVERLOAD_SET( FUNC )\
([](auto&&... args){\
return FUNC(std::forward<decltype(args)>(args)...);\
})
(note: no brackets around FUNC, as that blocks ADL). Brackets around everything else, because otherwise if used within a subscript operation (operator[]), it would be parsed as a [[ starting an attribute, among other spots (thanks to #ecatmur))
which makes your code:
template<typename... ARGS>
void f( const std::tuple<ARGS...>& t ){}
int main() {
tuple(1,2,3)(std_tuple)(OVERLOAD_SET(f));
}
I'm having a problem with the type deduction of variadic template functions. Here is what I'm trying to do:
I have a template class which works as an allocator. In order to choose the right allocator, I need some information about the type parameters. The information is collected in a vector.
The functions creating the actual meta data look like this:
template<typename T>
MetaData* create_meta() {
return new DefaultMetaData();
}
template<MyExampleType>
MetaData* create_meta() {
return new MyExampleTypeMetaData();
}
template<MyOtherType>
MetaData* create_meta() {
etc.
}
And the function collecting the meta data looks like this right now:
template<typename T, typename... Args>
void fill_meta_data(std::vector<MetaData*> &meta) {
meta.push_back(create_meta<T>());
fill_meta_data<Args...>(meta);
}
edit: Trying to clearify the problem:
I want to call the fill_meta_data function like this:
fill_meta_data<MyExampleType, MyExampleType, MyOtherType>(meta_data_vector)
And as a result the meta_data_vector to contain Meta Data for MyExampleType, MyExampleType and MyOtherType
The error I'm getting is "template argument deduction/substitution failed: couldn't deduce template parameter 'T'".
I think that the problem occurs when it tries to deduce the argument for the no-arg version, however, I just can't figure out how to define this default (where it should just return).
I already tried template<> (which is not recognized), template<typename... Args> (the compiler says there are two implementations for more than 0 parameters).
Different solutions for the problem would also be welcome :)
Thanks!
EDIT: Thanks to #catscradle's link:
Here is a solution which worked for me:
I had to add the template
template<typename... Args>
typename std::enable_if<sizeof...(Args) == 0>::type
fill_meta_data(std::vector<MetaData*> &meta) {}
which is only enabled when the size of the Args parameters is zero.
Thanks everybody!
There's a few syntax errors in your code, but there shouldn't be a problem once you sort them out.
Your function specializations are wrong. Instead of:
template<typename T>
MetaData* create_meta() {
return new DefaultMetaData();
}
template<MyExampleType>
MetaData* create_meta() {
return new MyExampleTypeMetaData();
}
Try:
template<typename T>
MetaData* create_meta() {
return new DefaultMetaData();
}
template <>
MetaData* create_meta<MyExampleType>() {
return new MyExampleTypeMetaData();
}
The other issue is that your recursion doesn't have a final function. Some find it helpful to draw out recursion so it makes more sense. If we draw out your recursion with some types, we might get this:
fill_meta_data<short, int, long> // T = short, Args... = int, long
fill_meta_data<int, long> // T = int, Args... = long
fill_meta_data<long> // T = long, Args... =
fill_meta_data<> // T = ???, Args... =
You can see that the final step is undefined because T has no meaning, but it needs an input. So, to "close off" your recursive template function you'll just need a regular function overload with no arguments that does nothing:
void fill_meta_data(std::vector<MetaData*> &meta) {
}
template<typename T, typename... Args>
void fill_meta_data(std::vector<MetaData*> &meta) {
meta.push_back(create_meta<T>());
fill_meta_data<Args...>(meta);
}
Is there a good way to forward arguments of a function f to a function g in a situation like
template<typename... T>
void g(T && ... x);
template<typename... T>
void f(T && ... x)
{
g(x..., x...);
}
In the next code x could be moved twice
template<typename... T>
void f(T && ... x)
{
g(std::forward<T>(x)..., std::forward<T>(x)...);
}
In the next code std::forward<T>(x)... could be evaluated before x...
template<typename... T>
void f(T && ... x)
{
g(x..., std::forward<T>(x)...);
}
std::forward doesn't move things - it creates a reference that says "it is ok to move from me". The actual moving occurs inside g, not in f where std::forward or std::move is called.
The problem of move is only one of the problems here. There is also the problem of passing the same object twice as a reference in two spots, which is generally considered quite rude!
We can fix that by creating temporary objects in f and pass those by reference, but that leads to a serious issue: references are often used to return values from a function, and we have the same variable being used twice -- we cannot return both results.
So the answer is "don't do that", because it is not in general safe. You have to know the semantics of both g and f to figure out what the correct thing to do is, and a simple forwarding type interface won't reflect the depth of knowledge required.
If you do have deep semantic understanding of what g and f are supposed to do, then the situation changes.
You can in general force the order using
using separate statements (obviously)
expressions separated by the comma operator. (Beware of overloaded operator,)
The use of brace initialization works because the order of evaluation of the arguments in a brace initializer list is the order in which they appear1. The following has well-defined evaluation order:
std::tuple<T..., T...> args {
std::forward<T>(x)...,
std::forward<T>(x)... }; // still not sane, but evaluation order defined
But it's still useless as g(...) might still move from the same reference twice. What you'd actually want for rvalue refs is not:
g(rvalue, std::move(rvalue)); // or
g(std::move(rvalue), rvalue); // or even
g(std::move(rvalue), std::move(rvalue)); // [sic]
The only sane way would be:
g(lvalue=std::move(rvalue), lvalue); // BUT: fix the evaluation order
So how do we achieve precisely that but generically?
Enter Indices?!
Let's say you have variadic g as you described:
template<typename... T>
void g(T && ... x)
{
}
Now, you can duplicate the arguments passed to f using
the index trick:
namespace detail // indices
{
template<std::size_t... Is> struct seq{};
template<std::size_t I, std::size_t... Is>
struct gen_seq : gen_seq<I-1, I-1, Is...>{};
template<std::size_t... Is>
struct gen_seq<0, Is...>{ using type = seq<Is...>; };
}
and an invoker helper function:
#include <tuple>
template<typename Tuple, std::size_t... Is>
void f_invoke_helper(Tuple const& tup, detail::seq<Is...>)
{
g(std::get<Is>(tup)..., std::get<Is>(tup)...);
}
All that's required next is to tie it all together:
template<typename... T>
void f(T && ... x)
{
f_invoke_helper(
std::make_tuple(std::forward<T>(x)...),
typename detail::gen_seq<sizeof...(T)>::type());
}
Note that if you pass rvalue-refs, it will get moved once (into the tuple) and used twice (as a lvalue) in the invoker helper:
int main()
{
std::string x = "Hello world";
int i = 42;
// no problem:
f(i, -42, std::move(x));
}
Hope this helps!
PS. As it has been aptly pointed out, it's probably a lot easier to just say
template<typename... T>
void f(T&&... x) { g(x..., x...); }
I haven't thought of a way in which the tuple idiom doesn't result in the same, except for actually moving movable arguments into the tuple.
1The semantics of T{...} are described in 12.6.1See also: how to avoid undefined execution order for the constructors when using std::make_tuple .
I want to do identical processing to a bunch of arguments of a function. Is there a way to loop over all arguments ? I am doing it the way represented in following code, but want to see if there is a compact way to do this.,
void methodA(int a1, int a2, int b1, double b2){
//.. some code
methodB(a1, f(a1));
methodB(a2, f(a2));
methodB(b1, f(b1));
methodB(b2, f(b2));
// more code follows ...
}
int f(int a){
// some function.
return a*10;
}
double f(double b){
return b/2.0;
}
You could use variadic templates:
template <typename T, typename ...Args>
void methodAHelper(T && t, Args &&... args)
{
methodB(t, f(t));
methodAHelper(std::forward<Args>(args)...);
}
void methodAHelper() { }
template <typename ...Args>
void methodA(Args &&... args)
{
// some code
methodAHelper(std::forward<Args>(args)...);
// some other code
}
You can possibly get rid of the && and the forwarding if you know that your methodB call doesn't know about rvalue references, that would make the code a bit simpler (you'd have const Args &... instead), for example:
methodAHelper(const T & t, const Args &... args)
{
methodB(t, f(t));
methodAHelper(args...);
}
You might also consider changing methodB: Since the second argument is a function of the first argument, you might be able to only pass the first argument and perform the call to f() inside the methodB(). That reduces coupling and interdependence; for example, the entire declaration of f would only need to be known to the implementation of methodB. But that's depends on your actual situation.
Alternatively, if there is only one overload of methodB whose first argument is of type T, then you could just pass a std::vector<T> to methodA and iterate over it:
void methodA(const std::vector<T> & v)
{
// some code
for (auto it = v.cbegin(), end = v.cend(); it != end; ++it)
methodB(*it, f(*it));
// some more code
}
int main() { methodA(std::vector<int>{1,2,3,4}); }
Yes there is, the concept you're looking for is called a variadic function.
Depending on what you are trying to do. The simplest thing might to revisit your function and see if it can take an array or std::vector as an argument. Might be much simpler that going the variadic route