快速掌握Lua 5.3 —— 扩展你的程序
Posted VermillionTear
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了快速掌握Lua 5.3 —— 扩展你的程序相关的知识,希望对你有一定的参考价值。
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
以上是关于快速掌握Lua 5.3 —— 扩展你的程序的主要内容,如果未能解决你的问题,请参考以下文章