Создание шаблона для конкретной строки в функции

Я пишу оболочку для встроенного 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;
}

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

чего ты хочешь добиться? Звонящий должен выбрать тип? Или вызывающий должен просто передать name глобального и вернуть правильный тип?

463035818_is_not_a_number 19.12.2018 15:31

@ user463035818 Вызывающий будет использовать GetGlobal <int> (name) для получения int, GetGlobal <float> для получения числа с плавающей запятой и т. д.

PixelArtDragon 19.12.2018 15:33

хорошо, тогда вы почти у цели. дайте мне секунду, чтобы написать ответ;)

463035818_is_not_a_number 19.12.2018 15:40

Вопрос не помечен как C++ 17, но, может быть, у вас все еще есть доступ к функциям C++ 17?

sebrockm 19.12.2018 15:41

@sebrockm Я не уверен. У меня определенно есть функции C++ 11, но я не уверен, что есть в VS2017.

PixelArtDragon 19.12.2018 15:42

извините, неправильно прочитал вопрос, придется исправить мой уже написанный ответ, но сейчас нет времени

463035818_is_not_a_number 19.12.2018 15:45
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
1
6
68
4
Перейти к ответу Данный вопрос помечен как решенный

Ответы 4

Объявление должно быть таким:

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

Это может быть менее утомительно с шаблонами переменных, но это правильный подход :)

Quentin 19.12.2018 15:45

Если ваш компилятор поддерживает 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++ -> Язык -> Стандарт языка C++ и выберите /std:c++17 или /std:c++latest.


Обновлять

Если вы не можете или не хотите использовать 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;
}

Я думаю, в этом случае у вас должна быть общая декларация и специализированные реализации. Вы не можете просто «переключить функцию» на основе 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, который вызывает соответствующий вариант шаблона

Ответ принят как подходящий

Оказывается, я пытался сделать это немного сложнее, чем мне нужно: Я сделал функцию 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, что, похоже, означает, что он будет работать и для других типов.

Другие вопросы по теме