Reputation: 1437
In C++ we can pretty easily define template function arguments as seen in this question. However, this only allows functions with a defined parameter list and return value.
We can make those types template parameters like this:
template<typename R, typename P, R(*Func)(P)>
R proxy_func(P arg)
{
// do some stuff...
R&& res{ Func(std::forward(arg)) };
// do some more stuff...
return res;
}
However, here we are still limited to a fixed number of parameters.
An attempt like the following won't compile (at least on MSVC 19.24.28315), because "template parameter 'Func' cannot be used because it follows a template parameter pack and cannot be deduced from the function parameters of 'proxy_func'".
template<typename R, typename... P, R(*Func)(P...)>
R proxy_func(P... args)
{
// do some stuff...
R&& res{ Func(std::forward(args)...) };
// do some more stuff...
return res;
}
So what are the options for allowing an arbitrary function as template argument and - just as importantly - allow its parameters to be used as the parameters for, say, the function template it's used in (like in proxy_func
above)? Ideally, I would be able to call proxy_func
with just any function as (ideally single) template argument Func
and be able to apply its (proxy_func
's) arguments to Func
.
Edit: I am particularly interested in having a function-instance as template argument (e. g. to allow template instantiations of proxy_func
to apply compile-time optimisations based on individual Func
s).
Upvotes: 0
Views: 286
Reputation: 1015
thanks to C++17, auto
can be used as a placeholder of a non-type template parameter, and then we can pass the function pointer without type declarations:
template<auto func, typename... Args>
auto proxy_func(Args&&... args){
// do something...
auto result = func(std::forward<Args>(args)...);
// do something...
return result;
}
and it's extremely exhausting before C++17. if you really want, you have to use a wrapper:
int foo(){ return 0; };
template<typename Ret, typename... Args>
struct func_wrapper{
template<Ret(*func)(Args...)>
struct helper{
typedef Ret return_type;
typedef std::tuple<Args...> parameter_types;
static constexpr Ret (*value)(Args...) = func;
};
};
template<typename Ret, typename... Args>
auto helper(Ret(*)(Args...))->func_wrapper<Ret, Args...>;
template<typename Wrapper, typename... Args>
auto proxy_func(Args&&... args)
->decltype(Wrapper::value(std::forward<Args>(args)...)){
// do something...
auto result = Wrapper::value(std::forward<Args>(args)...);
// do something...
return result;
}
int main(){
typedef decltype(helper(&foo)) wrapper_klass;
typedef wrapper_klass::helper<&foo> wrapper_type;
proxy_func<wrapper_type>();
}
and then, if you want to decide the Args
by func
, you have to use some tricky way to unpack the arguments pack:
template<auto>
struct proxy_func_klass;
template<typename Ret, typename... Args, Ret(*func)(Args...)>
struct proxy_func_klass<func>{
static Ret call(Args... args){
return func(args...);
}
};
and the C++11 version:
int foo(){ return 0; };
template<typename Ret, typename... Args>
struct func_wrapper{
template<Ret(*func)(Args...)>
struct helper{
typedef Ret return_type;
typedef std::tuple<Args...> parameter_types;
static constexpr Ret (*value)(Args...) = func;
};
};
template<typename Ret, typename... Args>
auto helper(Ret(*)(Args...))->func_wrapper<Ret, Args...>;
template<typename, typename, typename>
struct helper_klass;
template<typename Self, typename Ret, typename... Args>
struct helper_klass<Self, Ret, std::tuple<Args...>>{
static Ret call(Args... args){
return Self::value(args...);
}
};
template<typename Wrapper>
struct proxy_func_klass : public helper_klass<proxy_func_klass<Wrapper>, typename Wrapper::return_type, typename Wrapper::parameter_types>{
static constexpr auto value = Wrapper::value;
};
int main(){
typedef decltype(helper(&foo)) wrapper_klass;
typedef wrapper_klass::helper<&foo> wrapper_type;
proxy_func_klass<wrapper_type>::call();
}
Upvotes: 1
Reputation: 122133
In some sense, the Q&A you link is the complicated way. The simple way is:
template <typename F>
void foo(F f) {
f(); // call the function
}
In your case the return value can be deduced and the argument types can be deduced from the parameters to foo
. Note that they need not necessarily match exactly the argument types of f
as long as f
can be called with them:
template <typename F,typename ... Args>
auto foo( F f, Args ... args) {
return f(args...); // call the function
}
For the sake of brevity I ommitted forwarding and storing the result of the call in a temporary, but the answer still applies.
In case you want to have the function itself, not its type as template parameter (sorry I missed that part first) this still works:
template <auto f,typename ... Args>
auto foo(Args ... args) {
return f(args...); // call the function
}
However, this won't work in case f
is a lambda. See here for details: https://en.cppreference.com/w/cpp/language/template_parameters#Non-type_template_parameter
Upvotes: 4