解决函数的部分模板特化时的 Typedef 和参数包扩展问题
Typedef and parameter pack expansion issues when working around partial template specialization of a function
对于代码的冗长,我提前表示歉意。
我一直在使用 TMP 在 C++ 中编写一个小型 Lua 绑定生成器,我遇到了对函数的部分模板特化的需求,我认为不能通过函数重载来解决。为清楚起见,我省略了实际的 Lua 代码。
编辑:我知道函数不能部分特化。
下面是我要写的代码:
template<typename _Result, class _Class, typename ..._Args>
class LuaMethodRegistrar
{
public:
typedef _Result (_Class::*Func)(_Args...);
using _This = LuaMethodRegistrar<_Result, _Class, _Args...>;
enum { N_ARGS = std::tuple_size<std::tuple<_Args...>>::value };
public:
template <Func _Func>
static typename NamedRegistrar::RegisterFunc GetWrapper()
{
return Register<_Func>;
}
private:
template <Func _Func>
static bool Register(lua_State* L, char const* className, char const* methodName)
{
// register _This::LuaCallWrapper<_Result, _Func>
return true;
}
template<Func _Func>
static _Result MethodWrapper(void* object, _Args... args)
{
return (static_cast<_Class*>(object)->*_Func)(args...);
}
/////////////////// Key functions are down here ///////////////////
template <typename _Result, Func _Func>
static int LuaCallWrapper(lua_State* L)
{
// grab obj
int nArgs = N_ARGS;
return LuaReturn(L, MethodWrapper<_Func>(obj, LuaExtract<_Args>(L, nArgs)...)); // this line works fine here
}
template <Func _Func>
static int LuaCallWrapper<void, _Func>(lua_State* L)
{
// grab obj
MethodWrapper<_Func>(obj, LuaExtract<_Args>(L, nArgs)...));
return 0;
}
};
问题
根据客户端尝试注册的函数,_Result 可能为空,这会在传递 LuaExtract (稍后显示)作为 MethodWrapper.
的第 2 到第 2+N 个参数
我不能重载 LuaWrapper 因为 Lua 需要一个函数指针类型:int(*)(lua_State*)
.
我尝试的解决方案:
我决定制作一个我可以部分特化的新结构。
////////////////// Forward declared above but defined below LuaMethodRegistrar ///////////////////
template <typename _Result, typename _Class, typename ..._Args>
struct LuaCallWrapper
{
using Registrar = LuaMethodRegistrar<_Result, _Class, _Args...>;
enum { N_ARGS = std::tuple_size<std::tuple<_Args...>>::value };
template<_Result(_Class::*_Func)(_Args...)>
static int Call(lua_State* L)
{
// I can't use Registrar here because it gives me the "does not name a type error"
// Yet, when I use the full type, it finds it.
int nArgs = N_ARGS;
return LuaReturn(L, LuaMethodRegistrar<_Result, _Class, _Args...>::MethodWrapper<_Func>(obj, LuaExtract<_Args>(L, nArgs)...));
}
};
template <typename _Class, typename ..._Args>
struct LuaCallWrapper<void, _Class, _Args...>
{
using Registrar = LuaMethodRegistrar<void, _Class, _Args...>;
enum { N_ARGS = std::tuple_size<std::tuple<_Args...>>::value };
template<void(_Class::*_Func)(_Args...)>
static int Call(lua_State* L)
{
int nArgs = N_ARGS;
LuaMethodRegistrar<void, _Class, _Args...>::MethodWrapper<_Func>(obj, LuaExtract<_Args>(L, nArgs)...);
return 0;
}
};
////////////////////// Back in LuaMethodRegistrar::Register //////////////
// register LuaCallWrapper<_Result, _Class, _Args...>::Call<_Func>
Oddities/Problems
- 类型没有被正确解析。例如 Registrar,因此 Registrar::Func "don't name a type".
- 在对函数调用使用参数包扩展的调用站点上,我得到 "parameter packs not expanded with '...' ",即使它们看起来与我的原始代码中的相同。
编译器:Clang 3.7.0、GCC 4.8.1
Lua提取(扩展上下文):
template <typename T>
inline T LuaExtract(lua_State* L, int& argCount);
template <>
inline int LuaExtract<int>(lua_State* L, int& argCount)
{
// get result
return result;
}
在我看来,要么是编译器陷入了类型问题,要么更有可能是我遗漏了一些东西。谢谢你的时间。
编辑:看起来编译器无法解析类型,因为 LuaMethodRegistar<_Result, _Class, _Args...> 和 Lua CallWrapper<_Result, _Class, _Args...> 相互依赖。我该如何打破这种依赖?
事实证明,这些类型似乎只是相互依赖,因为在调用站点中省略 ::template 会产生看似奇怪的错误消息。
事实证明这是一个错误的编译器错误消息的简单案例。
当我将 LuaCallWrapper
移出 LuaMethodRegistrar
时,我忘记将 ::template
添加到此处的成员模板函数调用中:
return LuaReturn(L, LuaMethodRegistrar<_Result, _Class, _Args...>::MethodWrapper<_Func>(obj, LuaExtract<_Args>(L, nArgs)...));
此处:
LuaMethodRegistrar<void, _Class, _Args...>::MethodWrapper<_Func>(obj, LuaExtract<_Args>(L, nArgs)...);
return 0;
一旦进入 LuaMethodRegistrar
本身。
不使用 ::template
的模板成员函数调用被视为“<unresolved overloaded function type>
”
这导致编译器试图评估这样的事情:LuaExtract<_Args>(L, nArgs)...
自己,让它抱怨 template parameter packs not expanded with "..."
,这是有道理的。
这些更改修复了其他类型的错误。
对于代码的冗长,我提前表示歉意。
我一直在使用 TMP 在 C++ 中编写一个小型 Lua 绑定生成器,我遇到了对函数的部分模板特化的需求,我认为不能通过函数重载来解决。为清楚起见,我省略了实际的 Lua 代码。
编辑:我知道函数不能部分特化。
下面是我要写的代码:
template<typename _Result, class _Class, typename ..._Args>
class LuaMethodRegistrar
{
public:
typedef _Result (_Class::*Func)(_Args...);
using _This = LuaMethodRegistrar<_Result, _Class, _Args...>;
enum { N_ARGS = std::tuple_size<std::tuple<_Args...>>::value };
public:
template <Func _Func>
static typename NamedRegistrar::RegisterFunc GetWrapper()
{
return Register<_Func>;
}
private:
template <Func _Func>
static bool Register(lua_State* L, char const* className, char const* methodName)
{
// register _This::LuaCallWrapper<_Result, _Func>
return true;
}
template<Func _Func>
static _Result MethodWrapper(void* object, _Args... args)
{
return (static_cast<_Class*>(object)->*_Func)(args...);
}
/////////////////// Key functions are down here ///////////////////
template <typename _Result, Func _Func>
static int LuaCallWrapper(lua_State* L)
{
// grab obj
int nArgs = N_ARGS;
return LuaReturn(L, MethodWrapper<_Func>(obj, LuaExtract<_Args>(L, nArgs)...)); // this line works fine here
}
template <Func _Func>
static int LuaCallWrapper<void, _Func>(lua_State* L)
{
// grab obj
MethodWrapper<_Func>(obj, LuaExtract<_Args>(L, nArgs)...));
return 0;
}
};
问题
根据客户端尝试注册的函数,_Result 可能为空,这会在传递 LuaExtract (稍后显示)作为 MethodWrapper.
的第 2 到第 2+N 个参数
我不能重载 LuaWrapper 因为 Lua 需要一个函数指针类型:
int(*)(lua_State*)
.
我尝试的解决方案:
我决定制作一个我可以部分特化的新结构。
////////////////// Forward declared above but defined below LuaMethodRegistrar ///////////////////
template <typename _Result, typename _Class, typename ..._Args>
struct LuaCallWrapper
{
using Registrar = LuaMethodRegistrar<_Result, _Class, _Args...>;
enum { N_ARGS = std::tuple_size<std::tuple<_Args...>>::value };
template<_Result(_Class::*_Func)(_Args...)>
static int Call(lua_State* L)
{
// I can't use Registrar here because it gives me the "does not name a type error"
// Yet, when I use the full type, it finds it.
int nArgs = N_ARGS;
return LuaReturn(L, LuaMethodRegistrar<_Result, _Class, _Args...>::MethodWrapper<_Func>(obj, LuaExtract<_Args>(L, nArgs)...));
}
};
template <typename _Class, typename ..._Args>
struct LuaCallWrapper<void, _Class, _Args...>
{
using Registrar = LuaMethodRegistrar<void, _Class, _Args...>;
enum { N_ARGS = std::tuple_size<std::tuple<_Args...>>::value };
template<void(_Class::*_Func)(_Args...)>
static int Call(lua_State* L)
{
int nArgs = N_ARGS;
LuaMethodRegistrar<void, _Class, _Args...>::MethodWrapper<_Func>(obj, LuaExtract<_Args>(L, nArgs)...);
return 0;
}
};
////////////////////// Back in LuaMethodRegistrar::Register //////////////
// register LuaCallWrapper<_Result, _Class, _Args...>::Call<_Func>
Oddities/Problems
- 类型没有被正确解析。例如 Registrar,因此 Registrar::Func "don't name a type".
- 在对函数调用使用参数包扩展的调用站点上,我得到 "parameter packs not expanded with '...' ",即使它们看起来与我的原始代码中的相同。
编译器:Clang 3.7.0、GCC 4.8.1
Lua提取(扩展上下文):
template <typename T>
inline T LuaExtract(lua_State* L, int& argCount);
template <>
inline int LuaExtract<int>(lua_State* L, int& argCount)
{
// get result
return result;
}
在我看来,要么是编译器陷入了类型问题,要么更有可能是我遗漏了一些东西。谢谢你的时间。
编辑:看起来编译器无法解析类型,因为 LuaMethodRegistar<_Result, _Class, _Args...> 和 Lua CallWrapper<_Result, _Class, _Args...> 相互依赖。我该如何打破这种依赖?
事实证明,这些类型似乎只是相互依赖,因为在调用站点中省略 ::template 会产生看似奇怪的错误消息。
事实证明这是一个错误的编译器错误消息的简单案例。
当我将 LuaCallWrapper
移出 LuaMethodRegistrar
时,我忘记将 ::template
添加到此处的成员模板函数调用中:
return LuaReturn(L, LuaMethodRegistrar<_Result, _Class, _Args...>::MethodWrapper<_Func>(obj, LuaExtract<_Args>(L, nArgs)...));
此处:
LuaMethodRegistrar<void, _Class, _Args...>::MethodWrapper<_Func>(obj, LuaExtract<_Args>(L, nArgs)...);
return 0;
一旦进入 LuaMethodRegistrar
本身。
不使用 ::template
的模板成员函数调用被视为“<unresolved overloaded function type>
”
这导致编译器试图评估这样的事情:LuaExtract<_Args>(L, nArgs)...
自己,让它抱怨 template parameter packs not expanded with "..."
,这是有道理的。
这些更改修复了其他类型的错误。