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() { // 初始化导出map C.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代码中进行注册。
// 压入gofunction func (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 valid int* pUserData = (int*)luaL_checkudata(L, 1, g_szGoMetaTable); if(0 != pUserData) { int nGoFunctionSeq = *pUserData; // get the golua_state void* pLuaGoState = luago_getGoLuaState(L); // call go callback return (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中通过这个序列找出对应的函数,调用这个函数即可。
有疑问加站长微信联系(非本文作者)