Lua 学习 chapter28

扩展应用

Posted by Jow on August 1, 2019

目录

  1. 基础知识
  2. 操作表
  3. 一些简便方法
  4. 调用lua函数

回顾复习加巩固,自己应该认真地学习复习和巩固,并不是只有学习新知识是自己最重要的,而是复习和巩固才是自己最重要的,所以自己每天必须抽出一定的时间对自己的学习进行复习和巩固。

基础知识

lua可以作为配置文件来供c语言使用,例如我们来定义一个窗口的大小。

1
2
width = 200
height = 300

然后我们使用LuaAPI来指挥lua语言解析该文件,并获取width和height的值。

  • luaL_loadfile(lua_State *L, fname):加载文件并且编译,返回一个执行这个文件的函数,放到栈顶
  • lua_pcall(L,0,0,0):调用栈中的函数,第二个参数为函数参数的个数,第三个参数为返回值的个数,第四个参数为错误处理函数。在压入结果之前,pcall函数会将参数和函数出栈,将返回值从第一个按照顺序压入栈中。至于最后一个参数为零,则lua会把错误信息作为string压倒栈中,如果是函数,第四个参数为它在栈中的索引,一般这个错误处理函数应该在被压入栈且位于待调用函数之下。
  • lua_getglobal(lua_State *L, const char *key):根据key值获取全局变量,并放到栈顶。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
#include<stdio.h>
#include<string.h>
#include<stdarg.h>
#include<stdlib.h>
extern "C" {
#include <lua.h>
#include <lauxlib.h>
#include <lualib.h>
}

int getglobint(lua_State* L, const char* var);
void load(lua_State* L, const char* fname, int* w, int* h);
void error(lua_State* L, const char* fmt, ...);

int main(void)
{
	lua_State* L = luaL_newstate();//打开lua
	luaL_openlibs(L);//打开标准库

	int width, height;
	load(L, "config", &width, &height);
	printf("%d %d", width, height);
	return 0;
}

int getglobint(lua_State* L, const char* var)
{
	int isnum, result;
	lua_getglobal(L, var);//将相应的全局变量的值压入到栈中。
	result = (int)lua_tointegerx(L, -1, &isnum);
	if (!isnum)
	{
		error(L, "'%s' should be a number \n", var);
	}
	lua_pop(L, 1);
	return result;
}

void load(lua_State* L, const char* fname, int* w, int* h)
{
	if (luaL_loadfile(L,fname) || lua_pcall(L,0,0,0))//loadfile加载代码,pcall运行编译后的代码段
	{
		error(L, "cannot  run config. file %s", lua_tostring(L,-1));
	}
	*w = getglobint(L, "width");
	*h = getglobint(L, "height");
}

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);
}


}

操作表

现在我们要为每个窗口配置一种背景色,假设最终的颜色格式是由三个数字分量组成的RGB颜色。

1
2
3
4
width = 200
height = 300
BLUE = {red = 0,green = 0,blue = 1.0}
background = BLUE

我们可以对表进行操作,具体用到的函数:

  • lua_pushstring(L,key)
  • lua_gettable(L,-2) //-2是表的索引,它会pop出key,然后根据key取值,并把值放到栈中
  • lua_getfield(L,-1,key)//相当于上面的两步,这个函数会返回结果的类型
  • lua_pushnumber(L,val)
  • lua_pushstring(L,key)
  • lua_settable(L,-3)// -3是表的索引,它会pop出val,key,然后给表设置值。
  • lua_setfield(L,-2,key)// 相当于上面的两步,它会pop出val然后根据key设置值。
  • lua_createtable(L,int narr,int nrec):narr,表示连续的值个数,nrec表示hash的值个数。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
#include<stdio.h>
#include<string.h>
#include<stdarg.h>
#include<stdlib.h>
extern "C" {
#include <lua.h>
#include <lauxlib.h>
#include <lualib.h>
}

const int MAX_COLOR = 255;

struct ColorTable
{
	const char* name;
	unsigned char red, green, blue;
}colortable[] = {
	{"WHITE", MAX_COLOR, MAX_COLOR, MAX_COLOR},
	{"RED", MAX_COLOR, 0, 0},
	{"GREEN", 0, MAX_COLOR, 0},
	{"BLUE", 0, 0, MAX_COLOR},
	{NULL, 0, 0, 0}
};

int getglobint(lua_State* L, const char* var);
void load(lua_State* L, const char* fname, int* w, int* h);
void error(lua_State* L, const char* fmt, ...);
void getColor(lua_State* L, const char* what, int* r, int* g, int* b);
int getcolorfield(lua_State* L, const char* key);
void setcolor(lua_State* L,struct ColorTable *ct);
void setcolorfield(lua_State* L, const char* index, int value);


int main(void)
{
	lua_State* L = luaL_newstate();//打开lua
	luaL_openlibs(L);//打开标准库

	int width, height, red, green, blue;
	load(L, "config", &width, &height);
	printf("%d %d \n", width, height);
	getColor(L, "background", &red, &green, &blue);
	printf("%d\t%d\t%d\n", red, green, blue);
	int i = 0;
	while (colortable[i].name != NULL)
	{
		setcolor(L, &colortable[i++]);
	}
	return 0;
}

int getglobint(lua_State* L, const char* var)
{
	int isnum, result;
	lua_getglobal(L, var);
	result = (int)lua_tointegerx(L, -1, &isnum);
	if (!isnum)
	{
		error(L, "'%s' should be a number \n", var);
	}
	lua_pop(L, 1);
	return result;
}

void load(lua_State* L, const char* fname, int* w, int* h)
{
	if (luaL_loadfile(L,fname) || lua_pcall(L,0,0,0))
	{
		error(L, "cannot  run config. file %s", lua_tostring(L,-1));
	}
	*w = getglobint(L, "width");
	*h = getglobint(L, "height");
}

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);
}

void getColor(lua_State* L, const char* what, int* r, int* g, int* b)
{
	lua_getglobal(L, what);
	if (!lua_istable(L,-1))
	{
		error(L, "'%s' is not a table", what);
	}

	*r = getcolorfield(L, "red");
	*g = getcolorfield(L, "green");
	*b = getcolorfield(L, "blue");
}

int getcolorfield(lua_State* L, const char* key)
{
	int result, isnum;
	lua_getfield(L, -1, key);
	result = (int)(lua_tonumberx(L, -1, &isnum) * MAX_COLOR);
	if (!isnum)
	{
		error(L, "invalid component '%s' in color", key);
	}
	lua_pop(L, 1);
	return result;
}

void setcolor(lua_State* L, ColorTable* ct)
{
	lua_newtable(L);
	setcolorfield(L, "red", ct->red);
	setcolorfield(L, "green", ct->green);
	setcolorfield(L, "blue", ct->blue);
	lua_setglobal(L, ct->name);
}



void setcolorfield(lua_State* L, const char* index, int value)
{
	lua_pushstring(L, index);
	lua_pushnumber(L, double(value) / MAX_COLOR);
	lua_settable(L, -3);
	//lua_setfield(L, -2, index); lua_setfield是将pushstring和settable结合起来的
}

调用lua函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
//一个通用的调用函数
//这里vl其实就是一个指针,指向了参数的地址。
//
//va_start()所做的就是让vl指向函数的最后一个确定的参数(声明程序中是sig)的下一个参数的地址。
//
//va_arg()所做的就是根据vl指向的地址,和第二个参数所确定的类型,将这个参数的中的数据提取出来,作为返回值,同时让ap指向下一个参数。
//
//va_end()所做的就是让vl这个指针指向0。
void call_va(lua_State* L, const char* func, const char* sig, ...) {
	va_list vl;
	int narg, nres; //参数和结果的个数

	va_start(vl, sig);
	lua_getglobal(L, func);

	//压入参数并计数
	for (narg = 0; *sig; narg++)
	{
		luaL_checkstack(L, 1, "too many argument");
		switch (*sig++)
		{
		case 'd':
			lua_pushnumber(L, va_arg(vl, double));
			break;
		case 'i':
			lua_pushinteger(L, va_arg(vl, int));
			break;
		case 's':
			lua_pushstring(L, va_arg(vl, char *));
			break;
		case '>':
			goto endargs;
		default:
			error(L,"invalid option(%c)", *(sig - 1));
		}
	}
	endargs:

	nres = strlen(sig);
	if (lua_pcall(L,narg,nres,0) != 0 )
	{
		error(L, "error calling '%s' : %s", func, lua_tostring(L, -1));
	}

	//获取结果
	nres = -nres;
	while (*sig)
	{
		switch (*sig++) {
			case 'd': {
				int isnum;
				double n = lua_tonumberx(L, nres, &isnum);
				if (!isnum)
				{
					error(L, "wrong result type");
				}
				*va_arg(vl, double*) = n;
				break;
			}
			case 'i': {
				int isnum;
				double n = lua_tointegerx(L, nres, &isnum);
				if (!isnum)
				{
					error(L, "wrong result type");
				}
				*va_arg(vl, int*) = n;
				break;
			}
			case 's': {
				const char *s = lua_tostring(L, nres);
				if (s == NULL)
				{
					error(L, "wrong result type");
				}
				*va_arg(vl, const char **) = s;
				break;
			}
			default:
				error(L, "invalid option (%c)", *(sig - 1));
		}
		nres++;
	}

	va_end(vl);
}
void loadfile(lua_State* L, const char* fname)
{
	if (luaL_loadfile(L, fname) || lua_pcall(L, 0, 0, 0))
	{
		error(L, "cannot  run config. file %s", lua_tostring(L, -1));
	}
}
//在main函数里面调用
loadfile(L, "test.lua");
int sum;
call_va(L, "f", "ii>i", 1, 2, &sum);
printf("sum is %d", sum);