I apologize ahead of time for the verbosity of the code.
I have been writing a small Lua bindings generator in C++ using TMP and I have encountered a need for partial template specialization of a function that I don't think can be resolved with a function overload. For clarity, I have omitted the actual Lua code.
EDIT: I understand that functions cannot be partially specialized.
Here is the code I want to write:
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;
}
};
The Problems
Depending on the function that the client is trying to register, _Result could be void, which would result in an "invalid use of void expression" error when passing the result of LuaExtract (shown later) as the 2nd to 2+Nth arguments to MethodWrapper.
I cannot overload LuaWrapper because Lua expects a function pointer of type:
int(*)(lua_State*)
.
My Attempted Solution:
I decided to make a new struct that I can partially specialize.
////////////////// 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
- Types aren't being resolved correctly. e.g Registrar and consequently Registrar::Func "don't name a type".
- On the call sites that use parameter pack expansion on a function call, I am getting "parameter packs not expanded with '...' " even though they look identical to the ones that worked in my original code.
Compilers: Clang 3.7.0, GCC 4.8.1
LuaExtract (the expansion context):
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;
}
It seems to me that either the compilers are just getting bogged down with types, or, more likely, I am missing something. Thanks for your time.
EDIT: It looks like compiler can't resolve the types because LuaMethodRegistar<_Result, _Class, _Args...> and LuaCallWrapper<_Result, _Class, _Args...> depend on each other. How do I break that dependency?
It turns out, it only seemed like the types depended on each other because omitting the ::template in the call sites produces seemingly bizarre error messages.