Tengo esta función de plantilla, que está funcionando bien:

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

Que se llama así:

GlobalBind(aFunction, "aName", vm);

Su alcance es la función vinculante fn a un motor de secuencias de comandos.

Al usarlo de esta manera, debo mantener el puntero fn Y un puntero de plantilla de proxy dentro del motor de scripting; la plantilla de proxy (que no se muestra aquí) se llama con fn como parámetro, hace algunas cosas y luego llama a fn.

Lo que me gustaría lograr es eliminar el puntero fn de la llamada a la función y ponerlo en la plantilla como parámetro, algo como esto:

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

Entonces, una plantilla específica es instanciada para cada fn diferente, lo que significa que podría evitar el puntero fn en el lenguaje de programación; la llamada debe ser:

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

El compilador acepta la declaración de la plantilla, pero la llamada trae errores, incluso agregando los tipos de parámetros antes de aFunction, así (asumiendo que aFunction devuelve y int y tiene const char * como parámetro):

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

¿Hay alguna forma de lograr este resultado (el primero, sin una lista de parámetros)?

EDITAR: para simplificar la pregunta, es la plantilla

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

Uno válido? Y si es así ... ¿cómo debería llamarse? Probé esto:

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

EDIT2: Esta:

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

Funciona, pero no es útil para mi propósito

0
Max 14 nov. 2017 a las 03:20

2 respuestas

La mejor respuesta
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__

Ahora constant_t<TYPEDARG(foo)> es un tipo cuyas instancias se pueden invocar si foo es una función no sobrecargada y llaman a foo.

En esto se convierte en:

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

Y el uso es simplemente constant_t<foo>, sin todo ese ruido adicional.

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 );
3
Yakk - Adam Nevraumont 14 nov. 2017 a las 12:53

Encontré una solución (fea), pero también funciona para funciones sobrecargadas:

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";

La macro DISPATCH () crea un despachador del cual puedo obtener la dirección C y pasarla a mi motor de scripting. Preferiría tener una solución sin macros, de hecho. ¡Todavía abierto a mejores soluciones!

0
Max 14 nov. 2017 a las 17:34