快速掌握Lua 5.3 —— 扩展你的程序 (1)

来源:互联网 发布:英语口语书籍 知乎 编辑:程序博客网 时间:2024/05/21 18:30

Q:如何在C中将Lua作为配置文件语言使用?

A:
“config.lua”文件中:

-- window sizewidth = 200height = 300

“main.c”文件中:

#include <stdarg.h>#include <stdio.h>#include <stdlib.h>#include <lua.h>#include <lauxlib.h>#include <lualib.h>void error(lua_State *L, const char *fmt, ...){       // 逐一取出参数并打印到标准错误输出。    va_list argp;    va_start(argp, fmt);    vfprintf(stderr, fmt, argp);    va_end(argp);    lua_close(L);    // 关闭Lua状态机。    exit(EXIT_FAILURE);    // 以失败退出。}void load(char *filename, int *width, int *height){       lua_State *L = luaL_newstate();    luaL_openlibs(L);    /* luaL_loadfile():      * 读取配置文件,并将其中的内容编译为"chunk"放入虚拟栈中。     * lua_pcall():      * 使用保护模式调用虚拟栈中的函数。被调用的函数应该遵循以下协议:     * 被调用的函数应该被首先入栈,接着把参数按正序入栈(第一个参数首先入栈)。     * 第二个参数代表参数个数;第三个参数代表返回值个数;     * 第四个参数代表是否使用自定义的错误处理函数。     * 当函数调用完成之后,所有的参数以及函数本身都会出栈,而函数的返回值则被入栈。     */    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环境下名为"width"的全局变量的值入栈。    lua_getglobal(L, "height");   // 将Lua环境下名为"height"的全局变量的值入栈。    // 判断全局变量的值是否为数值。    if (!lua_isnumber(L, -2))    // 上面"width"先被入栈,所以-2是"width"。        error(L, "`width' should be a number\n");    if (!lua_isnumber(L, -1))    // -1是"height"。        error(L, "`height' should be a number\n");    // 将全局变量的值转换为浮点数。    *width = (int)lua_tonumber(L, -2);    *height = (int)lua_tonumber(L, -1);    lua_close(L);}int main(void){    int width = 0, height = 0;    load("config.lua", &width, &height);    printf("width = %d, height = %d\n", width, height);    return 0;}
prompt> gcc main.c -llua -ldl -lm -Wallprompt> ./a.outwidth = 200, height = 300

看完这个例子是否会觉得使用Lua作为配置文件语言有些不值?仅仅为了获取两个变量的值,却写了这么一大段代码。
但是使用Lua确实有其优势。首先Lua承担了配置文件格式检查的工作,如果你自己写解析程序,这一段代码不会少;其次,你可以在配置文件中写注释,同样如果你自己写解析程序,也需要考虑跳过注释的问题;最后,配置文件中可以做更多的灵活配置,比如,

-- 根据环境变量,灵活的定义窗口大小。if os.getenv("DISPLAY") == "small" then    width = 100    height = 100else    width = 500    height = 500end

Q:如何在C中使用Lua的”table”?

A:我们在上一个例子的基础上继续扩展程序。
“config.lua”文件中:

-- window size.width = 200height = 300-- window's background color.-- 配置文件中可以自己定义颜色。ORANGE = {r = 255, g = 128, b = 0}--background = ORANGE--background = {r = 128, g = 0, b = 255}background = "BLUE"    -- 也可以直接使用C中定义的颜色。

“main.c”文件中:

#include <stdarg.h>#include <stdio.h>#include <stdlib.h>#include <string.h>#include <lua.h>#include <lauxlib.h>#include <lualib.h>#define MAX_COLOR       255    // 颜色分量的最大值。struct ColorTable {    char *name;    unsigned char red, green, blue;} colortable[] = {    // C中定义的颜色。    {"WHITE", MAX_COLOR, MAX_COLOR, MAX_COLOR},    {"RED", MAX_COLOR, 0, 0},    {"GREEN", 0, MAX_COLOR, 0},    {"BLUE", 0, 0, MAX_COLOR},    {"BLACK", 0, 0, 0},    {NULL, 0, 0, 0}    // sentinel};void error(lua_State *L, const char *fmt, ...){    va_list argp;    va_start(argp, fmt);    vfprintf(stderr, fmt, argp);    va_end(argp);    lua_close(L);    exit(EXIT_FAILURE);    return ;}void setfield(lua_State *L, const char *index, int value){    lua_pushstring(L, index);    //"key"入栈。    lua_pushinteger(L, value);    //"value"入栈。    /* 将指定的索引位置(-3)作为"table",将索引位置(-1)作为"value",将索引位置(-2)作为"key",     * 做相当于"table[key] = value"的工作。之后将"key""value"出栈。     */    lua_settable(L, -3);    return ;}// 获取"table"中元素"key"的值(此函数假设"table"在栈顶)。int getfield(lua_State *L, const char *key){    int result = 0;    lua_pushstring(L, key);    //"key"入栈。    /* 从给定的索引位置(-2)获得"table",从栈顶位置获得"key",     * 弹出"key",将"table[key]"的结果入栈。     */    lua_gettable(L, -2);    if (!lua_isinteger(L, -1))        error(L, "invalid component in background color");    result = lua_tointeger(L, -1);    // "table[key]"的结果即是颜色分量值。    lua_pop(L, 1);    // 弹出颜色分量值,保证栈的状态与调用该函数时一样。    return result;}void setcolor(lua_State *L, struct ColorTable *ct){    lua_newtable(L);    // 创建一个"table"并将其入栈。    setfield(L, "r", ct->red);    /* table.r = ct->r */    setfield(L, "g", ct->green);    /* table.g = ct->g */    setfield(L, "b", ct->blue);    /* table.b = ct->b */    // 将栈顶的元素出栈(实际为上面创建的"table"),并使用名为"name"的全局变量存储。    lua_setglobal(L, ct->name)    return ;}void load(lua_State *L, char *filename, int *width, int *height, int *r, int *g, int *b){    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");    lua_getglobal(L, "background");   // 将Lua环境下名为"background"的全局变量的值入栈。    if(!lua_isnumber(L, -3))    {        error(L, "`width' should be a number\n");    }    *width = (int)lua_tonumber(L, -3);    if(!lua_isnumber(L, -2))    {        error(L, "`height' should be a number\n");    }    *height = (int)lua_tonumber(L, -2);    // 如果"background"值是个字符串,那么说明使用C中定义的颜色。    if(lua_isstring(L, -1))    {        const char *colorname = lua_tostring(L, -1);        int i = 0;        // 寻找使用的哪一种颜色。        while((colortable[i].name != NULL)               && (strcmp(colorname, colortable[i].name) != 0))        {            ++i;        }        if(colortable[i].name == NULL)        {            error(L, "invalid color name (%s)", colorname);        }        else        {            // 获取颜色的分量。            *r = colortable[i].red;            *g = colortable[i].green;            *b = colortable[i].blue;        }    }    // 如果"background"值是个字符串,那么说明使用Lua中定义的颜色。    else if(lua_istable(L, -1))    {        // 获取颜色的分量。        *r = getfield(L, "r");        *g = getfield(L, "g");        *b = getfield(L, "b");    }    else    {        error(L, "invalid value for `background'");    }    return ;}int main(void){    int i = 0;    int width = 0, height = 0;    int r = 0, g = 0, b = 0;    lua_State *L = luaL_newstate();    luaL_openlibs(L);    while(colortable[i].name != NULL)    {        setcolor(L, &colortable[i++]);    // 将C中定义的颜色逐一初始化到Lua的"table"中。    }    load(L, "config.lua", &width, &height, &r, &g, &b);    lua_close(L);    printf("width = %d, height = %d\n"            "red = %d, green = %d, blue = %d\n",            width, height, r, g, b);    return 0;}
prompt> gcc main.c -llua -ldl -lm -Wallprompt> ./a.outwidth = 200, height = 300red = 0, green = 0, blue = 255

附加:

1、

/* int lua_pcall(lua_State *L, int nargs, int nresults, int msgh) * 以保护模式调用具有"nargs"个参数,"nresults"个返回值得函数。函数在第一个参数的前一个位置。 * 保护模式指的是当调用出错时不会报错,而是返回一个错误码同时将错误信息入栈。 * 当调用成功时,函数返回0。将函数名以及参数出栈,之后将函数的返回值入栈。 * 无论函数返回多少个返回值,Lua会调整为你需要的数量,忽略多余的或者将不够的补为"nil"。 * 当调用出错时,函数返回非0值。将函数名以及参数出栈, * 以错误信息作为参数,执行虚拟栈中索引"msgh"处的出错处理函数, * 将出错处理函数的返回值作为"lua_pcall"的返回值入栈。 * "msgh"0代表没有错误处理函数,错误处理函数必须要在被调用函数和其参数入栈之前入栈。 * 典型的用法中,错误处理函数被用来给错误消息加上更多的调试信息,比如栈跟踪信息。 * 这些信息在"lua_pcall"返回后,由于栈已经展开,所以收集不到了。 * lua_pcall 函数会返回下列常数(定义在"lua.h"内)中的一个:   LUA_OK (0): 成功。   LUA_ERRRUN: 运行时错误(一般错误)。   LUA_ERRMEM: 内存分配错误(此种情况,Lua不会调用错误处理函数)。   LUA_ERRERR: 在运行错误处理函数时发生的错误(此种情况,Lua不会再次调用错误处理函数)。   LUA_ERRGCMM: 在运行"__gc"元方法时发生的错误(这个错误和被调用的函数无关。)。 */
0 0
原创粉丝点击