42

I want to pass a function value as a template parameter to a function. Currently the best I managed to do is :

template< typename F, F f >
void pass()
{
    ...
}

...which is used:

pass< decltype(&func), &func >();

What I would really like is to have:

pass< &func >();

Is there any way to achieve this without macros? Basically to pass both the type and the value at the same time? The compiler obviously has all the information needed for that...

The solution must work with variable parameters and return types. The function value is used at compile time, so it cannot be passed as an argument.

C++11 solutions welcome.


Edit: use case - I'm generating bindings at compile-time, where I need to create a C++ function for each passed function. The use case of this code looks (simplified) more or less like this:

template < typename F, F f > 
int function_wrapper( lua_State* L ) 
{
    return dispatcher<typename return_type<F>::type>::call( L, 1, f );
}

void register_native_function( lua_Function f, const char* name )
{
    // binding call using pure C function f
}

template < typename F, F f >
void register_function( const char* name )
{
    register_native_function( function_wrapper< F, f >, name );
}

Please note that I need to create a compile-time function wrapper, so I need the pass function value at compile time. There are binding solutions that allow binding at runtime, but they always require boilerplate code compared to hand-written bindings. I'm aiming to achieve a hand-written performance here.

user1095108
  • 14,119
  • 9
  • 58
  • 116
Kornel Kisielewicz
  • 55,802
  • 15
  • 111
  • 149
  • 2
    did you measure whether `template pass (F fun)` gives you any overhead at all? The fundamental problem is that you cannot pass anything other than an integral value as non-type template parameter. – TemplateRex Jun 12 '14 at 13:42
  • Passing the function doesn't (obviously) give any overhead, but the need to use a generic call function is significantly visible when binding things like a ``vec3d`` class whose methods may be called thousands of times during a frame. – Kornel Kisielewicz Jun 12 '14 at 13:57
  • @TemplateRex "you cannot pass anything other than an integral value" --- this is not quite true. – n. m. could be an AI Jun 14 '14 at 19:32
  • The only way to pass a non-type template parameter of a type not known to the template is to pass its type as a type template parameter, then to pass the value. So `decltype(&func), &func` is about the best you can get. – n. m. could be an AI Jun 14 '14 at 19:44
  • I'm currently trying to find a hack that could use a temporary constructed class instead of a function call, seems there might be a (not-ideal) alternative... – Kornel Kisielewicz Jun 14 '14 at 20:46
  • 1
    http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2013/n3601.html – dyp Jun 14 '14 at 23:44
  • Interestingly, you could do this solely with the type when you had a (default-constructible) function object instead of a function. Unfortunately, lambdas have deleted default ctors, otherwise we could use them just for their type. – dyp Jun 14 '14 at 23:50
  • Another possible, but weird approach is to just use just the function pointer as a fixed-type non-type template argument. If a fixed set of function types is required, you could overload some function templates with different non-type template parameters. – dyp Jun 14 '14 at 23:52
  • I'm curious, why you discount use of a macro as an option as it seems to be the most obvious and straightforward solution? – JarkkoL Jun 15 '14 at 00:37
  • @dyp problem is this is a binding library, so there's no limit to the amount of overloads. And yeah, I'd love to see N3601 accepted... fast :P. – Kornel Kisielewicz Jun 15 '14 at 08:21
  • @JarkkoL it's a member function, so the best that could be done is `.register_function("name");` which is ugly and not properly scoped, etc, etc. I probably will go with this but it breaks my code conventions :/. – Kornel Kisielewicz Jun 15 '14 at 08:23
  • What about `reg(func).reg()`? Not as convenient as a macro, but doesn't need `decltype`. – dyp Jun 15 '14 at 14:31
  • @KornelKisielewicz Or alternatively you could define it like: `#define register_function(func__, name__) register_function_impl(name__)` in order to have: `.register_function(func, "name");` – JarkkoL Jun 15 '14 at 16:55
  • I've made a similar question [here](http://stackoverflow.com/questions/19857444/wildcard-function-pointer-non-type-template-parameter). – user1095108 Jun 17 '14 at 12:13

3 Answers3

26

It's now possible in C++17 with template<auto>:

template<auto Func>
struct FuncWrapper final
{
    template<typename... Args>
    auto operator()(Args &&... args) const
    {
        return Func(std::forward<Args>(args)...);
    }
};

int add(int a, int b)
{
    return a + b;
}

int main()
{
    FuncWrapper<add> wrapper;
    return wrapper(12, 34);
}

Demo: https://godbolt.org/g/B7W56t

You can use #ifdef __cpp_nontype_template_parameter_auto to detect compiler support for this in your code.

If you are able to use C++20 and you want better error messages, you can also use concepts:

template<typename T>
concept CanAddTwoNumbers = std::is_invocable_r_v<int, T, int, int>;

template<auto Func>
    requires CanAddTwoNumbers<decltype(Func)>
struct AddTwoNumbersWrapper final
{
    auto operator()(int a, int b) const
    -> int
    {
        return std::invoke(Func, a, b);
    }
};

int add(int a, int b)
{
    return a + b;
}

int main()
{
    AddTwoNumbersWrapper<add> wrapper;
    return wrapper(12, 34);
    AddTwoNumbersWrapper<123> bad; //error: constraint failure
}

Demo: https://gcc.godbolt.org/z/ai3WGH

LB--
  • 2,506
  • 1
  • 38
  • 76
  • I wonder if you could extend you solution for overloaded fucntion. for example if your have and add that takes int and an add that takes float, I beleive `template< auto Func>`can't resolve overload sets and compilation fails – monamimani Jan 29 '22 at 21:16
  • @monamimani You can just cast the overloaded function name to the function signature you want to use, just the same as in any other case you are dealing with overloaded functions. – LB-- Jan 29 '22 at 22:05
  • Yes thanks, but is makes for ugly caller code. I just ask a question to see if I can't improve on it. https://stackoverflow.com/questions/70911263/ – monamimani Jan 30 '22 at 02:07
  • @monamimani Unfortunately this appears to be an inherent problem with how C++ handles overloaded functions. The only way to deal with them is to either call them or cast them. You can't do anything special or automatic with the whole set of overloads overall. At best you could use a forwarding lambda. – LB-- Feb 01 '22 at 20:40
17

I believe shortening this is currently impossible. A year ago, the C++ committee looked at http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2013/n3601.html to fix this, and they encouraged the authors to pursue it further after C++14 was released.

Jeffrey Yasskin
  • 5,171
  • 2
  • 27
  • 39
  • 5
    FWIW, I've been arguing for using the more compact notation `template` (retrieve the type part with `decltype`). I believe that now has traction, so the revision of the paper will hopefully suggest that. – Daveed V. Jun 23 '14 at 16:05
0

You need to make a typedef for whatever function type you want to pass as a pointer, like this:

typedef int (*MyFunctionType)(int);

template <typename FunctionTypedef, typename ReturnType, typename ParameterType>
ReturnType callFunction(FunctionTypedef functionPointer, ParameterType i)
{
  static MyFunctionType myFunctionPointer = functionPointer;
  return (*functionPointer)(i);
}
int myFunction(int i)
{
}


int main()
{
  int i = 7;
  MyFunctionType myFunctionPointer = myFunction;
  return callFunction<MyFunctionType, int, int>(myFunctionPointer, i);
}

Edit: If you want to store these arbitrarily typed function pointers, then make a base class with a virtual "call function" function, and a templated derived class that implements this function.

deek0146
  • 962
  • 6
  • 20