|
上篇文章简单说到了 lua_pcall 这个方法,不过没有考虑到参数和返回值的情况,本节重点讲这个函数,还会讲如何把C#端的方法放在lua的栈上以供lua调用。
<hr/>先上代码:
var L = LuaDLL.luaL_newstate();
var path = Application.dataPath + &#34;/Examples/03_Function/03.lua&#34;;
LuaDLL.luaL_loadfile(L, path);//加载lua文件
LuaDLL.lua_pcall(L, 0, 0, 0);//运行lua文件
LuaDLL.lua_getglobal(L, &#34;addandsub&#34;);
LuaDLL.lua_pushnumber(L, 10);
LuaDLL.lua_pushnumber(L, 20);
if (LuaDLL.lua_pcall(L, 2, 2, 0) != 0)
{
Debug.LogError(LuaDLL.lua_tostring(L, -1));
}
Debug.Log(LuaDLL.lua_tonumber(L, -1));
Debug.Log(LuaDLL.lua_tonumber(L, -2));
LuaDLL.lua_close(L);
Lua代码如下:
function addandsub(x,y)
return x+y,x-y
end
结果是先打印-10,再打印30
<hr/>函数、参数、返回值的入栈顺序
上面的各个方法之前我们已经学过了,这里需要理解 函数、参数、返回值的入栈顺序。如图所示:
- 先将方法入栈 - 再将方法需要的参数按顺序入栈 - 调用 lua_pcall - 此时lua会把返回值入栈
<hr/>上面的东西很简单,接下来讲更重要的东西,如何把C#的方法放到Lua里面? 这个问题本质上是如何让C来调用C#。 先放代码:
private void LuaHelloWorld(IntPtr L)
{
var functionIntptr = Marshal.GetFunctionPointerForDelegate(new MyCSFunction(HelloWorld));
LuaDLL.lua_pushcclosure(L, functionIntptr, 0);
LuaDLL.lua_pcall(L, 0, 0, 0);
}
[MonoPInvokeCallbackAttribute(typeof(MyCSFunction))]
private static void HelloWorld(IntPtr L)
{
Debug.Log(&#34;helloworld&#34;);
}
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
public delegate void MyCSFunction(IntPtr L);
<hr/>Lua调用C#方法
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
public delegate void MyCSFunction(IntPtr L);
UnmanagedFunctionPointer就是字面意思,表面这个委托不受C#管理。在C++调用C#端函数也会用到这个特性。
CallConvention(调用约定):决定函数参数传送时入栈和出栈的顺序,由调用者还是被调用者把参数弹出栈,以及编译器用来识别函数名字的修饰约定。Cdecl表示由调用方把参数出栈,除了Cdecl外还有好几种。感兴趣的看这篇文章:调用约定(Calling convention)的介绍。
[MonoPInvokeCallbackAttribute(typeof(MyCSFunction))]
private static void HelloWorld(IntPtr L)
{
Debug.Log(&#34;helloworld&#34;);
}
MonoPInvokeCallbackAttribute 用来标记这个方法是由C或者C++来调用的
var functionIntptr = Marshal.GetFunctionPointerForDelegate(new MyCSFunction(HelloWorld));
Marshal是System.Runtime.InteropServices命名空间下的一个类,作用是提供了一个方法集合,这些方法用于分配非托管内存、复制非托管内存块、将托管类型转换为非托管类型,此外还提供了在与非托管代码交互时使用的其他杂项方法。MSDN上有更详细的说明和案例,MSDN关于Marshal类
LuaDLL.lua_pushcclosure(L, functionIntptr, 0);
使用lua_pushcclosure将函数入栈,第三个参数的意思是入栈的函数是否需要关联除了luastate以外的参数,这里不需要其他参数,所以写0。tolua里的lua_pushcclosure这个函数和lua提供的接口有些不同。lua提供的是这样的,
我推测是lua要和C#交互,所以tolua对接口进行了修改。 现在我们做完了全部准备工作,调用lua_pcall就能在unity的控制台上看到helloworld了。
<hr/>lua_pcall传入错误处理函数
现在再来看加入错误处理函数的代码:
void Start()
{
var L = LuaDLL.luaL_newstate();
HandleError(L);
LuaDLL.lua_close(L);
}
private void HandleError(IntPtr L)
{
var functionIntptr = Marshal.GetFunctionPointerForDelegate(new MyCSFunction(ErrorHandle));
LuaDLL.lua_pushcclosure(L, functionIntptr, 0);
var errorFuncIndex = LuaDLL.lua_gettop(L);//获得栈顶位置
var path = Application.dataPath + &#34;/Examples/03_Function/03.lua&#34;;
LuaDLL.luaL_loadfile(L, path);
LuaDLL.lua_pcall(L, 0, 0, 0);
LuaDLL.lua_getglobal(L, &#34;addandsub&#34;);
LuaDLL.lua_pushnumber(L, 10);
LuaDLL.lua_pushstring(L, &#34;error&#34;);
LuaDLL.lua_pcall(L, 2, 2, errorFuncIndex);
Debug.Log(LuaDLL.lua_tonumber(L, -1));
Debug.Log(LuaDLL.lua_tonumber(L, -2));
}
[MonoPInvokeCallbackAttribute(typeof(MyCSFunction))]
private static void ErrorHandle(IntPtr L)
{
if (LuaDLL.lua_isstring(L, -1) == 1)
{
Debug.LogError(LuaDLL.lua_tostring(L, -1));
}
else
{
Debug.Log(&#34;Not find error string&#34;);
}
}
需要提一点的是,错误处理函数需要在要调用的方法入栈之前入栈。 我们故意传入一个字符串,这样字符串和数值进行加法和减法运算就会出错。 结果如下:
<hr/>大功告成,这就是本节全部内容。 github工程 对应的是Examples/03_Function
系列文章:
【Lua与C#交互①】Lua中的栈
【Lua与C#交互②】加载Lua文件
【Lua与C#交互③】方法调用和错误处理函数
qq群:891809847 |
本帖子中包含更多资源
您需要 登录 才可以下载或查看,没有账号?立即注册
×
|