c和lua交互

要理解Lua和C++交互,首先要理解Lua堆栈。

简单来说,Lua和C/C++语言通信的主要方法是一个无处不在的虚拟栈。栈的特点是先进后出。

在Lua中,Lua堆栈就是一个struct,堆栈索引的方式可是是正数也可以是负数,区别是:正数索引1永远表示栈底,负数索引-1永远表示栈顶。如图:

栈的数据结构是TValue *stack,TValue代表lua里面的值类型.欲知是如何交互的,详细阅读下面的代码和运行结果后相信你会对lua和c/c++交互的了解肯定是没问题的,静下心来,不懂的都会变成你自己的,曾听说过:”把别人的东西嚼碎的东西还原再把他嚼碎了才是真正掌握了~
,所以,let’s GO!

test.lua:

1
2
3
4
5
6
7
str="hello,I am a lua value"
tbl = { name = "test", num = 10 }
function echo(name)
return name
end

main.c:

capi参考:http://cloudwu.github.io/lua53doc/manual.html

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
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <lua.h>
#include <lauxlib.h>
#include <lualib.h>
void stackTest(lua_State* L){
char str[] = "first one";
char *ud = (char*)malloc(20);
strcpy(ud,"user data");
lua_pushstring(L, str);
//lua_pushvalue(L, -1);
lua_pushnumber(L, 20);
lua_pushlightuserdata(L, ud);
// 从栈底取值
const char *res = lua_tostring(L, 1);
printf("addr: %p %p \n", res, str); // pushstring是拷贝的一个副本
if (strncmp(res, str, strlen(str))) {
printf("i need %s,but %s\n", str, res);
lua_pop(L, 3);
return;
}
// 取栈底第二个
int num = lua_tonumber(L, 2);
if (num != 20){
printf("i need %d,but %d\n", 20, num);
lua_pop(L, 3);
return;
}
// 从栈顶取值
char*ptr = lua_touserdata(L, -1); // 把c中的一个指针针转到lua中例用
if (ptr != ud){
printf("should be same ptr : %p ud %p\n ", ptr, ud);
lua_pop(L, 3);
return;
}
printf("位置1的值为 : %s\t 位置2的值为 : %d\n", res, num);
printf("栈元素的个数 : %d\n", lua_gettop(L));
//清空栈
lua_pop(L, 3);
printf("弹出栈后的元素个数 : %d\n", lua_gettop(L));
printf("\n\n------------------------------------------------\n\n");
}
void luaTest(lua_State* L){
int res;
//加载文件
res = luaL_loadfile(L, "test.lua");
if (res){
printf("load file failed\n");
return;
}
//运行lua脚本
res = lua_pcall(L, 0, 0, 0);
if (res){
printf("test.lua语法错误");
return;
}
lua_getglobal(L, "str"); // 得到test.lua里的全局变量str并push到栈上
const char *str = lua_tostring(L, -1);
printf("str : %s\t 栈元素个数 : %d\n", str, lua_gettop(L));
lua_getglobal(L, "tbl");
lua_getfield(L, -1, "name"); // 得到tbl表name的值
str = lua_tostring(L, -1);
printf("lua tbl.name : %s\t 栈元素个数 : %d\n", str, lua_gettop(L));
lua_getglobal(L, "echo");
lua_pushstring(L, "hello,lua ~");
res = lua_pcall(L, 1, 1, 0); // call后会把push进栈的全部pop出来
if (res){
const char *errMsg = lua_tostring(L, -1);
printf("errMsg : %s\t 栈元素个数 : %d\n", errMsg, lua_gettop(L));
return;
}
int elements = lua_gettop(L);
if(lua_isstring(L, -1)){
str = lua_tostring(L, -1);
printf("add返回值 : %s\t 栈元素个数 : %d\n", str, elements);
}
lua_pop(L, elements);
printf("\n\n------------------------------------------------\n\n");
//至此,栈中的情况是:
//=================== 栈顶 ===================
// 索引 类型 值
// 4 string: hello,lua ~
// 3 string: test
// 2 table: tbl
// 1 string: hello,I am a lua value
//=================== 栈底 ===================
}
//rawset rawget不会触发表的元方法
void save(lua_State* L, const char* f, const char* con){
lua_pushstring(L, f);
lua_pushvalue(L, -1); //file1的副本push栈
lua_rawget(L, LUA_REGISTRYINDEX); // 得到lua全局注册表,拿到栈顶的值然后在表里面取值,如:t["file1"],pop现有栈,再把t["file1"]push上去
printf("num is %d\n", lua_gettop(L));
const char *p = lua_tostring(L, -1);
if (NULL == p){//not exist
lua_pop(L,1);
lua_pushstring(L, con);
lua_rawset(L, LUA_REGISTRYINDEX);//以栈顶的值为value,栈顶下一个值为key设置表,栈顶的两个值都会pop
printf("文件名%s添加成功\n", f);
}else{
printf("文件名已经存在:%s\n", p);
lua_pop(L, 2);
}
printf("num is %d\n",lua_gettop(L));
}
void load(lua_State* L,char *name){
lua_pushstring(L, name);
lua_rawget(L, LUA_REGISTRYINDEX);
const char *p = lua_tostring(L, -1);
if (NULL != p){
printf("%s的内容是%s\n", name, p);
}else{
printf("%s的内容不存在\n", name);
}
lua_pop(L, 1);// 把结果pop出来
}
// 在lua注册表上保存一些东西(skynet的加载lua chunk就是这样实现的)
void luaGlobalTbl(lua_State* L){
save(L, "file1", "I am file 1");
save(L, "file2", "I am file 2");
save(L, "file2", "I am file 2");
printf("\n\n------------------------------------------------\n\n");
load(L, "file2");
load(L, "file1");
printf("\n\n");
}
int main (void) {
// 创建Lua状态
lua_State *L = luaL_newstate();
if (NULL == L){
return 0;
}
stackTest(L);
luaTest(L);
luaGlobalTbl(L);
return 0;
}

运行结果:

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
addr: 0x24f26c8 0x7ffc236ba870
位置1的值为 : first one 位置2的值为 : 20
栈元素的个数 : 3
弹出栈后的元素个数 : 0
------------------------------------------------
str : hello,I am a lua value 栈元素个数 : 1
lua tbl.name : test 栈元素个数 : 3
add返回值 : hello,lua ~ 栈元素个数 : 4
------------------------------------------------
num is 2
文件名file1添加成功
num is 0
num is 2
文件名file2添加成功
num is 0
num is 2
文件名已经存在:I am file 2
num is 0
------------------------------------------------
file2的内容是I am file 2
file1的内容是I am file 1