Texturant pour la ligne spécifique en fonction

voix
1

J'écris une enveloppe pour Lua intégré, et j'ai une série de fonctions pour récupérer les valeurs globales d'un lua_State. Étant donné que la fonction ne fait presque exactement la même chose pour chaque (obtient le nom global avec lua_getglobal(L, name), appelle appropriée la lua_to___()fonction, et apparaît alors la pile de retourner le lua_State à son état d' origine), je me suis dit qu'il devrait y avoir un moyen de le faire avec des modèles .

Cependant, je ne peux pas sembler trouver un moyen d'avoir qu'une ligne spécifique où les questions de type soient fonction du type sans l'écriture des fonctions complètement distinctes pour chaque type. Bien que cette fonction en ce moment ne serait trois lignes, il y a d'autres fonctions similaires qui pourraient être plus compliquées, mais avec la même question.

A partir de maintenant, les fonctions ressemblent à ceci (ce qui est dans une classe appelée LuaManager, qui a un membre de lua_State de *):

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

Est-il possible de pouvoir, pour les lignes individuelles de code, se spécialisent un modèle? Ou suis-je mal compris ce que je devrais utiliser des modèles pour?

Créé 19/12/2018 à 14:21
source utilisateur
Dans d'autres langues...                            


4 réponses

voix
1

Si votre compilateur prend en charge 17 C ++, vous pouvez utiliser 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;
}

Remarque: Pour activer 17 C ++ dans Visual Studio, faites un clic droit sur votre projet et cliquez sur Propriétés. Ensuite , allez à C / C ++ -> Langue -> C ++ Standard Langue et sélectionnez /std:c++17ou /std:c++latest.


Mettre à jour

Si vous ne pouvez pas ou ne voulez pas utiliser 17 C ++, voici une autre approche qui n'utilise toutes les fonctionnalités « nouveaux », même sans modèles:

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

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

Ajouter un de ces surcharges pour chaque type. Ensuite , vous pouvez simplement appeler get_lua_value()et la résolution de surcharge fera le travail pour vous:

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;
}
Créé 19/12/2018 à 14:48
source utilisateur

voix
1

La déclaration doit être juste:

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

Et pour la mise en œuvre, je voudrais créer un modèle de struct, et utiliser les spécialisations comme une carte de genre à fonctionner.

#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;
}
Créé 19/12/2018 à 14:42
source utilisateur

voix
0

Donc, se trouve que je suis en train de faire un peu plus compliqué que je devais: Je faisais la fonction GetGlobal se soucient pas du tout de quel type il essayait d'obtenir:

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

Et puis je définissais un modèle pour lua_to<T>(int stack_index)et fait chaque spécialisation pour l'utiliser une autre fonction:

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

Jusqu'à présent , il fonctionne à la fois std::stringet int, ce qui semble impliquer qu'il va travailler pour d' autres types aussi bien.

Créé 20/12/2018 à 12:25
source utilisateur

voix
0

Je pense que vous devriez avoir une déclaration générique et implémentations spécialisées dans ce cas. Vous ne pouvez pas simplement « activer la fonction » basée sur T sur cette ligne. Prenons l'exemple suivant:

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

Vous aurez une fonction GetGlobal presque identique différant seulement celui-ligne. Si vous voulez couper la répétition vers le bas, vous pouvez avoir un modèle pour une conversion en C ++ de type seul (en prenant un luaState comme arg), puis peut-être que GetGlobal templated appelle variante de modèle approprié

Créé 19/12/2018 à 14:48
source utilisateur

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