Шаблонирование для конкретной строки в функции - PullRequest
0 голосов
/ 19 декабря 2018

Я пишу оболочку для встроенного Lua, и у меня есть ряд функций для извлечения глобальных значений из lua_State.Поскольку функция выполняет почти одинаковые действия для каждого (получает глобальное имя с lua_getglobal(L, name), вызывает соответствующую функцию lua_to___(), а затем извлекает стек, чтобы вернуть lua_State в исходное состояние), я решил, что должно быть несколькоспособ сделать это с помощью шаблонов.

Тем не менее, я не могу найти способ иметь эту конкретную строку, где тип имеет значение, зависит от типа без написания совершенно отдельных функций для каждого типа.Хотя сейчас эта функция будет иметь длину всего три строки, есть другие похожие функции, которые могут быть более сложными, но с той же проблемой.

На данный момент функции выглядят так (это внутри класса, называемогоLuaManager, в котором есть член 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;
}

Есть ли способ, позволяющий для отдельных строк кода специализировать шаблон?Или я неправильно понимаю, для чего мне следует использовать шаблоны?

Ответы [ 4 ]

0 голосов
/ 20 декабря 2018

Оказывается, я пытался сделать это немного сложнее, чем нужно: я заставил функцию GetGlobal совершенно не заботиться о том, какой тип он пытается получить:

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

И затемЯ определил шаблон для lua_to<T>(int stack_index) и заставил каждую специализацию для него использовать различную функцию:

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

Пока что он работает как для std::string, так и int, что, по-видимому, подразумевает, что он будет работать дляа также другие типы.

0 голосов
/ 19 декабря 2018

Если ваш компилятор поддерживает C ++ 17, вы можете использовать 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;
}

Примечание. Чтобы включить C ++ 17 в Visual Studio, щелкните правой кнопкой мыши свой проект и выберите Свойства.Затем перейдите к C / C ++ -> Language -> C ++ Language Standard и выберите /std:c++17 или /std:c++latest.


Update

Если вы не можете или не хотите использоватьC ++ 17, вот еще один подход, который не использует никаких «новых» функций, даже без шаблонов:

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

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

Добавьте одну из этих перегрузок для каждого типа.Тогда вы можете просто позвонить по номеру get_lua_value(), и разрешение перегрузки выполнит эту работу за вас:

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;
}
0 голосов
/ 19 декабря 2018

Я думаю, у вас должно быть общее объявление и специализированные реализации в этом случае.Вы не можете просто «переключить функцию» на основе T в этой строке.Рассмотрим этот пример:

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

У вас будет почти идентичная функция GetGlobal, отличающаяся только этой строкой.Если вы хотите сократить количество повторений, у вас может быть шаблон для преобразования только в тип C ++ (в качестве аргумента используется luaState), а затем может быть создан шаблон GetGlobal, который вызывает соответствующий вариант шаблона

0 голосов
/ 19 декабря 2018

Объявление должно быть просто:

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

А для реализации я бы создал шаблон структуры и использовал бы специализации как карту от типа к функции.

#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;
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...