《Programming in Lua 3》读书笔记(二十二)

日期:2014.8.6

PartⅣ The C API

26 Extending Your Application

使用Lua很重要的一点是用来做配置语言。配合主语言做一些功能的配置。

26.1 The Basics

有的时候程序需要配置一些功能信息,很多时候可能有许多别的方法比用lua做配置要更简单:如使用环境变量或者读取文件,读取文件涉及到文件的解析。如果使用Lua进行配置的话,相当于用lua文件替代了要读取的如csv、txt文件等。

使用Lua进行配置的时候,就需要使用Lua API去控制Lua来解析文件了,然后从Lua的全局变量中得到要配置的值。Load函数承担了加载文件的功能,函数调用luaL_loadfile从给定名字的文件中读取语句快,然后调用lua_pcall来运行语句快。假如遇到了错误(例如:配置文件中的语法错误),此时函数将错误信息push至栈中然后返回一个非零的错误代码;此时我们的程序使用lua_tostring 带参数index -1 从栈顶得到信息。

e.g.

lua_tostring(L,-1)     --从栈顶得到信息

加载完文件之后,就需要从Lua的全局变量中得到变量值了。此时调用lua_getglobal得到需要的值,参数为全局变量的名字。每一次调用都会将找到的全局变量值push值栈中,然后相应的主程序就从栈中得到相应的值。然后使用lua_is* 来确定值的类型,然后调用lua_to* 得到相应类型的值。

使用Lua做配置的好处就是,Lua帮你做了所有的语法检测处理,(配置文件中甚至可以有注释?);用户可以用Lua来做更为复杂的配置处理,如配置文件可以提示用户一些信息,或者检测某个环境变量来选择合适的值:

e.g.
--configuration file
if getenv("DISPLAY") == ":0.0" then
     width = 300;height = 300
else
     width = 200;height = 200

另外一个原因便是:现在来看很容易使用Lua给程序添加新的配置信息,这个特性使得程序更为灵活。

26.2 Table Manipulation

假设一个这样的场景:需要配置窗口的背景颜色。此时需要有三个值:RGB值。通常在C中,每个值的范围是[0,255],而Lua中,因为所有的number都是实数,所以使用的是[0,1]这个范围。

通常为每一个值声明一个变量会带来很多麻烦,如:假如程序需要多种配置,如前景色、按钮颜色等等,那这样会需要很多的变量;而且这种方法不适合用预定义颜色,或者说默认值。此时,Lua中的table就适合做这样的配置工作了:

e.g.
background = {r = 0.30,g = 0.10,b = 0}

使用table将会给脚本带来非常大的便利性;此时也可以方便的使用一些预定值了,如:

e.g.
BLUE = { r = 0, g = 0, b = 1.0}
background = BLUE

而C代码中要获得这些值,可以如下来实现:

e.g.
lua_getglobal(L,"background")
if (! lua_istable(L,-1))
     error(L,"'background' is not a table");
red = getcolorfield(L,"r");
green = getcolorfield(L,"g");
blue = getcolorfield(L,"b");

#define MAX_COLOR     255
int getcolorfield(lua_State *L,const char* key)
{
      int result;
     //key 推出,而把相应的value推进栈
    lua_pushstring(L,key);
      lua_gettable(L,-2);
     //前面两段可以用 lua_getfield(L,-1,key);来替代
     if(!lua_isnumber(L,-1))
          error(L,"invalid component in background color");
     result = (int)(lua_tonumber(L,-1)) * MAX_COLOR);
     lua_pop(L,1);
     return result;
}

26.3 Calling Lua Function

配置文件可以定义函数给程序调用,这是Lua的一大强势所在。

API调用一个函数是比较简单的:首先,将要调用的函数push至栈中;接着将要调用的参数push至栈中;然后可以使用lua_pcall 函数执行实际的函数调用;最后从栈中得到调用的结果。

lua_pcall 的第二个参数表示的是传递参数的数量和返回结果的数量;第四个参数指的是错误处理函数,0表示没有错误处理函数,其余的值表示函数在栈中的位置index,因此这些函数首先应该push至栈中的;Lua将会根据实际的函数的运算结果来调整参数和返回的数量,如果需要将会push ni或者抛弃额外的值。在将结果push之前,lua_pcall 将会从栈中移除掉函数和函数的参数。当函数返回了多个结果,第一个结果将首先被push。

如果在lua_pcall运行过程中遇到了任何的错误,lua_pcall将会返回一个错误代码;不仅如此,还会将错误信息push至stack中(并且仍然会推出函数和函数的参数)。在push错误信息之前,如果有错误处理函数,lua_pcall还是会首先执行错误处理函数的。

而普通的错误情况下,lua_pcall 返回的是 LUA_ERRUN.有两类错误将会返回不同的错误代码:第一种情况是内存配置错误,将会返回LUA_ERRMEM;第二种情况是当lua正在运行错误处理函数本身的时候,因为此时肯定不会再调用错误函数了,所以此时将会立即返回错误代码LUA_ERROR.在lua5.2中还定义了第三种情况,当finalizer(解释器?) 抛出错误的时候,此时将会返回LUA_ERRGCMM(error in a GC metamethod)。这个代码表明错误不与调用函数本身有关。

26.4 A Generic Call Function

这里,将会建立一个wrapper(包装器?)用来调用lua函数,使用了C中的vararg(可变参数?) 特性。假如称我们的包装器为call_va,参数为:1、要被调用的函数的名字;2、一个表示参数和返回值结果的字符串;3、参数列表;4、存储返回值结果的指针列表。使用API来处理所有的操作:

e.g.
call_va(L,"f","dd>d",x,y,&z);

字符串"dd>d"表示“两个类型为double的参数,一个类型为double的返回值”,这里用"d"表示"double","i"表示"integer","s"表示"string"。用">"做分隔符。而如果函数没有返回值,那么">"是可选的。

《Programming in Lua 3》读书笔记(二十二),布布扣,bubuko.com

时间: 2024-10-27 07:19:01

《Programming in Lua 3》读书笔记(二十二)的相关文章

Effective C++读书笔记之十二:复制对象时勿忘其每一个成分

Item 12:Copy all parts of an object 如果你声明自己的copying函数,意思就是告诉编译器你并不喜欢缺省显示中的某些行为.而编译器会对"你自己写出copying函数"做出一种复仇的行为:既然你拒绝它们为你写出copying函数,如果你的代码不完全,它们也不会告诉你.结论很明显:如果你为class添加一个成员变量,你必须同时修改copying函数.如果你忘记,编译器不太可能提醒你. 一下提供一种正确的模版: class Date{...}; class

Hadoop读书笔记(十二)MapReduce自定义排序

Hadoop读书笔记系列文章:http://blog.csdn.net/caicongyang/article/category/2166855 1.说明: 对给出的两列数据首先按照第一列升序排列,当第一列相同时,第二列升序排列 数据格式: 3 3 3 2 3 1 2 2 2 1 1 1 2.代码 SortApp.java package sort; import java.io.DataInput; import java.io.DataOutput; import java.io.IOExc

《Android源码设计模式解析与实战》读书笔记(十二)

第十二章.观察者模式 观察者模式是一个使用率非常高的模式,它最常用在GUI系统.订阅–发布系统.因为这个模式的一个重要作用就是解耦,将被观察者和观察者解耦,使得它们之间的依赖性更小,甚至做到毫无依赖.比如安卓的开源项目EventBus.Otto.AndroidEventBus等事件总线类的和RxJava响应式编程其核心都是使用观察者模式. 1.定义 观察者模式是一种行为类模式,它定义对象间一种一对多的依赖关系,使得每当一个对象改变状态,则所有依赖于它的对象都会得到通知并被自动更新. 2.使用场景

《Linux内核设计与实现》读书笔记(十二)- 内存管理

内核的内存使用不像用户空间那样随意,内核的内存出现错误时也只有靠自己来解决(用户空间的内存错误可以抛给内核来解决). 所有内核的内存管理必须要简洁而且高效. 主要内容: 内存的管理单元 获取内存的方法 获取高端内存 内核内存的分配方式 总结 1. 内存的管理单元 内存最基本的管理单元是页,同时按照内存地址的大小,大致分为3个区. 1.1 页 页的大小与体系结构有关,在 x86 结构中一般是 4KB或者8KB. 可以通过 getconf 命令来查看系统的page的大小: [[email prote

《APUE》读书笔记第十二章-线程控制

本章中,主要是介绍控制线程行为方面的内容,同时介绍了在同一进程中的多个线程之间如何保持数据的私有性以及基于进程的系统调用如何与线程进行交互. 一.线程属性 我们在创建线程的时候可以通过修改pthread_attr_t结构的值来修改线程的属性,将这些属性与创建的线程联系起来.调用pthread_attr_init以后,pthread_attr_t结构所包含的内容就是操作系统实现支持的线程所有属性. #include <pthread.h> int pthread_attr_init(pthrea

java编程思想读书笔记 第十二章 通过异常处理错误(下)

1.异常的限制 当覆盖方法的时候,只能抛出在基类方法的异常说明里列出的那些异常.这意味着,当基类使用的代码应用到其派生类对象的时候,一样能够工资,异常也不例外. 下面的例子是在编译时施加在异常上面的限制: public class BaseBallException extends Exception {} public class Foul extends BaseBallException{} public class Strike extends BaseBallException{} p

C primer plus 读书笔记第十二章

C的强大功能之一在于它允许我们控制程序的细节.C的内存管理系统正是这种控制能力的例子.它通过让我们决定哪些函数知道哪些变量以及一个变量在程序中存在多长时间来实现这些控制. 1.存储类及其说明符 主要的定义:作用域.链接以及存储时间.其他编程语言也有类似的概念.C语言通过这三个概念定义了5中存储类.其说明符分别为auto.register.static.extern和typedef. 2.存储类和函数 函数也分为外部的和静态的.关键字是extern和static.默认情况下是外部的. 3.mall

《android开发艺术探索》读书笔记(十五)--Android性能优化

接上篇<android开发艺术探索>读书笔记(十四)--JNI和NDK编程 No1: 如果<include>制定了这个id属性,同时被包含的布局文件的根元素也制定了id属性,那么以<include>指定的id属性为准 No2: 绘制优化 1)onDraw中不要创建新的局部对象 2)onDraw方法中不要做耗时的任务 No3: 内存泄露优化 场景一:静态变量导致的内存泄露: 如果静态变量持有了一个Activity,会导致Activity无法及时释放. 解决办法:1使用Ap

42. 蛤蟆的数据结构笔记之四十二图的遍历之广度优先

42. 蛤蟆的数据结构笔记之四十二图的遍历之广度优先 本篇名言:"生活真象这杯浓酒 ,不经三番五次的提炼呵 , 就不会这样一来可口 ! -- 郭小川" 继续看下广度优先的遍历,上篇我们看了深度遍历是每次一个节点的链表是走到底的. 欢迎转载,转载请标明出处:http://write.blog.csdn.net/postedit/47029275 1.  原理 首先,从图的某个顶点v0出发,访问了v0之后,依次访问与v0相邻的未被访问的顶点,然后分别从这些顶点出发,广度优先遍历,直至所有的