lua 与c#交互原理

原文链接:http://www.manew.com/thread-89354-1-1.html   作者:周玉杰

结合这篇博客看会比较容易理解,作者:秦元培, 链接:http://blog.csdn.net/qinyuanpei/article/details/39910099


自从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之后为什么可以直接使用了。

猜你喜欢

转载自blog.csdn.net/a673544319/article/details/74066717
今日推荐