Szablonów dla określonej linii w funkcji

głosy
1

Piszę otoki dla osadzonych Lua i mam szereg funkcji do pobierania wartości globalnych z lua_State. Ponieważ funkcja robi prawie dokładnie to samo dla każdego (dostaje globalną nazwę z lua_getglobal(L, name), wywołuje odpowiednią lua_to___()funkcję, a następnie wyskakuje stos zwrócić lua_State do pierwotnego stanu), pomyślałem, nie powinno być jakiś sposób, aby to zrobić za pomocą szablonów ,

Jednak nie wydaje się znaleźć sposób, aby mieć tę jedną konkretną linię, w której sprawy typ zależy od rodzaju bez pisania całkowicie oddzielne funkcje dla każdego typu. Chociaż teraz funkcja ta byłaby tylko długie trzy linie, istnieją inne podobne funkcje, które mogą być bardziej skomplikowane, ale z tym samym problemem.

Jak na razie, funkcje wyglądać następująco (jest to w obrębie klasy zwanej LuaManager, która ma lua_State * Członek):

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

Czy istnieje sposób, aby móc dla poszczególnych linii kodu, specjalizują się szablon? Albo ja niezrozumienia tego, co powinno być za pomocą szablonów?

Utwórz 19/12/2018 o 14:21
źródło użytkownik
W innych językach...                            


4 odpowiedzi

głosy
1

Jeśli kompilator C ++ obsługuje 17, można użyć 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;
}

Uwaga: Aby włączyć C ++ 17 w Visual Studio, kliknij prawym przyciskiem myszy na projekcie, a następnie kliknij przycisk Właściwości. Następnie przejdź do C / C ++ -> Language -> standardu języka C ++ i wybierz /std:c++17lub /std:c++latest.


Aktualizacja

Jeżeli nie mogą lub nie chcą korzystać z C ++ 17, tutaj jest inne podejście, że nie korzysta z żadnych „nowych” funkcji, nawet bez szablonów:

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

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

Dodaj jeden z tych przeciążeń dla każdego typu. Następnie można po prostu zadzwonić get_lua_value()i rozdzielczości przeciążenie będzie wykonać zadanie dla Ciebie:

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;
}
Odpowiedział 19/12/2018 o 14:48
źródło użytkownik

głosy
1

Oświadczenie powinno być po prostu:

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

Oraz wykonania, chciałbym utworzyć szablon struct i użyć jako specjalizacje mapie od rodzaju funkcjonować.

#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;
}
Odpowiedział 19/12/2018 o 14:42
źródło użytkownik

głosy
0

Tak więc okazuje się, starałem się zrobić to nieco bardziej skomplikowane niż potrzebowałem: Zrobiłem funkcja GetGlobal nie dbają w ogóle o tym, co wpisać, że starał się dostać:

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

I wtedy zdefiniowany szablon lua_to<T>(int stack_index)i uczynił to dla każdej specjalizacji korzystania inną funkcję:

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

Do tej pory to działa dla obu std::stringi int, co wydaje się sugerować, będzie pracować dla innych typów, jak również.

Odpowiedział 20/12/2018 o 12:25
źródło użytkownik

głosy
0

Myślę, że trzeba mieć ogólną deklarację i specjalistycznych implementacje w tej sprawie. Nie można po prostu „włączyć funkcję” w oparciu o T na tej jednej linii. Rozważmy następujący przykład:

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

Będziesz miał niemal identyczny GetGlobal funkcję różniącą się tylko tej jednej linii. Jeśli chcesz wyciąć powtórzenia w dół, można mieć szablon dla konwersji do C ++ wpisać samodzielnie (biorąc luaState jako Arg), a następnie może matrycy GetGlobal który wywołuje odpowiedni wariant szablonu

Odpowiedział 19/12/2018 o 14:48
źródło użytkownik

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