第25章 扩展你的程序 |
作为配置语言是LUA的一个重要应用。在这个章节里,我们举例说明如何用LUA设置一个程序。让我们用一个简单的例子开始然后展开到更复杂的应用中。
首先,让我们想象一下一个简单的配置情节:你的C程序(程序名为PP)有一个窗口界面并且可以让用户指定窗口的初始大小。显然,类似这样简单的应用,有多种解决方法比使用LUA更简单,比如环境变量或者存有变量值的文件。但,即使是用一个简单的文本文件,你也不知道如何去解析。所以,最后决定采用一个LUA配置文件(这就是LUA程序中的纯文本文件)。在这种简单的文本形式中通常包含类似如下的信息行:
-- configuration file for program `pp'
-- define window size
width = 200
height = 300
现在,你得调用LUA API函数去解析这个文件,取得width和height这两个全局变量的值。下面这个取值函数就起这样的作用:
#include <lua.h>
#include <lauxlib.h>
#include <lualib.h>
void load (char *filename, int *width, int *height) {
lua_State *L = lua_open();
luaopen_base(L);
luaopen_io(L);
luaopen_string(L);
luaopen_math(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");
lua_getglobal(L, "height");
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);
}
首先,程序打开LUA包并加载了标准函数库(虽然这是可选的,但通常包含这些库是比较好的编程思想)。然后程序使用luaL_loadfile方法根据参数filename加载此文件中的信息块并调用lua_pcall函数运行,这些函数运行时若发生错误(例如配置文件中有语法错误),将返回非零的错误代码并将此错误信息压入栈中。通常,我们用带参数index值为-1的lua_tostring函数取得栈顶元素(error函数我们已经在24.1章节中定义)。
解析完取得的信息块后,程序会取得全局变量值。为此,程序调用了两次lua_getglobal函数,其中一参数为变量名称。每调用一次就把相应的变量值压入栈顶,所以变量width的index值是-2而变量height的index值是-1(在栈顶)。(因为先前的栈是空的,需要从栈底重新索引,1表示第一个元素2表示第二个元素。由于从栈顶索引,不管栈是否为空,你的代码也能运行)。接着,程序用lua_isnumber函数判断每个值是否为数字。lua_tonumber函数将得到的数值转换成double类型并用(int)强制转换成整型。最后,关闭数据流并返回值。
Lua是否值得一用?正如我前面提到的,在这个简单的例子中,相比较于lua用一个只包含有两个数字的文件会更简单。即使如此,使用lua也带来了一些优势。首先,它为你处理所有的语法细节(包括错误);你的配置文件甚至可以包含注释!其次,用可以用lua做更多复杂的配置。例如,脚本可以向用户提示相关信息,或者也可以查询环境变量以选择合适的大小:
-- configuration file for program 'pp'
if getenv("DISPLAY") == ":0.0" then
width = 300; height = 300
else
width = 200; height = 200
end
在这样简单的配置情节中,很难预料用户想要什么;不过只要脚本定义了这两个变量,你的C程序无需改变就可运行。
最后一个使用lua的理由:在你的程序中很容易的加入新的配置单元。方便的属性添加使程序更具有扩展性。