這是一個建立於 的文章,其中的資訊可能已經有所發展或是發生改變。
有一個需求,就是使用go語言來實現一些基本模組,使用lua來實現基本邏輯,於是就有了在lua中調用go函數的需求。
go的資料非常少,好在go可以嵌入c語言,使得這個實現變得有可行性。
最終使用了lua中的full userdata來實現這個東西。將go中的函數全部封裝成userdata,供lua調用。這樣是可行的,由於userdata可以設定metatable,metatable內可以設定一個原方法__call,那麼如下的調用:func(1, 3) 就可以變成func.metatable.__call(func, 1, 3),我們只要在c中實現這麼一個元方法,裝入特定的metatable中,將此metatable設為函數userdata的metatable,那麼就可以實現這個需求了。
在lua指令碼中的所有調用,均會調用到go中的c代碼部分,在c函數中,將此userdata解析,確定到某個go函數,那麼在go中再調用go語言部分,那麼lua-》go的橋樑就已經打通了。
那麼,我們可以在go中定義一個結構
type LuaGo_State struct { ....BasePart gofunctions map[int]interface{} baseSeq int}
在BasePart中應該已經實現了lua的一些基本操作,比如建立什麼的,我就不貼了。baseSeq是匯出的gofunction的序號,用於回調時確定回調的go函數。gofunctions則記錄著某個序號對應的gofunction。
然後,我們需要在lua_state中記錄該lua_state中對應的LuaGo_State。
//開啟gofunction註冊支援func (this *LuaGo_State) LuaGo_OpenInvokeCompenent() {//初始化匯出mapC.luago_open(this.handle, unsafe.Pointer(this))this.gofunctions = make(map[int]interface{})this.baseSeq = 1}
於是我們要在c代碼中壓入LuaGo_State。
接下來就是註冊某個Go函數了,每個go函數對應一個LuaGo_State裡面的序列,然後在map中做下記錄,最後在c代碼中進行註冊。
//壓入gofunctionfunc (this *LuaGo_State) LuaGo_PushGoFunction(_name string, _func LuaGo_Function) int {funcSeq := this.luago_getGoFunctionSeq()this.gofunctions[funcSeq] = _func//註冊到lua中cfuncname := C.CString(_name)C.luago_pushGoFunction(this.handle, cfuncname, C.int(funcSeq))C.free(unsafe.Pointer(cfuncname))return funcSeq}
在c代碼中,思路就是為這個go函數建立一個userdata,userdata的值就是它的序號,然後設定好上述的metatable,記錄在lua_state的全域變數裡。那麼在lua中進行函數調用時候,就會調用我們c中的原方法。
//元方法static int luago_metamethod_call(struct lua_State* L){//stack userdata, parameters...if(lua_isuserdata(L, 1)){//check validint* pUserData = (int*)luaL_checkudata(L, 1, g_szGoMetaTable);if(0 != pUserData){int nGoFunctionSeq = *pUserData;// get the golua_statevoid* pLuaGoState = luago_getGoLuaState(L);//call go callbackreturn (int)luago_call(pLuaGoState, nGoFunctionSeq);}else{luaL_argcheck(L, pUserData != 0, 1, "GoFunction expected");}}luago_error(L, "trying to call a non-callable object");return 0;}
這個也很簡單,將userdata的序列取出,調用go的回呼函數,在go中通過這個序列找出對應的函數,調用這個函數即可。