爲LUA封裝C/C++函數(不涉及結構體等參數形式)

四、爲LUA封裝C/C++函數(不涉及結構體等參數形式)

        由上例中的int _cdecl MyCMax(lua_State* L)函數的實現,可以看出lua調用一個非lua_CFunction類型的函數的過程

(1) 爲該函數實現一個lua_CFunction類型的函數(或模板)封裝。

(2) 調用LUA庫的註冊函數以某name註冊封裝函數

    (3) 封裝函數從LUA棧中爲原函數(被封裝)獲取參數。

    (4) 封裝函數使用(3)所獲取參數call原函數。

    (5) 最後封裝函數將原函數的返回值pushLUA棧。

   

        要註冊一個lua_CFunction類型的函數funcLUA中去,我們至少需要提供該函數的地址以及註冊名。那麼,想像中的封裝函數最簡略的形式看起來可能是這樣的:

    這是我們希望的最簡略的註冊方式(一步到位,那是最好不過的,暫且假設它就是我們要實現的形式)。接下來是處理封裝函數,封裝函數必須具有lua_CFunction的形式:

        template <typename _Ft> int lua_CFAdapter (lua_State* L);

但是,這就少了具體函數地址的信息。可以用類或結構體(它們能攜帶一些額外的信息,當然全局變量也可以)來彌補這個缺點(如下面代碼中的struct lua_CFAdapter)。下面直接給出基本代碼部分,以及最終註冊C函數的方式:

上面就是封裝的基本框架(指的是我正在做的,而不是專業封裝的基本框架),全部完成後可以下面的方式註冊C函數(這些函數的參數類型爲基本類型和各類指針)

            void MyCPrint(const char* str); 

 lua_fbinder< void MyCPrint(const char* str, MyCPrint)>:: Register (L, "MyCPrint");

(非常遺憾的是要傳函數類型,還要直接傳遞函數名, 如果不考慮重載函數的情況,可以這樣處理。lua_fbinder<MyCPrint)>:: Register (L, "MyCPrint",MyCPrint); 這樣可做個宏:#define Lua_CFRegister(L, rname, fname)   /

lua_fbinder< fname >:: Register (L, # rname, fname); 

然後,用宏Lua_CFRegister(L, RegisterName, MyCPrint);即可。 止外接下來的實現還存在一個問題:不能註冊_stdcall的函數和可變參數的函數,當然在適當的地方加上_stdcall或修改即可,但這要拷貝一份部分代碼。)

對於C++函數,由於有重載的情況會出現歧義,因此,需要指明函數類型。

struct lua_fbinder 中,模板參數_Fv是函數地址,每個函數都需要要封裝一次,而static int _cdecl lua_CFunction(lua_State* L)也很方便從中獲取函數地址

            從調用方式上看,還有比起全手寫還有那麼一點吸引力(有很多庫完成了很好的封裝,這裏僅是一種自我嘗試,娛樂),但是如何封裝呢?注意到struct lua_fbinder結構,其靜態函數int lua_CFunction ( lua_State *L )可由其獲得的信息只有C函數指針_Fv,但是函數參數類型和返回值類型都不十分明確。我們可以通過模板編程來自動匹配,從而獲取具體的信息。先看一下封裝函數的實現:

                 

                   

       靜態函數int lua_CFunction ( lua_State *L )調用了一個函數_call_cfuncion(),這個函數,應該說是函數模板族,它有兩個參數,其中第二個就是函數類型,但在模板實現中,模板參數個數不再是單獨的_Ft,具體如下:

template <typename _R> inline int _call_cfuncion(lua_State* L, _R (*func)());

template <typename _R, typename _A> inline int _call_cfuncion(lua_State* L, _R (*func)(_A));

//當然不僅這兩個,上面只是無參數和一個參數的情況,由於處理相同,只給出兩個做示例。

 

函數特徵模板集成了函數參數和返回值的特徵,也就是映射了C數據類型與LUA數據類型的對應關係,即封裝了數據的pushpop操作。

   下面是函數特徵模板function_traits的實現(其中數據特徵模板data_traits隨後給出)

         

 

    數據特徵模板data_traits的實現(數據類型映射,並實現存取操作):   

       

這是最後的一個模板_call_cfuncion2_t,也是實際進行棧操作和執行原函數的具體實現,下面是是上面提到的“錯誤做法”(別外的正確的做法可像上面的按參數個數實現)

這裏由於原函數的返回值類型爲void時,data_traits沒有實現任何操作(也就沒有入棧操作),所以需要分情況處理,就有了data_traits模板特化的情況。

最後,要指出的是,這裏沒有對不定參數的處理,但可以輕鬆(只是代碼不少,copy有點累)地來實現它。

 

參考資料:

(1)Lua 5.1 Reference Manual

(2)Programming in Lua

(3)http://www.cppblog.com/zzxhang/

(4)http://www.cppblog.com/kevinlynx/archive/2008/08/13/58684.html

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章