在线时间:8:00-16:00
迪恩网络APP
随时随地掌握行业动态
扫描二维码
关注迪恩网络微信公众号
Lua可以与C很好地互通。主要是通过栈来通信。 引入Lua提供的C API lauxlib.h lua.h lua.hpp luaconf.h lualib.h 以下实现一个Lua的简单的解释器 #include <stdio.h> #include <string.h> extern "C" { #include <lua.h> #include <lauxlib.h> #include <lualib.h> } int main (void) { char buff[256]; int error; lua_State *L = lua_open(); /* opens Lua */ if (NULL == L) { printf("can not open lua_State"); } luaopen_base(L); /* opens the basic library */ luaopen_table(L); /* opens the table library */ //luaopen_io(L); /* opens the I/O library */ int x = lua_cpcall(L, luaopen_io, NULL); //luaopen_io在5.1版本被删除了,这里用lua_cpcall luaopen_string(L); /* opens the string lib. */ luaopen_math(L); /* opens the math lib. */ while (fgets(buff, sizeof(buff), stdin) != NULL) { error = luaL_loadbuffer(L, buff, strlen(buff), "line") || lua_pcall(L, 0, 0, 0); if (error) { fprintf(stderr, "...%s\n", lua_tostring(L, -1)); lua_pop(L, 1);//pop error message from the stack } } lua_close(L); return 0; } 函数lua_open创建一个新环境(或state)。lua_open创建一个新的环境时,这个环境并不包括预定义的函数,甚至是print。为了保持Lua的苗条,所有的标准库以单独的包提供,所以如果你不需要就不会强求你使用它们。头文件lualib.h定义了打开这些库的函数。例如,调用luaopen_io,以创建io table并注册I/O函数(io.read,io.write等等)到Lua环境中。 创建一个state并将标准库载入之后,就可以着手解释用户的输入了。对于用户输入的每一行,C程序首先调用luaL_loadbuffer编译这些Lua代码。如果没有错误,这个调用返回零并把编译之后的chunk压入栈。之后,C程序调用lua_pcall,它将会把chunk从栈中弹出并在保护模式下运行它。和luaL_laodbuffer一样,lua_pcall在没有错误的情况下返回零。在有错误的情况下,这两个函数都将一条错误消息压入栈;我们可以用lua_tostring来得到这条信息、输出它,用lua_pop将它从栈中删除。 注意luaopen_io函数在5.1的版本中被删除了,有以下三种解决方法 1. lua_cpcall(L, luaopen_io, NULL); 2. lua_pushcfunction(L, luaopen_io); 3. lua_pushcfunction(L, luaopen_io); lua_call函数没有返回值,如果函数运行出错程序会崩溃,所以应该用前两种情况。 lua_cpcall只能用来调用lua_CFunction类型的C函数,而lua_pcall和lua_call可以调用Lua函数也可以调用lua_CFunction类型的C函数
堆栈: Lua与C进行通信时采用堆栈作为中转站(由于篇幅问题就不赘述使用堆栈的原因了,http://book.luaer.cn/ 24.2 堆栈一章有详细的解释) 无论C要调用Lua的函数还是Lua要调用C函数,都是把数据先压入栈,然后再从栈顶取出数据 正如上面的例子,先由lua_loadBuffer运行一个chunk,并把结果压入栈(被编译了的chunk或一条错误信息), lua_pcall从栈中获取这个chunk并把一些临时的错误信息压入栈 Lua的栈严格先进后出,在Lua中只能对栈顶进行操作。但在C中,可以对栈中的任何位置操作,甚至可以在栈中任一位置插入和删除元素 压入元素: API有一系列压栈的函数,它将每种可以用C来描述的Lua类型压栈:空值(nil)用lua_pushnil,数值型(double)用lua_pushnumber,布尔型(在C中用整数表示)用lua_pushboolean,任意的字符串(char*类型,允许包含'\0'字符)用lua_pushlstring,C语言风格(以'\0'结束)的字符串(const char*)用lua_pushstring: void lua_pushnil (lua_State *L); void lua_pushboolean (lua_State *L, int bool); void lua_pushnumber (lua_State *L, double n); void lua_pushlstring (lua_State *L, const char *s, size_t length); void lua_pushstring (lua_State *L, const char *s); int lua_checkstack (lua_State *L, int sz); // 用于扩大栈空间 int lua_gettop(Lua_State *L) //获取栈顶的序号,同时也相当于返回栈内元素的个数 Lua从来不保持一个指向外部字符串(或任何其它对象,除了C函数——它总是静态指针)的指针。对于它保持的所有字符串,Lua要么做一份内部的拷贝要么重新利用已经存在的字符串。因此,一旦这些函数返回之后你可以自由的修改或是释放你的缓冲区。 Lua默认的栈空间为20,因为Lua不会自己去检测栈空间是否足够,所以我们需要确保不会栈溢出(一般情况下只要不是不断向栈中压入数据都不会栈溢出),lua_checkstack可以用于扩大栈空间。 Lua的栈起始序号是1,所以lua_gettop函数返回栈顶的序号相当于栈内的元素个数。 查询元素: API用索引来访问栈中的元素。在栈中的第一个元素(也就是第一个被压入栈的)有索引1,下一个有索引2,以此类推。我们也可以用栈顶作为参照来存取元素,利用负索引。在这种情况下,-1指出栈顶元素(也就是最后被压入的),-2指出它的前一个元素,以此类推。 API提供了一套lua_is*函数来检查一个元素是否是一个指定的类型,*可以是任何Lua类型。因此有lua_isnumber,lua_isstring,lua_istable以及类似的函数。所有这些函数都有同样的原型: int lua_is... (lua_State *L, int index); 实际上,这些函数都是调用了lua_type返回的变量类型,并且与宏类型常量比较(LUA_TNIL、LUA_TBOOLEAN、LUA_TNUMBER、LUA_TSTRING、LUA_TTABLE、LUA_TFUNCTION、LUA_TUSERDATA以及LUA_TTHREAD)。 为了从栈中获得值,这里有lua_to*函数: int lua_toboolean (lua_State *L, int index); double lua_tonumber (lua_State *L, int index); const char * lua_tostring (lua_State *L, int index); size_t lua_strlen (lua_State *L, int index); Lua_tostring函数返回一个指向字符串的内部拷贝的指针。你不能修改它(使你想起那里有一个const)。只要这个指针对应的值还在栈内,Lua会保证这个指针一直有效。当一个C函数返回后,Lua会清理他的栈,所以,有一个原则:永远不要将指向Lua字符串的指针保存到访问他们的外部函数中。 Lua_string返回的字符串结尾总会有一个字符结束标志0,但是字符串中间也可能包含0,lua_strlen返回字符串的实际长度。 其他堆栈操作: int lua_gettop (lua_State *L); void lua_settop (lua_State *L, int index); void lua_pushvalue (lua_State *L, int index); void lua_remove (lua_State *L, int index); void lua_insert (lua_State *L, int index); void lua_replace (lua_State *L, int index); settop可以用于设置栈顶,#define lua_pop(L,n) lua_settop(L, -(n)-1) 这个宏可用于弹出n个元素。 为了说明这些函数的用法,这里有一个有用的帮助函数,它dump整个堆栈的内容: static void stackDump (lua_State *L) { int i; int top = lua_gettop(L); for (i = 1; i <= top; i++) { /* repeat for each level */ int t = lua_type(L, i); switch (t) { case LUA_TSTRING: /* strings */ printf("`%s'", lua_tostring(L, i)); break; case LUA_TBOOLEAN: /* booleans */ printf(lua_toboolean(L, i) ? "true" : "false"); break; case LUA_TNUMBER: /* numbers */ printf("%g", lua_tonumber(L, i)); break; default: /* other values */ printf("%s", lua_typename(L, t)); break; } printf(" "); /* put a separator */ } printf("\n"); /* end the listing */ } 这个函数从栈底到栈顶遍历了整个堆栈,依照每个元素自己的类型打印出其值。它用引号输出字符串;以%g的格式输出数字;对于其它值(table,函数,等等)它仅仅输出它们的类型(lua_typename转换一个类型码到类型名)。 下面的函数利用stackDump更进一步的说明了API堆栈的操作。 #include <stdio.h> #include <lua.h> static void stackDump (lua_State *L) { ... } int main (void) { lua_State *L = lua_open(); lua_pushboolean(L, 1); lua_pushnumber(L, 10); lua_pushnil(L); lua_pushstring(L, "hello"); stackDump(L); /* true 10 nil `hello' */ lua_pushvalue(L, -4); stackDump(L); /* true 10 nil `hello' true */ lua_replace(L, 3); stackDump(L); /* true 10 true `hello' */ lua_settop(L, 6); stackDump(L); /* true 10 true `hello' nil nil */ lua_remove(L, -3); stackDump(L); /* true 10 true nil nil */ lua_settop(L, -5); stackDump(L); /* true */ lua_close(L); return 0; }
Lua调用C函数 需要先把C函数注册到Lua中(相当于把C函数的地址通过栈传递到Lua中)。 Lua调用C函数,会将参数放入栈中,C函数通过栈获取参数并返回结果。同时,C函数将会返回结果的个数。 Lua调用C函数所用的栈是私有的,每个C函数都会有一个独立的栈 任何在Lua中注册的函数必须有同样的原型,这个原型声明定义就是lua.h中的lua_CFunction: typedef int (*lua_CFunction) (lua_State *L); 假设现有这样一个函数 static int l_sin (lua_State *L) { double d = lua_tonumber(L, 1); /* get argument */ lua_pushnumber(L, sin(d)); /* push result */ return 1; /* number of results */ } lua_pushcfunction(l, l_sin); // push function into stack lua_setglobal(l, "mysin"); // named as "mysin" , finish reg lua_pushcfunction会把l_sin压入栈,lua_setglobal会把l_sin重新编译并赋值给全局变量'mysin' , 完成注册后即可调用。^o^ 采用前面提到的简单Lua解释器的C实现就可以轻松调用了。 未完待续
|
请发表评论