Lua和C语言的交互(一)

Lua生来就是为了和C交互的,因此使用C扩展Lua或者将Lua嵌入到C当中都是非常流行的做法。要想理解C和Lua的交互方式,首先要回顾一下C语言是如何处理函数参数的。


C函数和参数
大家知道C语言是用汇编实现的,在汇编语言中可没有函数的概念,与函数对应的是叫做子过程的东西,子过程就是一段指令,一个子过程与它调用的子过程之间通过栈来进行参数的传递交互。在一个子过程在调用别的子过程之前,会按照约定的格式将要调用的子过程需要的参数入栈,在被调用的子过程中,可以按照约定的规则将参数从栈中取出。同理,对于返回值的传递也同样是通过堆栈进行的。C语言约定的参数放入栈中的格式,就是“调用惯例”。C语言的函数原型则决定了压入栈中的参数的数量和类型。


Lua的虚拟堆栈
Lua和C之间的交互巧妙的模拟了C语言的堆栈,Lua和C语言之间的相互调用和访问都通过堆栈来进行,巧妙的解决了不同类型之间变量相互访问的问题。具体的,我们想象如下一个图


  +-------+                      +-------+
  |       |                      |       |
  |       |      +-------+       |       |
  |   C   | <==> |       | <==>  |  Lua  |
  | Space |      |Virtual|       | Space |
  |       |      | Stack |       |       |
  |       |      |       |       |       |
  +-------+      +-------+       +-------+

  
由于C和Lua是不同层次的语言,因此C语言的变量和Lua中的变量以及函数不能直接的交互,我们假定C语言和Lua都有自己的“空间(C Space和Lua Space)”。而这两个空间之间的交互就通过上图中的这个虚拟堆栈来解决。为何采用虚拟堆栈的方式来进行交互呢?其目的是在提供强大的灵活性的同时避免交互时两种语言变量类型的组合爆炸。


C语言读写Lua全局变量(基本类型)
C语言读取Lua的全局变量是一种最简单的操作。通过上图我们可以猜测到,如果通过C语言读取Lua中的全局变量需要两步:1、将全局变量从Lua Space压入虚拟堆栈;2、从堆栈将全局变量读取到C语言Space中。在Lua和C的交互中,Lua无法看到和操作虚拟堆栈,仅在C语言中有操作堆栈的权利,因此前面说到的两步全都是在C语言中完成的。我们看一个简单的例子


Lua代码:
global_var1 = 5;
print("Print global varb from lua", global_var1);

C代码:
......
void get_global(lua_State *L)
{
        int global_var1;
        lua_getglobal(L, "global_var1");    /* 从lua的变量空间中将全局变量global_var1读取出来放入虚拟堆栈中 */
        global_var1 = lua_tonumber(L, -1);  /* 从虚拟堆栈中读取刚才压入堆栈的变量,-1表示读取堆栈最顶端的元素 */


        printf("Read global var from C: %d\n", global_var1);
}
......

执行结果:
pi@raspberrypi ~/Programming/article_lua $ ./a.out global_var.lua
Print global var from lua      5
Read global_var: 5

Lua中对堆栈的操作都是通过索引来进行的,索引为1表示从栈底数第一个元素,索引为2表示从栈底数第二个元素;同样也可以使用负数从栈顶开始计算,-1表示从栈顶数第一个元素,-2表示从栈顶数第二个元素。更多堆栈的操作函数请参考lua的官方手册http://www.lua.org/manual/5.2/manual.html。 同样从堆栈中获取元素,除了我们使用的lua_tonumber之外,还有lua_tolstring,lua_toboolean等。

通常情况下在读取变量之前还需要对堆栈中元素的实际类型做出检查:

C代码:
......
void get_global(lua_State *L)
{
        int global_var1;
        lua_getglobal(L, "global_var1");    /* 从lua的变量空间中将全局变量global_var1读取出来放入虚拟堆栈中 */
	if (!lua_isnumber(L, -1))	    /* 检查堆栈中栈顶第一个元素是否是数字 */
		error(L, "Is not number.");
        global_var1 = lua_tonumber(L, -1);  /* 从虚拟堆栈中读取刚才压入堆栈的变量,-1表示读取堆栈最顶端的元素 */
......
}

写入全局变量也一样简单:
首先将数据压入堆栈,然后再将堆栈中的数据存入全局变量。
C代码:
void set_global(lua_State *L)
{
        lua_pushinteger(L, 9);
        lua_setglobal(L, "global_var1");


        printf("set global var from C:9\n");
}

执行结果:
pi@raspberrypi ~/Programming/article_lua $ ./a.out global_var.lua
set global var from C:9
Print global var from lua       9

你可能感兴趣的:(Lua)