2012-04-10 136 views
0

我正在嘗試編寫一個類,該類提取從Lua函數調用(Lua腳本調用已註冊的C函數)傳遞的參數,並將它們傳遞給已註冊方法(IDelegate在我的代碼片段),所以我可以執行它並返回值。使用可變參數模板從Lua函數回調(C api)提取參數

假設我寄存器從遊戲鍵盤類下面的方法: long int GameBoard::testFunct(long int);「GB:testFunction」的Lua下用下面的代碼:

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用於註冊方法,函數和函數,所以我可以稍後給他們打電話

然後LuaCall<I32,GameBoard,I32>::LuaCallback將被稱爲(以Lua堆棧爲參數)當我將在Lua腳本中寫入GB:testFunction(17);,然後註冊的方法將被觸發並返回等待的值。

它的工作原理如果我註冊並調用一個沒有任何參數的方法。 但如果等待爲long int GameBoard::testFunct(long int);任何參數做的話,我已經得到了以下錯誤......

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&’

我找不到爲什麼ArgsValue試圖通過一個std::basic_string<char>或當我註冊的方法bool等待long int ...它應該通過long int

這裏是我寫的類來提取來自Lua腳本函數調用的參數。

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...); 
    } 
}; 

這裏是我如何使用它:

// 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; 
    } 
}; 

事實上,如果我刪除LUA_TSTRINGLUA_TBOOLEAN從的if/else在unpack功能,它編譯和工作正常。

回答

0

就這樣,如果您的代碼包含例如tUnpackLuaArgs<1>::unpack,那麼unpack的主體將熱切地實例化全部的可能性(因爲它們都可能在運行時取決於Lua棧上參數的類型)。

這包括例如tUnpackLuaArgs<0>::apply<C, Tr, Args..., std::string>,這意味着當Args是例如long int由於std::string無法轉換爲long int const&,因此operator()IDelegate預期。

您並不需要所有這些可能性:您已經知道您的期望(例如您的案例中的long int)。當您想要long int時提取std::string是沒有用的。所以相反,你應該嘗試從Lua堆棧中提取你期望的內容(如果轉換不起作用,可能會出錯,直到運行時纔會知道)。

+0

需要注意的是,'tUnpackLuaArgs :: unpack'實例化後的實例化數量是N^d的數量級,其中根據您的代碼,d似乎至少爲3。爲了避免這種情況,你會在編譯時做得很好。 – 2012-04-10 08:29:56

+0

我仍然不知道如何提取這些參數,但是您的解釋很明確,並確認了我被懷疑出現此問題的原因。謝謝。 – Valkea 2012-04-10 17:28:46

+0

@Valkea你可能會感興趣的[一個解決方案](http://stackoverflow.com/questions/10014713/build-tuple-using-variadic-templates#comment12805453_10014713)使用元組沒有遞歸(它也有用時,當沒有元組參與)。請注意,這裏不可能使用遞歸。 – 2012-04-10 17:53:02