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

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

A:

“config.lua”文件里:

-- window size
width = 200
height = 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 -Wall
prompt> ./a.out
width = 200, height = 300

看完这个样例是否会认为使用Lua作为配置文件语言有些不值?只为了获取两个变量的值,却写了这么一大段代码。

可是使用Lua确实有其优势。

首先Lua承担了配置文件格式检查的工作,假设你自己写解析程序。这一段代码不会少;其次。你能够在配置文件里写凝视,相同假设你自己写解析程序,也须要考虑跳过凝视的问题;最后。配置文件里能够做很多其它的灵活配置。比方,

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

Q:怎样在C中使用Lua的”table”?

A:我们在上一个样例的基础上继续扩展程序。

“config.lua”文件里:

-- window size.
width = 200
height = 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 -Wall
prompt> ./a.out
width = 200, height = 300
red = 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"元方法时发生的错误(这个错误和被调用的函数无关。)。

*/
时间: 2024-11-07 16:23:50

高速掌握Lua 5.3 —— 扩展你的程序 (1)的相关文章

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

Q:如何在C中将Lua作为配置文件语言使用? A: "config.lua"文件中: -- window size width = 200 height = 300 "main.c"文件中: #include <stdarg.h> #include <stdio.h> #include <stdlib.h> #include <lua.h> #include <lauxlib.h> #include <

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

Q:如何在C中调用Lua的函数? A: 1.将函数名入栈. 2.将参数按照形参的顺序依次入栈. 3.调用函数.此过程会将函数的参数出栈,调用完成后将函数的返回值入栈. 4.获取函数的返回值. "config.lua"文件中: function f(x, y) return (x ^ 2 * math.sin(y)) / (1 - x) end "main.c"文件中: #include <stdarg.h> #include <stdio.h>

对lua的简单扩展,使它能快速关联C++的数据。

很早的时候,我纠结过一件事,假如,我在C++公开给脚本的接口中,要使用C++里的某一个对象,并且,我的程序中有很多个不同的lua虚拟机,每一个虚拟机要关联一个C++对象,并且这是多线程的,那么这时候应该如何快速的利用lua_State指针来定位到对象指针呢? 以前我没有能力读懂lua的源码,也可以说不知道关键部分如何操作,我当时的做法,是利用临界区和std::map来解决问题的,很明显这个方式的效率很低很低. 现在有能力读lua源码了,当然有更有效的解决办法了,因为在我们利用lua的过程中,lu

Lua_第24章 扩展你的程序

 第24章扩展你的程序 作为配置语言是 LUA的一个重要应用.在这个章节里,我们举例说明如何用 LUA 设 置一个程序.让我们用一个简单的例子开始然后展开到更复杂的应用中. 首先,让我们想象一下一个简单的配置情节:你的 C程序(程序名为 PP)有一个 窗口界面并且可以让用户指定窗口的初始大小.显然,类似这样简单的应用,有多种解决方法比使用LUA更简单,比如环境变量或者存有变量值的文件.但,即使是用一个 简单的文本文件,你也不知道如何去解析.所以,最后决定采用一个 LUA 配置文件(这就是 LUA

扩展Web应用程序(上)

1.8 扩展Web应用程序 1.8.1 性能 对于开发者而言,他们主要关注程序的响应时间和延展性. 响应时间也是衡量一个应用程序的效率的指标之一.如果一个请求到响应时间超出了良好的感知范围,我们就可以认为这个程序的性能很糟糕.一般而言,最好能够在一两秒内得到想要的页面. 延展性,说了是添加了更多的硬件设备,应用程序能够线性地承受更多的请求.添加硬件的方式有两种: l  向上扩展(垂直扩展)--增加CUPs数量或者在一个单一的盒子(指的是一台机器)里添加更快的CUP l  横向扩展(水平扩展)--

反射 学习笔记(五)构建可扩展的应用程序

构建可扩展的应用程序如果可扩展的应用程序预编程为查询指定的接口,则它可以在运行时确定类型是否可以被激活.一旦验证测试通过,类型便可以支持额外的接口,为它们的功能提供多种结构. 示例: 目标:学习简单的接口编程过程,使用反射.特性将外部程序集在运行时加载到内存中并使用 第一步:新建解决方案 两个项目文件 MExtendableApp是主项目,CommonSnappableTypes是扩展项目(提供接口标准.公司信息描述.功能信息描述) 接下来,我们来写主项目 主界面非常简单 对Form进行初始化

[转] 扩展微信小程序框架功能

通过第三方 JavaScript 库,扩展微信小程序框架功能. 扩展微信小程序框架功能(1)——Promise ES6 对 Promise 有了原生的支持,但微信开发者工具更新版本(0.11.112200)后, 移除了开发者工具对 ES6 中Promise 特性原生的支持, 需要引入第三方的 Promise 库. 扩展微信小程序框架功能(2)——Generator Generator函数是ES6提供的一种异步编程解决方案,语法行为与传统函数完全不同. 扩展微信小程序框架功能(3)——函数功能增强

正确lua简单的扩展,可以加速相关C++数据。

很早的时候,我有一件事纠结.如果,我在这里C++打开界面脚本.使用C++其中一个目标,和.我的程序有很多不同的lua虚拟机.每个虚拟机与一个关联C++对象,它是多线程,那么如何快速应利用这个好时机lua_State针来定位到对象指针呢? 曾经我没有能力读懂lua的源代码,也能够说不知道关键部分怎样操作,我当时的做法.是利用临界区和std::map来解决这个问题的.非常明显这个方式的效率非常低非常低. 如今有能力读lua源代码了.当然有更有效的解决的方法了.由于在我们利用lua的过程中.lua_S

CommandExtra.lua --游戏命令扩展

1 --[[作者信息: 2 Command Extra (游戏命令扩展) 3 作者QQ:247321453 4 作者Email:[email protected] 5 修改日期:2014-3-12 6 功能:添加额外的命令.GM命令 7 ]]-- 8 print(">>Script: Command Extra.") 9 10 --[[ 11 .wmsg 内容 GM发送世界消息 12 .be 查看机器人装备 13 .npcbot equips 查看机器人装备 14 .rh