解决函数的部分模板特化时的 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;
        }   
};

问题

  1. 根据客户端尝试注册的函数,_Result 可能为空,这会在传递 LuaExtract (稍后显示)作为 MethodWrapper.

  2. 的第 2 到第 2+N 个参数
  3. 我不能重载 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

  1. 类型没有被正确解析。例如 Registrar,因此 Registrar::Func "don't name a type".
  2. 在对函数调用使用参数包扩展的调用站点上,我得到 "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 "...",这是有道理的。

这些更改修复了其他类型的错误。