Templating für bestimmte Linie in Funktion

stimmen
1

Ich bin ein Wrapper für eingebettete Lua zu schreiben, und ich habe eine Reihe von Funktionen globalen Werte von einem lua_State abzurufen. Da die Funktion für jeden fast genau das gleiche tut (bekommt den globalen Namen mit lua_getglobal(L, name), ruft die entsprechende lua_to___()Funktion, und dann wird vom Stack der lua_State in seinen ursprünglichen Zustand zurückkehren), dachte ich , es sollte eine gewisse Art und Weise sein , dies mit Vorlagen zu tun .

Allerdings kann ich nicht, dass eine bestimmte Linie zu haben scheinen, einen Weg zu finden, wo die Art Fragen von der Art abhängig sein, ohne dass für jeden Typ vollständig getrennte Funktionen zu schreiben. Während jetzt nur diese Funktion drei Zeilen lang sein würde, gibt es andere ähnliche Funktionen, die komplizierter sein könnten, aber mit dem gleichen Problem.

Ab sofort suchen die Funktionen dieses wie (dies innerhalb einer Klasse ist LuaManager genannt, die ein lua_State * Mitglied hat):

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

Gibt es eine Möglichkeit, um in der Lage zu sein, für einzelne Codezeilen, spezialisiert eine Vorlage? Oder bin ich Missverständnis, was soll ich Vorlagen verwenden für?

Veröffentlicht am 19/12/2018 um 14:21
quelle vom benutzer
In anderen Sprachen...                            


4 antworten

stimmen
1

Wenn Ihr Compiler ++ 17 C unterstützt, können Sie verwenden 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;
}

Hinweis: So aktivieren C ++ 17 in Visual Studio mit der rechten Maustaste auf Ihren Projekt und klicken Sie auf Eigenschaften. Dann gehen Sie zu C / C ++ -> Sprache -> C ++ Language - Standard und wählen Sie /std:c++17oder /std:c++latest.


Aktualisieren

Wenn Sie nicht 17 C ++ können oder wollen nicht verwenden, hier ist ein weiterer Ansatz, der keine „neue“ Features nicht verwenden, auch ohne Vorlagen:

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

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

In einer dieser Überlastungen für jeden Typ. Dann können Sie einfach anrufen get_lua_value()und Überladungsauflösung wird die Arbeit für Sie tun:

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;
}
Beantwortet am 19/12/2018 um 14:48
quelle vom benutzer

stimmen
1

Die Erklärung sollte nur sein:

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

Und für die Umsetzung, würde ich eine Struktur-Vorlage erstellen und die Spezialisierungen als eine Karte vom Typ verwenden zu funktionieren.

#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;
}
Beantwortet am 19/12/2018 um 14:42
quelle vom benutzer

stimmen
0

So stellt sich heraus, dass ich eine dieser etwas komplizierter zu machen versuchte, als ich brauchte, um: ich die GetGlobal Funktion haupt nicht über gemacht egal welche Art es zu bekommen versuchte:

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

Und dann definiert ich eine Vorlage für lua_to<T>(int stack_index)und machte jede Spezialisierung für sie eine andere Funktion verwenden:

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

Bisher funktioniert es für beide std::stringund int, die es auch für andere Arten funktionieren scheint zu implizieren.

Beantwortet am 20/12/2018 um 12:25
quelle vom benutzer

stimmen
0

Ich denke, Sie sollten eine allgemeine Erklärung und spezialisierte Implementierungen in diesem Fall haben. Sie können nicht nur „schalten Sie die Funktion“ auf Basis von T auf, dass eine Linie. Betrachten Sie folgendes Beispiel:

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

Sie erhalten eine fast identische GetGlobal Funktion in nur diese eine Linie abweichende haben. Wenn Sie die Wiederholung fällen wollen, können Sie eine Vorlage für eine Umwandlung in einem C ++ haben allein geben (eine luaState als arg nehmen), und dann vielleicht als Templat GetGlobal, die rufen entsprechende Vorlage Variante

Beantwortet am 19/12/2018 um 14:48
quelle vom benutzer

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