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

     虽然之前看过《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函数确保给定参数具有特定的类型,否则它会引发一个错误。
  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Lua与C的相互调用Lua的一个重要特性,可以使得Lua获得更高的灵活性和扩展性。Lua提供了一系列的API函数,使得Lua与C之间的交互变得更加容易。下面我将介绍一下Lua与C相互调用的一些基本知识和方法: 1. Lua调用C函数 Lua调用C函数的方法很简单,只需要将C函数注册到Lua即可。我们可以使用lua_register函数将C函数注册到Lua,然后在Lua脚本使用该函数即可。例如,下面的代码将一个名为add的C函数注册到Lua: ``` int add(lua_State* L) { int a = luaL_checknumber(L, 1); int b = luaL_checknumber(L, 2); lua_pushnumber(L, a + b); return 1; } int luaopen_mylib(lua_State* L) { lua_register(L, "add", add); return 1; } ``` 在上面的代码,我们定义了一个名为add的C函数,该函数接受两个整数参数,然后将它们相加并将结果压入Lua。接着我们使用lua_register函数将该函数注册到Lua,并将其命名为add。最后我们将该函数打包成一个Lua模块,并使用luaopen_mylib函数将其注册到Lua。 在Lua脚本,我们可以像下面这样使用add函数: ``` local mylib = require "mylib" print(mylib.add(1, 2)) -- 输出3 ``` 2. C调用Lua函数 C调用Lua函数的方法比较复杂,需要使用一系列的API函数。下面是一个简单的示例: ``` int call_lua_function(lua_State* L) { // 加载Lua脚本 luaL_dofile(L, "test.lua"); // 获取Lua全局变量test lua_getglobal(L, "test"); // 判断test是否为函数 if (!lua_isfunction(L, -1)) { printf("test is not a function\n"); return 0; } // 压入函数参数 lua_pushnumber(L, 1); lua_pushnumber(L, 2); // 调用函数 lua_call(L, 2, 1); // 获取函数返回值 int result = lua_tonumber(L, -1); printf("result = %d\n", result); return 0; } ``` 在上面的代码,我们首先使用luaL_dofile函数加载了一个名为test.luaLua脚本,然后使用lua_getglobal函数获取了一个名为test的全局变量,接着使用lua_pushnumber函数压入了两个参数,最后使用lua_call函数调用了test函数,并将返回值压入了Lua。我们可以使用lua_tonumber函数获取返回值并将其转换为C语言的整数类型。 在Lua脚本,我们可以这样定义test函数: ``` function test(a, b) return a + b end ``` 在这个示例,我们定义了一个名为test的函数,该函数接受两个参数,然后将它们相加并返回结果。 这就是Lua与C相互调用的基本方法,通过这些方法,我们可以在Lua使用C函数,也可以在C程序调用Lua函数,从而实现更加灵活和高效的程序设计。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值