您的位置:首页 > 编程语言 > Lua

Lua学习笔记二之C中调用Lua

2014-02-26 20:33 232 查看
虽然之前看过《Lua程序设计》(第二版)这本书,但对C与Lua交互的部分,一直不是很理解。最近想系统学习和深入理解C与Lua的交互(实质是想通过这个,进一步阅读Lua源码,:)),因此又看了一下这本书的第4部分,下面是在学习C中调用Lua的全局变量、table和函数的笔记以及自己的理解,其中学习过程中的写的测试代码可以从我的GitHub下载。

0、Lua与C有两种交互形式。在第一种形式中,C语言拥有控制权,Lua是一个库,这种形式中的C代码称为“应用程序代码”;在第二种形式中,Lua拥有控制权,这种形式中的C代码称为“库代码”。Lua的解释器程序(lua.c)就是“应用程序代码”的一个实例;而Lua标准库(lstrlib.c,lmathlib.c等)则是“库代码”的实例。应用程序代码和库代码(注意他们都是C代码,只是功能或者说地位不同)都使用通用的API来与Lua代码通信,这些API称为C
API。

1、lua源码头文件lua.h中,定义了向C应用程序提供的基础函数,包括创建lua环境,调用lua函数(如lua_pcall)、读写Lua环境中的全局变量,以及注册供Lua调用的新函数等。lua.h中声明或定义的函数名都以lua_作为前缀。

2、头文件lauxlib.h定义了辅助库(auxiliary library, auxlib)提供的函数。在该文件中所定义的函数名都以luaL_作为前缀。该辅助库所有函数的实现,都只是用到lua.h中声明的函数,也就是说,该辅助库中所有的函数实现不会直接访问Lua的内部,该库相对于是一个较高的抽象,用于侧重于解决具体任务。

3、Lua库中没有定义任何全局变量。它将所有的状态都保存在动态结构lua_State中,所有的C API都要求传入一个指向该结构的指针。

4、在Lua与C之间交换数据时,主要有两个问题,一个是动态类型和静态类型之间的区别,其次是自动内存管理和手动内存管理之间的区别。为了解决两个问题,Lua与C之间交换数据时利用了一个抽象的栈。

5、Lua中的字符串不是以零结尾的,他们可以包含任意二进制数据。因此,它们必须同时保存一个显示的长度。将字符串压入栈中的基本函数是lua_pushlstring,它要求传入一个显示的长度参数。对于零结尾的字符串,可以使用函数lua_pushstring,这个函数通过strlen来计算字符串的长度。Lua不会持有指向外部(指Lua虚拟机外的)字符串的指针。对于所有Lua持有的字符串,它都会生成一个内部副本,或者复用现有的内容。因此,即使在这些函数立即返回后立刻释放或修改这些字符串,也不会出现问题。

6、接口中以lua_push*开头的函数,一般用来向栈中压入函数,通常Lua代码调用C函数时,调用的函数通常就用这些接口,把结果压入栈中,返回给lua(当然这些C函数也要求返回一个值,告诉Lua一共返回(压入)了多少个值)。值得注意的是,向栈中压入一个元素时,应该确保栈中具有足够的空间,可以调用lua_checkstack来检测是否有足够的空间。

7、接口中以lua_to*开头的函数,一般用于栈中获取一个值。通常在C函数中,可以用这些接口获取从lua中传递给C函数的参数。如果指定的元素不具有正确的类型,调用这些函数也不会出问题的。在这种情况下,lua_toboolean、lua_tonumber、lua_tointeger和lua_objlen会返回0,而其他函数会返回NULL。对于返回NULL的函数,可以直接通过返回值,即可以知道调用是否正确;对于返回0的函数,通常先需要使用lua_is*函数,看判断调用是否正确。

8、接口中以lua_get*开头的函数,调用后,一般会向栈顶中压入一个元素。比如void lua_gettable(lua_State *L,int index)会向栈顶压入t[k],这里的t是栈中索引为index的值,k是栈顶的值。

9、接口中以luaL_check*开头的函数,如果发生错误,则总是抛出一个错误。若正确,则会返回相应类型的值,该接口通常用来检测lua传入的参数是否正确。

10、当Lua调用的一个C函数返回时,Lua就会清空它的栈。也就是说在Lua代码中调用C函数时,当这个C函数返回后,Lua虚拟机就会把其相应的交换数据的栈清空。也说明了,对被调用的C函数,从栈中获取的值,但C函数返回后,就不能再使用了(特别是针对接口lua_tolstring)。

11、几乎所有的API函数都会抛出错误(即调用longjmp),而不是返回错误。当编写库代码时(被Lua调用的C函数),使用longjmp几乎和使用异常处理机制一样方便,Lua会捕获所有可能的错误。而当编写应用程序代码时(调用Lua的C代码),必须提供一种捕获错误的方式。在大多数应用程序中,通常都通过调用lua_pcall来运行Lua代码,这样即使发生错误了,应用程序(比如游戏主进程)也不会结束。如果要保护那些与Lua交互的C代码,可以使用lua_cpcall。这个函数类似于lua_pcall,但它接受一个C函数作为参数,然后调用这个C函数。

12、在调用lua_pcall时,第三个参数是期望的结果数量。就像lua的赋值一样,lua_pcall会根据要求的数量来调整实际结果的数量,即压入nil或丢弃多余的结果。在压入结果前,lua_pcall会先删除栈中的函数及其参数。如果一个函数会返回多个结果,那么第一个结果最先压入。

13、可以使用luaL_checktype函数确保给定参数具有特定的类型,否则它会引发一个错误。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: