lua学习25:《Lua程序设计(第2版)》第25章
来源:互联网 发布:南宁云宝宝大数据 编辑:程序博客网 时间:2024/06/04 19:13
1、C语言从lua文件里读取配置,可以用lua程序充当C程序的配置文件,这样设计的程序在更改某个配置信息时可以不需要修改C代码重新编译程序。
--lua文件,C程序从lua文件里读取width和height的配置
--定义窗口大小
width = 200
height = 300
C程序:
void load (char *filename, int *width, int *height){lua_State *L = luaL_newstate(); //打开 Lua ,创建一个新环境lua_openlibs(L); //打开标准库if (luaL_loadfile(L, filename) //读取文件中内容并将内容压入栈 || lua_pcall(L, 0, 0, 0)) //运行栈中内容,运行完后所有的参数以及函数本身都会出栈 { //将栈顶的错误信息转换成字符串并输出error(L, "cannot run configuration file: %s", lua_tostring(L, -1)); }lua_getglobal(L, "width"); //获取width变量并压入栈顶lua_getglobal(L, "height"); //获取height变量并压入栈顶//此时height在栈顶(-1),width在栈顶下一个(-2)if (!lua_isnumber(L, -2))error(L, "`width' should be a number\n");if (!lua_isnumber(L, -1))error(L, "`height' should be a number\n");*width = (int)lua_tonumber(L, -2);*height = (int)lua_tonumber(L, -1);lua_close(L);}
error源码:
#include <stdarg.h>#include <stdio.h>#include <stdlib.h>void error (lua_State *L, const char *fmt, ...){va_list argp;va_start(argp, fmt);vfprintf(stderr, argp);va_end(argp);lua_close(L);exit(EXIT_FAILURE);}
2、C中读取table
#define MAX_COLOR 255/* 假设table位于栈顶 */int getfield (const char *key){int result;lua_pushstring(L, key); //将key压栈lua_gettable(L, -2); //将对应key值出栈,将key对应的value压栈,key压栈后原来位于栈顶的table的索引变为-2if (!lua_isnumber(L, -1)) //判断栈顶的value是不是可以转换为数字error(L, "invalid component in background color");result = (int)lua_tonumber(L, -1) * MAX_COLOR;lua_pop(L, 1);//这里第二个参数1是弹出元素的个数,不是索引return result;}其中
lua_pushstring(L, key);
lua_gettable(L, -2);
等价于lua_getfield(L, -1, key),由于没有向栈中压入字符串key,所以当调用lua_getfield时,table的索引为-1。
lua_gettable()等价于lua_rawget()
3、C中赋值table
/* 假设table位于栈顶 */void setfield (const char *index, int value){lua_pushstring(L, index); //将key压栈lua_pushnumber(L, (double)value/MAX_COLOR); //将key对应的value压栈lua_settable(L, -3); //弹出栈中的key和value,将table[key]=value压栈}其中
lua_pushstring(L, index); //将key压栈
lua_settable(L, -3); //弹出栈中的key和value,将table[key]=value压栈
等价于lua_setfield(L, -2, key),由于没有向栈中压入字符串key,vlaue压入栈顶,所以当调用lua_getfield时,table的索引为-2。
lua_settable()等价于lua_rawset()
最后调用lua_setglobal(L, table_name)生成一个名为“table_name”的table,#define lua_setglobal(L, S) (lua_pushstring(L, S), lua_insert(L, -2), lua_settable(L, LUA_GLOBALSINDEX))
#define lua_setglobal(L, S) lua_setfield(L, LUA_GLOBALSINDEX), S)
注5.2版本已经没有LUA_GLOBALSINDEX,编译的时候需要加 #define LUA_GLOBALSINDEX LUA_RIDX_GLOBALS
4、C中调用lua函数
/* 调用Lua中定义的函数'f' */double f (double x, double y){double z;/* 压入函数和参数 */lua_getglobal(L, "f"); /* 从lua中获取全局函数'f'并压栈 */lua_pushnumber(L, x); /* 压入第一个参数 */lua_pushnumber(L, y); /* 压入第一个参数 *//* 完成lua中函数调用 (2个参数, 1个返回值),调用完成后会将函数已经参数出栈,故栈中只留返回值*/if (lua_pcall(L, 2, 1, 0) != 0)error(L, "error running function `f': %s",lua_tostring(L, -1));/* 检索结果 */if (!lua_isnumber(L, -1))error(L, "function `f' must return a number");z = lua_tonumber(L, -1);lua_pop(L, 1); /* 弹出返回值 */return z;}
调用lua_pcall时,第2个参数是指定lua函数的参数的个数,第3个参数是指定lua函数的返回结果的个数。第4个参数可以指定一个错误处理函数。和Lua中赋值操作一样,lua_pcall会根据你的要求调整返回结果的个数,多余的丢弃,少的用nil补足。在将结果入栈之前,lua_pcall会将栈内的函数和参数移除。如果函数返回多个结果,第一个结果被第一个入栈,因此如果有n个返回结果,第一个返回结果在栈中的位置为-n,最后一个返回结果在栈中的位置为-1。
如果lua_pcall运行时出现错误,lua_pcall会返回一个非0的结果。另外,他将错误信息入栈(仍然会先将函数和参数从栈中移除)。在将错误信息入栈之前,如果指定了错误处理函数,lua_pcall会调用错误处理函数。使用lua_pcall的最后一个参数来指定错误处理函数,0代表没有错误处理函数,也就是说最终的错误信息就是原始的错误信息。否则,那个参数应该是一个错误函数被加载的时候在栈中的索引,注意,在这种情况下,错误处理函数必须要在被调用函数和其参数入栈之前入栈,也就是必须位于待调用函数及其参数的下面。对于一般错误,lua_pcall返回错误代码LUA_ERRRUN。有两种特殊情况,会返回特殊的错误代码,因为他们从来不会调用错误处理函数。第一种情况是,内存分配错误,对于这种错误,lua_pcall总是返回LUA_ERRMEM。第二种情况是,当Lua正在运行错误处理函数时发生错误,这种情况下,再次调用错误处理函数没有意义,所以lua_pcall立即返回错误代码LUA_ERRERR。
5、看一个稍微高级的例子,我们使用C的vararg来封装对Lua函数的调用。我们的封装后的函数(call_va)接受被调用的函数明作为第一个参数,第二参数是一个描述参数和结果类型的字符串,最后是一个保存返回结果的变量指针的列表。使用这个函数,我们可以将前面的例子改写为:call_va("f", "dd>d", x, y, &z);
字符串 "dd>d" 表示函数有两个double类型的参数,一个double类型的返回结果。我们使用字母 'd' 表示double;'i' 表示integer,'s' 表示strings;'>' 作为参数和结果的分隔符。如果函数没有返回结果,'>' 是可选的。
#include <stdarg.h>void call_va (const char *func, const char *sig, ...){va_list vl;int narg, nres;/* 参数和结果的数量 */va_start(vl, sig); //此时vl指向可变形参的首地址lua_getglobal(L, func);/* 压入函数 *//* 压入参数 */narg = 0;while (*sig){/* push arguments */switch (*sig++){case 'd':/* double argument */lua_pushnumber(L, va_arg(vl, double));break;case 'i':/* int argument */lua_pushnumber(L, va_arg(vl, int));break;case 's':/* string argument */lua_pushstring(L, va_arg(vl, char *));break;case '>':goto endwhile;default:error(L, "invalid option (%c)", *(sig - 1));}narg++;luaL_checkstack(L, 1, "too many arguments"); //检查栈中空间}nres = strlen(sig);/* 期望的结果数量 */if (lua_pcall(L, narg, nres, 0) != 0)/* 完成调用 */error(L, "error running function `%s': %s",func, lua_tostring(L, -1));/* 检索结果 */nres = -nres;/* 第一个结果的栈索引 */while (*sig) {/* 遍历所有结果 */switch (*sig++){//将结果复制到vl中case 'd':/* double result */if (!lua_isnumber(L, nres))error(L, "wrong result type");*va_arg(vl, double *) = lua_tonumber(L, nres);break;case 'i':/* int result */if (!lua_isnumber(L, nres))error(L, "wrong result type");*va_arg(vl, int *) = (int)lua_tonumber(L, nres);break;case 's':/* string result */if (!lua_isstring(L, nres))error(L, "wrong result type");*va_arg(vl, const char **) = lua_tostring(L, nres);break;default:error(L, "invalid option (%c)", *(sig - 1));}nres++;}va_end(vl);}
尽管这段代码具有一般性,这个函数和前面我们的例子有相同的步骤:将函数入栈,参数入栈,调用函数,获取返回结果。大部分代码都很直观,但也有一点技巧。首先,不需要检查func是否是一个函数,lua_pcall可以捕捉这个错误。第二,可以接受任意多个参数,所以必须检查栈的空间。第三,因为函数可能返回字符串,call_va不能从栈中弹出结果,在调用者获取临时字符串的结果之后(拷贝到其他的变量中),由调用者负责弹出结果。
- lua学习25:《Lua程序设计(第2版)》第25章
- lua学习2:《Lua程序设计(第2版)》第2章
- lua学习1:《Lua程序设计(第2版)》第1章
- lua学习24:《Lua程序设计(第2版)》第24章
- lua学习26:《Lua程序设计(第2版)》第26章
- 《Lua程序设计[第二版]》第1,2章笔记
- Lua程序设计(第2版)第二章学习随笔——类型和值
- Lua程序设计(第2版)第三章学习随笔——表达式
- Lua程序设计(第2版)第四章学习随笔——语句
- Lua程序设计(第2版)第五章学习随笔——函数
- Lua程序设计(第2版)第六章学习随笔——深入函数
- Lua程序设计:第1章 开始
- Lua程序设计:第3章 表达式
- Lua程序设计:第4章 语句
- Lua程序设计:第5章 函数
- Lua程序设计:第6章 深入函数
- 《Lua程序设计[第二版]》第3,4章笔记
- 《Lua程序设计[第二版]》第5,6章笔记
- 进程的交互
- [[Journey to the Center of the Linux Kernel: Traffic Control, Shaping and QoS]]
- [置顶] Cocos2d-x使用过程中遇到的错误(VS下开发,移植到Android发布)(更新ing)
- Android versionCode与versionName
- 黑马程序员-java中IO流的操作总结1
- lua学习25:《Lua程序设计(第2版)》第25章
- Android 图表绘制
- SQL查询性能分析
- Android调用系统分享,指定某些应用来分享
- Oracle WebLogic 10.3 连接池参数配置详解
- 避免使用不当pthread_create函数造成内存泄露
- CM源代码的下载地址
- RTSP协议文档翻译
- 51单片机串口通信