Overloaded function as an argument to a variational pattern function

I am trying to make a Variadic template function that takes an overloaded function and its arguments as arguments :)

int sumall(int a) { return a; } int sumall(int a, int b) { return a+b; } template<typename R, typename... A> R doit( R(*f)(A...), A... a) { return f(a...); } 

I want to call doit without any template qualifiers or casting:

 cout << doit(sumall, 7, 6) << endl 

This does not compile, but when return types are invalid, everything works perfectly:

 void printsum(int a) { cout << a << endl; } void printsum(int a, int b) { cout << a+b << endl; } template<typename... A> void vdoit( void(*f)(A...), A... a) { f(a...); } // ... vdoit(printsum, 7, 6); 

Is it possible to change the first template to work with modyfing only with the doit template (I want to save the sumall functions and the doit call)? I think this can be done by removing typename R and leaving only template<typename... A> , since R depends on A... and f , but I don’t know how to show this dependency.

+7
source share
2 answers

When using a function pointer, the compiler must know which of the overloads you want to use. It is not possible to pass a pointer to the "overload set" and decide the compiler later. None of your examples work with any of the compilers I tried (the latest versions of EDG, gcc, and clang).

I do not think that you can do what you want without changing the designation of your call. If you want to change the call, you can encapsulate the knowledge about the function that should be called into the class, for example:

 struct sumall_t { template <typename... T> auto operator()(T... args) -> decltype(sumall(args...)) { return sumall(args...); } }; 

This effectively creates a wrapper for the set of overload. Since the type of the result cannot be inferred directly and may depend on how the function is called, you also need to use a different version of doit() :

 template<typename Func, typename... A> auto doit( Func f, A... a) ->decltype(f(a...)) { return f(a...); } 

Then it will be used something like this:

 doit(sumall_t(), 1, 2); 

Another way to fix this is to specify a result type specification: somehow you will try to do two things at once: you want to deduce the result type of the called function and you want the compiler manual to select a specific overload of the result set. However, they are interdependent. If you remove any dependency when outputting any template from the function pointer, you do not need to wrap the overload set, because you can determine the choice of the overloaded function from the first argument of the function. If you claim that "my compiler can do this if the return type is not void " I would say that your compiler is really wrong about that.

+7
source

(If you are ready to use variable macros, scroll to the end of this answer to see the best answer that makes everything completely variable. But I think variable macros are just a g ++ extension.)

This can be done to work if you are ready to specify the function name at the end of the parameter list. Putting it later, the compiler can infer the necessary types from earlier parameters in doit :

 cout << doit(7, 6, sumall) << endl; cout << doit(10, sumall) << endl; 

Here is the daemon on ideone .

The disadvantage is that for each number of parameters you need to implement one doit. I only implemented it for one- and two-parameter functions, but this should not be a problem for its extension:

 int sumall(int a) { return a; } int sumall(int a, int b) { return a+b; } template<typename A1, typename A2, typename R> auto doit( A1 a1, A2 a2, R (*f) (A1,A2)) -> R { return f(a1, a2); } template<typename A1, typename R> auto doit( A1 a1, R (*f) (A1)) -> R { return f(a1); } 

Update: Sometimes it may seem that you can avoid using f as the first argument. But it is not as strong as putting it in the end. Consider an example where where are two functions that take the same number of arguments, but different types of parameters. eg:.

 int sumall(int a, int b) { return a+b; } string sumall(string a, string b) { return a+" "+b; } 

You need to have a function as the last argument, so that the template output can use the type and number of parameters at the beginning to derive the argument types. Here's a demo on the idea of function-arg first and function-arg last .

The only drawback is that the end of the arg argument is that we cannot use variable patterns — the variable arg packages must be at the end. And you have to choose the types correctly - see how I had to use string("hi") instead of just "hi" .

Using Variadic Macros to Have the Best of All Worlds

By implementing doit as a macro and using variable macros (gcc / g ++ extension), it is possible to have a completely variational solution with the first function name. Demo on ideone .

 cout << doit(sumall, 7, 6) << endl; cout << doit(sumall, 10) << endl; cout << doit(sumall, string("hi"), string("world")) << endl; 

Using decltype and a couple of other simple classes, we can use the arguments provided to output the argument types, and then he can use this to select the correct method from the overload set and infer the return type from that.

 template<typename ...Args> struct OverloadResolved { template<typename R> static auto static_doit( R (*f) (Args...), Args ... args ) -> R { return f(args...); } }; template<typename ...Args> auto deduce(Args...) -> OverloadResolved<Args...> { return OverloadResolved<Args...>(); } template<typename T> struct dummy : public T { }; #define doit(f, ...) ( dummy<decltype(deduce( __VA_ARGS__ ))> :: static_doit(f, __VA_ARGS__) ) 

I am sure that it is a safe use of macros, nothing will be evaluated twice (in fact, nothing is executed inside decltype .

+2
source

All Articles