Templates para a linha específica na função

votos
1

Eu estou escrevendo um wrapper para Lua embutido, e eu tenho uma série de funções para recuperar valores globais de um lua_State. Desde que a função faz quase a mesma coisa exata para cada (recebe o nome global com lua_getglobal(L, name), chama o apropriado lua_to___()função, e em seguida, aparece a pilha para retornar o lua_State ao seu estado original), eu percebi que deve haver alguma maneira de fazer isso com modelos .

No entanto, eu não consigo encontrar uma maneira de ter que uma linha específica onde os assuntos do tipo ser dependente do tipo sem escrever funções completamente distintas para cada tipo. Enquanto agora esta função só seria três linhas longas, existem outras funções similares que possam ser mais complicado, mas com o mesmo problema.

A partir de agora, as funções esta aparência (isso está dentro de uma classe chamada LuaManager, que tem um membro 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;
}

Existe uma maneira de ser capaz de, por linhas individuais de código, especializar-se um modelo? Ou eu estou mal-entendido que eu deveria estar usando modelos para?

Publicado 19/12/2018 em 14:21
fonte usuário
Em outras línguas...                            


4 respostas

votos
1

Se o seu compilador suporta C ++ 17, você pode usar 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 em Visual Studio, clique direito em seu projeto e clique em Propriedades. Então vá para C / C ++ -> Idioma -> C ++ Idioma Padrão e selecione /std:c++17ou /std:c++latest.


Atualizar

Se você não pode ou não quer usar C ++ 17, aqui é outra abordagem que não usa nenhum "novos" recursos, mesmo sem modelos:

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

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

Adicione um destes sobrecargas para cada tipo. Depois, é só chamar get_lua_value()e resolução de sobrecarga irá fazer o trabalho para você:

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;
}
Respondeu 19/12/2018 em 14:48
fonte usuário

votos
1

A declaração deve ser apenas:

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

E para a implementação, gostaria de criar um modelo de estrutura, e usar as especializações como um mapa de tipo para 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;
}
Respondeu 19/12/2018 em 14:42
fonte usuário

votos
0

Então, acontece que eu estava tentando fazer isso um pouco mais complicado do que eu precisava: Eu fiz a função GetGlobal se importam em tudo sobre que tipo ele estava tentando obter:

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

E então eu definido um modelo para lua_to<T>(int stack_index)e fez cada especialização para ele usar uma função 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));
}

Até agora ele funciona para ambos std::stringe int, o que parece implicar que irá trabalhar para outros tipos também.

Respondeu 20/12/2018 em 12:25
fonte usuário

votos
0

Eu acho que você deve ter uma declaração genérica e implementações especializadas nesse caso. Você não pode simplesmente "mudar a função" com base em T em que uma linha. Veja este exemplo:

#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;
}

Você terá uma função GetGlobal quase idêntica diferindo em apenas isso uma linha. Se você quer cortar a repetição para baixo, você poderia ter um modelo para uma conversão para um C ++ digite sozinho (tendo um luaState como arg), e então talvez templated GetGlobal que chama variante modelo adequado

Respondeu 19/12/2018 em 14:48
fonte usuário

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