0

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

  1. 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.

  2. 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

  1. Types aren't being resolved correctly. e.g Registrar and consequently Registrar::Func "don't name a type".
  2. 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.

rationalcoder
  • 1,587
  • 1
  • 15
  • 29
  • 1
    There is no such thing as partial template specialization of a function. There is only overloading. You can use a class instead though. – VoidStar Sep 28 '15 at 06:55
  • @VoidStar I am well aware, but it gets the point across of what I am trying to do. Before I realized that I needed more than one implementation of LuaCallWrapper, the parameter pack expansion worked fine; now it doesn't. – rationalcoder Sep 28 '15 at 06:57
  • How does `LuaExtract<_Args>(L, nArgs)...` track which argument you are on? What if its 3 `ints`? Won't it just extract the first argument 3 times? Is there some hidden state I can't see? If not you probably need to add some means to iterate. Or does each type passed into `_Args` include a prebaked index? – VoidStar Sep 28 '15 at 07:13
  • @VoidStar Good Question. I don't know how much you know about Lua, but it communicates through a stack that can be accessed with negative indices. I am grabbing the number of arguments in N_ARGS and passing an integer of that number into LuaExtract. LuaExtract extracts data of the specified type at -nArrgs, then subtracts one from it. This makes each subsequent call access the next highest element in the stack. – rationalcoder Sep 28 '15 at 13:57
  • Please note that names that start with an underscore followed by a capital letter are reserved for the C++ implementation. – dyp Sep 28 '15 at 15:20
  • 1
    `LuaMethodRegistrar<_Result, _Class, _Args...>::MethodWrapper<_Func>` requires a `template` keyword. See http://stackoverflow.com/questions/610245/where-and-why-do-i-have-to-put-the-template-and-typename-keywords – dyp Sep 28 '15 at 15:23
  • @dyp Thanks, I forgot about that. Now that the call is correct, I just need to figure out how to forward declare correctly to get the two classes to not to be defined in terms of each other. – rationalcoder Sep 28 '15 at 17:16
  • @dyp Woops, turns out a didn't add ::template to the call in LuaMethodRegistrar and I needed to add a friend declaration for LuaCallWrapper in LuaMethodRegistrar. If you post an answer with the ::template, a friend declaration, and an explanation as to why the types can be resolved even though they are defined in terms of each other, I will accept it as the answer. – rationalcoder Sep 28 '15 at 17:54
  • No, I don't think I've seen the entire picture. But feel free to answer your own question. – dyp Sep 28 '15 at 22:23

1 Answers1

0

This turned out to be a simple case of a bad compiler error message.

When I moved LuaCallWrapper out of the LuaMethodRegistrar, I forgot to add ::template to the member template function calls here:

return LuaReturn(L, LuaMethodRegistrar<_Result, _Class, _Args...>::MethodWrapper<_Func>(obj, LuaExtract<_Args>(L, nArgs)...));

here:

LuaMethodRegistrar<void, _Class, _Args...>::MethodWrapper<_Func>(obj, LuaExtract<_Args>(L, nArgs)...);
return 0;

and once inside the LuaMethodRegistrar itself.

Template member function calls that don't use ::template are treated as an "<unresolved overloaded function type>"

This lead to the compiler trying to evaluate things like this: LuaExtract<_Args>(L, nArgs)... on their own, making it complain about template parameter packs not expanded with "...", which makes sense.

The other type errors were fixed by these changes.

rationalcoder
  • 1,587
  • 1
  • 15
  • 29