在功能中对特定行进行模板化

时间:2018-12-19 14:21:33

标签: c++ templates

我正在为嵌入式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 :(得分:1)

声明应为:

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

答案 1 :(得分:1)

如果编译器支持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;
}

注意:要在Visual Studio中启用C ++ 17,请右键单击您的项目,然后单击“属性”。然后转到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;
}

答案 2 :(得分:0)

在这种情况下,我认为您应该有一个通用的声明和专门的实现。您不能只基于那一行的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作为arg),然后可以模板化GetGlobal来调用适当的模板变量

答案 3 :(得分:0)

因此,事实证明我试图使它变得比我需要的复杂一些: 我使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::stringint,这似乎暗示它也适用于其他类型。