当前位置:文档之家› Lua编程之lua和c之间互相调用方式

Lua编程之lua和c之间互相调用方式

Lua编程之lua和c之间互相调用方式
Lua编程之lua和c之间互相调用方式

Lua编程之Lua和C之间互相调用方式

Lua和C调用主要有两种方式:

1.在C中运行,C函数调用Lua,Lua要注册C函数库。

2.程序在lua中运行,C注册到lua中。

第一种方式看起来很奇怪。既然程序主体运行在C中,而且最终使用的也是C中定义的函数,那么为何要将Lua函数注册给Lua,然后再通过Lua 调用函数呢?其实lua库在此调用中只是一个中间库,他只是一个table 保存C库函数的指针,一旦C函数注册到Lua中,Lua就可以直接通过C 函数的引用获取到C函数的地址(这也是我们注册的意义,将C函数的地址提供给Lua)。也就是说,我们必须把C函数的地址以一个适当的方式传递给Lua解释器。而这个指针只是保存在了Lua虚拟栈中。

首先看第二种Lua如何调用C。

lua可以将C函数注册到lua中,C函数必须遵循统一的原型格式,这个原型定义在lua.h中.

typedefint (*) (lua_State *)

被注册的C函数接收一个单一的lua_State类型的参数,同时返回一个表示返回值个数的数字。函数在将返回值入栈之前无需清理栈,在函数返回之后,Lua会自动清除栈中返回结果下面的所有内容。

用C函数扩展lua时,一般将所有的C函数编译成一个独立的模块,方便增加新的函数。

//mylib.c

#include

#include

#include

#include

#include

/* 所有注册给Lua的C函数具有

* "typedefint(*lua_CFunction) (lua_State *L);"的原型。*/

staticintmyadd(lua_State *L){

//如果给定虚拟栈中索引处的元素可以转换为数字,则返回转换后的数字,否则报错。

int a = luaL_checknumber(L, 1);

int b = luaL_checknumber(L, 2);

/* 这里可以看出,C可以返回给Lua多个结果, *

通过多次调用lua_push*(),之后return返回结果的数量。 */

lua_pushnumber(L, a+b);

return 1;

}

//luaL_Reg结构体的第一个字段为字符串,在注册时用于通知Lua该函数的名字。

//每一个元素中包含此函数在Lua中的名字,以及该函数在C库中的函数指针。

//结构体数组中的最后一个元素的两个字段均为NULL(必须),用于提示Lua注册函数已经到达数组的末尾。

staticconststructluaL_Regmylib [] = {

{"add", myadd},

{NULL, NULL}

};

//该C库的唯一入口函数。其函数签名等同于上面的注册函数。见如下几点说明:

//1. 我们可以将该函数简单的理解为模块的工厂函数。

//2. 其函数名必须为luaopen_xxx,其中xxx表示library名称。Lua代码require "xxx"需要与之对应。

//3. 需要强调的是,所有需要用到"xxx"的代码,不论C还是Lua,都必须保持一致,这是Lua的约定, // 否则将无法调用。

extern intluaopen_mylib(lua_State *L){

luaL_newlib(L, mylib);

return 1;

}

将”mylib.c”编译为动态连接库,

//call.lua

#!/usr/local/bin/lua

--[[ 这里"require"的参数对应C库中"luaopen_mylib()"中的"my lib"。

C库就放在"a.lua"的同级目录,"require"可以找到。]]

lib=require "mylib"

print(lib.add(1, 2))

每个被lua调用的C函数都有自己的私有栈,压入参数的索引从1开始递增,结果值也是直接压入栈中,函数返回时会将压入的参数全部删除,只留下结果值。mylib[]声明了模块中所有C函数列表,每一项映射了C函数在lua中的命名,比如上面代码中myadd函数在lua中用add表示,列表

必须用{NULL,

NULL}结束。luaL_newlib在栈中创建一个table,将mylib数组中的C函数注册进这个table中。luaopen_mylib将这个table中的函数加载进lua 环境中。

附加:

1、每一个与Lua通信的C函数都有其独有的虚拟栈。

2、在极端情况下,打印指定目录中文件的例子可能会造成小小的内存泄漏。在内存空间不足的情况下,l_dir()中的lua_newtable()、lua_ pushstring()和lua_settable()都会立即抛出错误并终止程序的运行,这将导致closdir()无法被调用。

3、通常C库中“特殊的函数”都被定义为公有的(使用extern修饰),而其他函数均被定义为私有的(使用static修饰)。

4、当你想要使用C函数扩展你的Lua程序时,即使只有一个C函数,也最好使用C库的方式。因为在不久的将来(通常来说会很快),你将需要其他的C函数。

C函数调用lua

#include

#include

#include

#include

#include

#include

#include

staticint l_sin(lua_State *L) {

//如果给定虚拟栈中索引处的元素可以转换为数字,则返回转换后的数字

,否则报错。

double d = luaL_checknumber(L, 1);

lua_pushnumber(L, sin(d));

/* push result */

/* 这里可以看出,C可以返回给Lua多个结果,

* 通过多次调用lua_push*(),之后return返回结果的数量。

*/

return1;

/* number of results */

}

int main(void) {

lua_State *L = luaL_newstate(); //

创建Lua状态机。luaL_openlibs(L); //

打开Lua状态机"L"中的所有Lua标准库。

lua_pushcfunction(L, l_sin);

//将C函数转换为Lua的"function"并压入虚拟栈。lua_setglobal(L, "mysin");

/* 这两句话还有更简单的方法:

* lua_register(L, "mysin", l_sin)

* 将C函数"l_sin"定义为Lua的全局变量"mysin"。

* 其实现是如下宏:

* #definelua_register(L,n,f) (lua_pushcfunction(L, f), lua_setglobal(L, n))

*/

//

弹出栈顶元素,并在Lua中用名为"mysin"的全局变量存储。constchar*

testfunc = "print(mysin(3.14 / 2))";

if(luaL_dostring(L, testfunc)) // 执行Lua命令。

printf("Failed to invoke.\n");

lua_close(L); // 关闭Lua状态机。

return0;

}

prompt>gccmain.c-llua-ldl-lm-Wall

prompt>./a.out

总结

Lua在调用C时候,主体函数实在lua程序中得到运行,此时c程序作为lua 的库函数,可在c程序中完成lua程序的扩展。由于Lua是动态类型语言,在Lua语言中没有类型定义的语法,每个值都携带了它自身的类型信息,而C语言是静态类型语言;另一个是Lua使用垃圾收集,可以自动管理内存,而C语言要求程序自己释放分配的内存,需应用程序自身管理内存。为了解决这个两个问题,Lua引入了一个虚拟栈。

所以如果要完成互相调用,要在C程序中提供可供lua调用的函数原型就是typedefint (*) (lua_State *),其中的lua_State 就是Lua和C相互传递数据时用到的虚拟栈,在C程序中要完成以下几个步骤.

1.定义被调用的函数原型(typedefint (*) (lua_State *))。

2.注册函数名到luaL_Reg 类型的表中tt[],该表是一个{“key”,

value}值类型,key表示在lua程序中要访问的函数名字,value表示C程序中函数原型的函数名。列表要以{null,null}结束。

3.要提供一个lua解释器可以调用的函数。该函数是一个公用函数要

以extern 申明,意为在lua中直接查找调用。要以luaopen_xxx

命名,此函数的功能是把上述luaL_Reg类型的列表tt注册到虚拟栈中。

4.把c程序编译为动态链接库。具体步骤省略。

5.在lua程序中require“xxx”,xxx与luaopen_xxx中的后缀xxx

必须相同,这是应为lua解释器查找的原因。

C调用lua程序时,主体函数在C程序中运行,lua提供了很多C API,也就是提供了一个状态机实现c程序对lua程序的加载和执行。步骤有以下几步:

1.调用luaL_newstate()函数创建lua状态机,返回一个lua_State类型的虚

拟栈。

2.打开状态机。

3.往虚拟栈中压人数据,

lua_pushcclosure(L, func, 0) // 创建并压入一个闭包

lua_createtable(L, 0, 0) // 新建并压入一个表

lua_pushnumber(L, 343) // 压入一个数字

lua_pushstring(L, “mystr”)// 压入一个字符串

压入的类型有数值, 字符串, 表和闭包[在c中看来是不同类型的值], 但是最后都是统一用TValue这种数据结构来保存的。

4. 关闭状态机

相关主题
文本预览
相关文档 最新文档