Templating para la línea específica en función

votos
1

Estoy escribiendo un contenedor para encajada de Lua, y tengo una serie de funciones para recuperar los valores globales de una lua_State. Dado que la función hace casi exactamente lo mismo para cada uno (se pone el nombre global con lua_getglobal(L, name), llama a la apropiada lua_to___()función, y luego aparece la pila para devolver el lua_State a su estado original), pensé que debería haber alguna manera de hacer esto con plantillas .

Sin embargo, me parece que no puede encontrar una manera de tener que una línea específica en la que los asuntos sean de tipo dependiente del tipo sin necesidad de escribir funciones completamente independientes para cada tipo. Mientras que en este momento esta función sólo sería de tres líneas de largo, hay otras funciones similares que podrían ser más complicado, pero con el mismo tema.

A partir de ahora, las funciones se ven así (esto está dentro de una clase llamada LuaManager, que tiene un miembro de lua_State *):

//Declaration
template<std::string>
std::string GetGlobal(const std::string & name);
template<int>
int GetGlobal(const std::string & name);
template<float>
float GetGlobal(const std::string & name);
template<bool>
bool GetGlobal(const std::string & name);

//Implementation
template<typename T>
T LuaManager::GetGlobal(const std::string & name)
{
    lua_getglobal(luaState, name.c_str()); //push the global to the top of the stack
    T value = lua_to____(luaState, -1); //store the value for return
    lua_pop(luaState, 1); //return the stack to empty
    return value;
}

¿Hay alguna manera de poder, para las líneas individuales de código, se especializan una plantilla? O estoy mal entendido lo que debería usar plantillas para?

Publicado el 19/12/2018 a las 14:21
fuente por usuario
En otros idiomas...                            


4 respuestas

votos
1

Si su compilador soporta C ++ 17, puede utilizar if constexpr:

template<typename T>
T LuaManager::GetGlobal(const std::string & name)
{
    lua_getglobal(luaState, name);
    T value;
    if constexpr (std::is_same_v<T, std::string>)
        value = lua_to_string(luaState, -1); // I don't know the actual name of this function
    else if (std::is_same_v<T, int>)
        value = lua_to_int(luaState, -1);
    else if (std::is_same_v<T, whatever>)
        value = lua_to_whatever(luaState, -1);
        // some other arbitrary type dependent code
    else ... // other types 
    lua_pop(luaState, 1);
    return value;
}

Nota: Para habilitar C ++ 17 en Visual Studio, haga clic derecho sobre el proyecto y haga clic en Propiedades. A continuación, vaya a C / C ++ -> Idioma -> lenguaje C ++ estándar y seleccione /std:c++17o /std:c++latest.


Actualizar

Si usted no puede o no desea utilizar C ++ 17, aquí es otro método que no utiliza ningún "nuevas" características, incluso sin plantillas:

void get_lua_value(string& value)
{
    value = lua_to_string(luaState, -1);
}

void get_lua_value(int& value)
{
    value = lua_to_int(luaState, -1);
}

Añadir una de estas sobrecargas para cada tipo. A continuación, sólo puede llamar get_lua_value()y resolución de sobrecarga va a hacer el trabajo para usted:

template<typename T>
T LuaManager::GetGlobal(const std::string& name)
{
    lua_getglobal(luaState, name);
    T value;
    get_lua_value(value); 
    lua_pop(luaState, 1);
    return value;
}
Respondida el 19/12/2018 a las 14:48
fuente por usuario

votos
1

La declaración debe ser sólo:

template<class T>
T GetGlobal(const std::string& name);

Y para la aplicación, me gustaría crear una plantilla de estructura, y el uso de las especializaciones como un mapa de un tipo a funcionar.

#include <type_traits>

template<class>
struct lua_to;

template<>
struct lua_to<int> {
    typedef int(*type)(decltype(luaState), int);
    static constexpr const type value = lua_to_int;
};


template<>
struct lua_to<std::string> {
    typedef std::string(*type)(decltype(luaState), int);
    static constexpr const type value = lua_to_string;
};

// In this case, since this is so tedious, I would use a macro
#define MY_MODULE_DEFINE_LUA_TO(ctype, luatype) \
template<> \
struct lua_to<ctype> { \
    typedef ctype(*type)(decltype(luaState), int); \
    static constexpr const type value = lua_to_  ## luatype; \
};


MY_MODULE_DEFINE_LUA_TO(std::map, table);
MY_MODULE_DEFINE_LUA_TO(double, float);

#undef MY_MODULE_DEFINE_LUA_TO


template<class T>
T GetGlobal(const std::string& name) {
    lua_getglobal(luaState, name); //push the global to the top of the stack
    T value = lua_to<T>::value(luaState, -1); //store the value for return
    lua_pop(luaState, 1); //return the stack to empty
    return value;
}
Respondida el 19/12/2018 a las 14:42
fuente por usuario

votos
0

Así resulta que yo estaba tratando de hacer esto un poco más complicado de lo que necesitaba: Hice la función GetGlobal le importa en absoluto acerca de qué tipo estaba tratando de conseguir:

template<typename T>
T GetGlobal(const std::string & name)
{
    lua_getglobal(luaState, name.c_str());
    T value = lua_to<T>(-1);
    lua_pop(luaState, 1);
    return value;
}

Y luego he definido una plantilla para lua_to<T>(int stack_index)e hice cada especialidad para que utilice una función diferente:

template<typename T>
T lua_to(int stack_index);
template<>
int lua_to(int stack_index) {
    return lua_tointeger(luaState, stack_index);
}
template<>
std::string lua_to(int stack_index) {
    return std::string(lua_tostring(luaState, stack_index));
}

Hasta ahora funciona para ambos std::stringy int, lo que parece dar a entender que va a trabajar para otros tipos también.

Respondida el 20/12/2018 a las 12:25
fuente por usuario

votos
0

Creo que usted debe tener una declaración genérica y implementaciones especializadas en ese caso. No se puede simplemente "cambiar la función" basado en T en esa sola línea. Considere este ejemplo:

#include <iostream>

// Generic declaration
template <typename T>
T doStuff(int arg);

// Specific definitions
template<>
int doStuff(int arg){
    return arg + 1;
}
template<>
float doStuff(int arg){
    return arg - 1;
}

int main(){
    // Our templated function varies in return type,
    //  so you always have to explicitly specify which variant to use

    // This WOULD NOT let compile infer what you want:
    /* float test = doStuff(10) */ // ambiguous call

    // This is OK
    std::cout << doStuff<int>(10) << " " << doStuff<float>(10) << "\n";
    return 0;
}

Tendrá una función GetGlobal casi idéntica que difieren en sólo esta línea. Si usted quiere cortar la repetición abajo, usted podría tener una plantilla para una conversión a un solo tipo C ++ (teniendo un luaState como arg), y luego tal vez con plantilla GetGlobal que llama variante de plantilla apropiada

Respondida el 19/12/2018 a las 14:48
fuente por usuario

Cookies help us deliver our services. By using our services, you agree to our use of cookies. Learn more