lua 与c#交互原理
自从ulua在官网上出来后,lua 就被U3D开发人员喜爱。国内有几个高手把lua拿过来 接着进行了封装。很多都是新手转过来。lua语法一看遍知,但是大多数人还是不明白两个语言之间的互相调用是怎么一回事,这也是难点和重点。所以今天想跟大家分享一下这方面的知识,让大家少走弯路吧。
先看看u3d 里面c# 调用lua 是c# 先调用了lua的dll它是一个C库这个C库又调用了lua的东西这样才实现了c#和lua的一个通信.
所以我们先来分析一下c与lua:
C与lua交互面临以下几个问题: 1, 由于lua里面的数据都是动态加载的所以内存也是动态分配的,也没有static 这样的修饰,而C里面有 static const 这样的静态类型数据 2, c里面是手动管理内存lua 里面是自动管理内存
所以为了解决这些问题就采用了一个抽象栈来让lua与c交换值,好比c里面的 void* 可以指向任何地址而不用管存储类型。这样两边都不管你传的何种类型的参数,只用知道它是一个地址指针。两边用的时候再强制转换成对应的类型。
我们看一下C调用lua示例:
在lua中定义一个table :background = {r=0.30, g=0.10, b=0}在c中这样取这些值
#define MAX_COLOR 255先定义了一个函数 供以下使用:int getfield (const char key) { int result;lua_pushstring(L, key); //将参数 key 栈为2或者-1入栈lua_gettable(L, -2); // 它会将栈顶做为key并将value返回到栈顶 if (!lua_isnumber(L, -1)) //栈顶值判断是否是一个数字 error(L, “invalid component in background color”);result = (int)lua_tonumber(L, -1) * MAX_COLOR; //将返回的栈顶值拿来进行强制转换 int lua_pop(L, 1); / remove number */ return result;}
这是咱们lua程序的起始位置 假设前面的lua环境已经加载好了:
lua_getglobal(L, “background”); // 这句话将lua 表background获取放在栈 1的位置if (!lua_istable(L, -1)) error(L, “`background’ is not a valid color table”);
red = getfield(“r”);green = getfield(“g”);blue = getfield(“b”);
具体分析:第一步: lua_getglobal(L, “background”); //这句话将lua 表background获取放在栈1的位置
第二步: getfield(“r”); lua_pushstring(L, key); //将参数“r”key栈为2或者-1入栈
第三步: lua_gettable(L, -2); //它会将栈顶做为key并将value返回到栈顶并去除 r (这个地方是lua内部的协议就会默认的将表以上的栈作为参数key传入并返回值将key去除)
第四步: result = (int)lua_tonumber(L, -1) * MAX_COLOR; //将返回的栈顶值拿来进行强制转换 int
第五步: lua_pop(L, 1); //将栈顶也就是result进行剔除栈
这样 c 就拿到了变量 red
注意: 栈索引既可以是正数索引也可以是负数, 正数最底下的为1最上面的为-1 lua_gettable(L, -2); //这个地方是lua内部的协议: 1,表以上的栈作为参数key传入 2,将key从栈中去除 3,并返回值放入栈顶
这是lua跟C传参的内置协议大家要明白了它就会有这么几步操作。
lua 调用C :第一步:定义c函数
向lua注册的函数必须要有这样的结构返回值为int传入参数为lua_State*typedef int (*lua_CFunction) (lua_State *L);
所以:static int l_sin (lua_State *L) {double d = lua_tonumber(L, 1); /*第一个参数总是在这个私有栈的index=1的位置//获取参数 /lua_pushnumber(L, sin(d)); / 第结果放入栈中 /return 1; / number of results */}
第二步向lua注册:
lua_pushcfunction(l, l_sin); // 这里相信大家要知道为啥u3d里面要注册wrap文件了lua_setglobal(l, “mysin”);
第三步:lua调用:
reuslt = mysin(45) // 取出的就是栈顶的值
分析 :
调用 reuslt = mysin(45)实则是调用:
static int l_sin (lua_State L) {double d = lua_tonumber(L, 1); / 获取参数 45 /lua_pushnumber(L, sin(d)); / 第结果放入栈顶就是返回给 reuslt /return 1; / number of results */}
注意: 1,每一个函数都有一个私有栈并且第一个参数就是在栈的1位置,后面以此类推有几个参数就有多少个 2,如果函数返回结果,第一个结果被第一个入栈,因此如果有n个返回结果,第一个返回结果在栈中的位置为-n,最后一个返回结果在栈中的位置为-1
c#就是在这基础之上又调用了C api所以相信大家在看c#与lua也就明白很多了。也知道为什么要进行wrap,wrap之后为什么可以直接使用了。