0
votes

I'm trying to write a class that extract the parameters passed from a Lua function call (Lua script calling a registered C function) and pass them to the registered method (IDelegate in my code snippets) so I can execute it and returns the value.

Suppose I register the following method from the GameBoard class: long int GameBoard::testFunct(long int); as "GB:testFunction" under Lua with the following code:

luaL_newmetatable(mState, "GameBoard");
lua_pushstring(mState, "__index");
lua_pushvalue(mState, -2);
lua_settable(mState, -3);

lua_pushstring(mState,"testFunction");
hbt::IDelegate<I32,I32>* ideleg = new MethodDelegatePtr<GameBoard,I32,I32>( NULL, &GameBoard::testFunct); // will be deleted later
lua_pushlightuserdata (mState, (IDelegate<I32,I32>*)ideleg);
lua_pushcclosure(mState, LuaCall<I32,GameBoard,I32>::LuaCallback,1);
lua_settable(mState,-3);

(IDelegate & MethodDelegatePtr are used to register methods, functions and functors so I can call them later)

then the LuaCall<I32,GameBoard,I32>::LuaCallback will be called (with the Lua stack as parameter) when I will write GB:testFunction(17); in the Lua-script, and then the registered method will be fired and the awaited value returned.

And it works If I register and call a method without any argument. But if there is any parameter awaited as long int GameBoard::testFunct(long int); do, then I have got the following errors...

In static member function static Tr tUnpackLuaArgs<0u>::unpack(IDelegate*, lua_State*, ArgsValue ...) [with C = GameBoard, Tr = int, Args = {long int}, ArgsValue = {std::basic_string}, lua_State = lua_State]’:

instantiated from ‘static Tr tUnpackLuaArgs::unpack(IDelegate*, lua_State*, ArgsValue ...) [with C = GameBoard, Tr = int, Args = {long int}, ArgsValue = {}, unsigned int i = 1u, lua_State = lua_State]’

instantiated from ‘static int LuaCall::LuaCallback(lua_State*) [with C = GameBoard, Args = {long int}, lua_State = lua_State]’

error: no match for call to ‘(MethodDelegatePtr) (std::basic_string&)’

note: no known conversion for argument 1 from ‘std::basic_string’ to ‘long int&’


In static member function ‘static Tr tUnpackLuaArgs<0u>::unpack(IDelegate*, lua_State*, ArgsValue ...) [with C = GameBoard, Tr = int, Args = {long int}, ArgsValue = {bool}, lua_State = lua_State]’:

instantiated from ‘static Tr tUnpackLuaArgs::unpack(IDelegate*, lua_State*, ArgsValue ...) [with C = GameBoard, Tr = int, Args = {long int}, ArgsValue = {}, unsigned int i = 1u, lua_State = lua_State]’

instantiated from ‘static int LuaCall::LuaCallback(lua_State*) [with C = GameBoard, Args = {long int}, lua_State = lua_State]’

error: no match for call to ‘(MethodDelegatePtr) (bool&)’

note: no known conversion for argument 1 from ‘bool’ to ‘long int&’

I'm unable to find why ArgsValue try to pass a std::basic_string<char> or a bool when I register a method awaiting for a long int... It should pass a long int.

And here is the class I wrote to extract the parameters coming from the Lua script function call.

template< unsigned int i >
class tUnpackLuaArgs
{

    public:
        template< class C, class Tr, class... Args, class... ArgsValue >
        static Tr unpack( IDelegate<C,Tr,Args...>* ideleg, lua_State *L, ArgsValue... argsVal)
        {
            int t = lua_type(L, i+1);

            if( t == LUA_TNUMBER)
            {
                I32 tmpUint = lua_tonumber(L, i+1);
                return tUnpackLuaArgs< i-1 >::unpack( ideleg, L, argsVal..., tmpUint);
            }
            else if( t == LUA_TSTRING)
            {
                std::string tmpStr = lua_tostring(L, i+1);
                return tUnpackLuaArgs< i-1 >::unpack( ideleg, L, argsVal..., tmpStr);
            }
            else if( t == LUA_TBOOLEAN)
            {
                bool tmpBool = lua_toboolean(L, i+1);
                return tUnpackLuaArgs< i-1 >::unpack( ideleg, L, argsVal..., tmpBool);
            }
            //etc.
        }
};


template<>
class tUnpackLuaArgs<0>
{
public:
    template< class C, class Tr, class... Args, class... ArgsValue >
    static Tr unpack( IDelegate<C,Tr,Args...>* ideleg, lua_State *L, ArgsValue... argsVal)
    {
        //-- Execute the registered method using the LUA arguments
        //-- and returns the returned value
        return (*ideleg)( argsVal... );
    }
};

And here is how I use it:

// Specialized template returning an integer
template <class C, class... Args>
struct LuaCall<int, C, Args...>
{
    static int LuaCallback(lua_State *L)
    {    
        //-- retrieve method "IDelegate" from Lua stack etc.
        //-- then call tUnpackLuaArgs with the arguments to push the returned value onto the lua stack
        lua_pushnumber(L, tUnpackLuaArgs< sizeof...(Args) >::unpack(funcPtr,L));
        return 1;
    }
};

Indeed if I remove the LUA_TSTRING and the LUA_TBOOLEAN from the if/else in unpack function, it does compile and works fine.

1

1 Answers

1
votes

As it is, if your code contains e.g. tUnpackLuaArgs<1>::unpack, then the body of unpack will eagerly instantiate all possibilities (since they're all possible to reach at runtime depending on the type of the arguments on the Lua stack).

This includes e.g. tUnpackLuaArgs<0>::apply<C, Tr, Args..., std::string>, which means you get the error you have when Args is e.g. long int since the std::string can't be converted to the long int const& that the operator() of IDelegate expects.

You don't actually need all those possibilities: you already know what you're expecting (like long int in your case). Extracting an std::string when you want a long int is of no use. So instead you should try to extract from the Lua stack what you're expecting (and likely error out if the conversion doesn't work, which you won't know until runtime).