Max
Max

Reputation: 101

Passing a function pointer as non-type template parameter

I have this template function, which is working well:

    template<typename RES, typename... PARMS> SQInteger GlobalBind(RES(*fn)(PARMS... parms), const char *sqName, HSQUIRRELVM v)

Which is called like this :

GlobalBind(aFunction, "aName", vm);

Its scope is binding function fn to a scripting engine.

Using it in this way, I must keep the fn pointer AND a proxy template pointer inside of the scripting engine; the proxy template (not shown here) is called with fn as parameter, does some stuff, and then calls fn.

What I'd like to achieve is to remove the fn pointer from the function call, and put it into the template as a parameter, something like this:

template<typename RES, typename... PARMS, RES(*fn)(PARMS...)> SQInteger GlobalBind(const char *sqName, HSQUIRRELVM v)

So, a specific template is instantiated for each different fn, which means that I could avoid the fn pointer in the scripting language; the call should be:

GlobalBind<aFunction>("aName", vm);

The template declaration is accepted by the compiler, but the call brings errors, even adding the parameter types before aFunction, like this (assuming aFunction returns and int and has const char * as a parameter):

GlobalBind<int, const char *, aFunction>("aName", vm);

Is there a way to achieve this result (the former, without a parameters list)?

EDIT: to simplify the question, is the template

template<typename RES, typename... PARMS, RES(*fn)(PARMS...)> RES TEST(PARMS... parms) {
    return fn(parms...);
}

a valid one ? And if yes... how should it be called ? I tried this :

int testfn(const char *s) { return strlen(s); }
TEST<testfn>("aString");   << ERROR
TEST<int, const char *, testfn>("aString");  << ERROR

EDIT2: This one :

template<int (*fn)(const char *)> int TEST2(const char *s) {
    return fn(s);
}
TEST2<testfn>("aString");

works, but it's not useful for my purpose

Upvotes: 0

Views: 781

Answers (2)

Max
Max

Reputation: 101

Found an (ugly) solution, but it works also for overloaded functions :

int myFunc(const char *s) {
    return strlen(s);
}

const char *myFunc(int i) {
    return "it's me";
}

template<typename FN, FN fn, typename RES, typename... PARMS> RES _DISPATCH(PARMS... parms) {
    return fn(parms...);
}

#define DISPATCH(RES, FN, ...) _DISPATCH<RES(*)(__VA_ARGS__), FN, RES, __VA_ARGS__>

Cout() << (void *)DISPATCH(int, myFunc, const char *) << "\n";
Cout() << (void *)DISPATCH(const char *, myFunc, int) << "\n";

The DISPATCH() macro builds a dispatcher of which I can get the C address and pass to my scripting engine. I'd rather to have a solution without macros, indeed. Still open to better solutions!

Upvotes: 0

Yakk - Adam Nevraumont
Yakk - Adam Nevraumont

Reputation: 275330

template<class T, T t>
struct constant_t:std::integral_constant<T,t>{
  constexpr operator T()const { return t; }
  constexpr constant_t(){}
};

#define TYPEDARG(...) \
  typename std::decay<decltype(__VA_ARGS__)>::type, \
  __VA_ARGS__

Now constant_t<TYPEDARG(foo)> is a type whose instances can be invoked if foo is a non-overloaded function and they call foo.

In this becomes:

template<auto x>
using constant_t=std::integral_constant<std::decay_t<decltype(x)>,x>;

and use is simply constant_t<foo>, without all that extra noise.

template<class Fn, class R, class... Args>
R test(Args&&... args) {
  return Fn{}(std::forward<Args>(args)...);
}

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

int r = test< constant_t<TYPEDARG(foo)>, int, int, int >( 3, 4 );

Upvotes: 3

Related Questions