我正在为嵌入式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;
}
有没有一种方法可以针对单个代码行专门化模板?还是我误解了我应该为模板使用什么?
答案 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::string
和int
,这似乎暗示它也适用于其他类型。