Lua是一种嵌入式语言,可以很好的嵌入其他应用程序。lua为我们提供了一组灵活的C API,使C代码能够很好的与Lua进行交互。包括读写Lua全局变量,调用Lua函数,运行Lua代码,注册C函数反过来供Lua调用。简单的说,C能调用Lua,反过来Lua也能调用C。真的是灰常强大灵活的脚本!!现在,先来学习一下怎么用C调用Lua。
其实最简单的我们已经做过了,通过一个dofile,运行一个lua脚本文件。
Lua与C的交互是通过一个虚拟栈进行的,这个栈对于Lua来说是严格的LIFO(后进先出)的,当调用Lua时,Lua只会改变栈的顶部。不过C有更大的自由度,可以检索栈中元素,甚至在任意位置插入和删除元素。
当Lua启动或者Lua调用C语言时,栈中至少有20个空间的空闲槽,一般调用来说这些空间足够了。如果调用的参数特别特别多,需要先检查槽够不够用,使用下面的函数:
<span style="white-space:pre"> </span>int lua_checkstack(lua_State* L, int sz );API使用索引来引用栈中的元素,记住最开始的索引为1,不是0!即第一个压入栈中的元素索引为1,第二个压入栈中的元素索引为2,直到栈顶。也可以使用负数的索引来访问栈顶的元素,即-1表示栈顶元素,以此类推。
在C语言的lua库中,提供了几个关于栈中元素操作的函数,由于C语言实现里没有泛型,所以,对应每一种数据类型都提供了一个函数,这里后面的数据类型暂时用*代替。
//检查栈中index索引的数据类型是否是*的类型 int lua_is*(lua_State * L, int index) //返回栈中index索引的数据的类型 int lua_type(lua_State* L, int index) //返回栈中index索引的数据的值,转化为*的类型 * lua_to*(lua_State* L, int index) //向栈中插入*类型的元素 void lua_push*(lua_State* L, type*)
而既然这个东东是个栈,所以当然也提供了一些列栈本身的操作:
//获得栈中元素个数 int lua_gettop(lua_gettop) (lua_State *L); //设置栈顶为一个指定位置 void lua_settop(lua_settop) (lua_State *L, int idx); //将指定索引上的值再次压入栈 void lua_pushvalue(lua_State *L, int idx); //删除指定索引的元素,之上的向下移补缺 void lua_remove(lua_State* L, int idx); //在index处开辟一个位置,上面的上移,然后将栈顶元素放到这个位置 void lua_insert(lua_State* L, int idx); //弹出栈顶元素,使用该元素替代index元素 void lua_replace(lua_State* L, int idx);
// LuaTest.cpp : 定义控制台应用程序的入口点。 // #include "stdafx.h" #include <stdio.h> #include <windows.h> //因为Lua是C的函数,而我们的程序是C++的,所以要使用extern "C"引入头文件 extern "C"{ #include "lua.h" #include "lualib.h" #include "lauxlib.h" #include "luaconf.h" } //注意还需要添加之前编译好的Lualib.lib文件,这里通过项目->属性->连接器->附加依赖项添加了 //否则需要 #pragma comment(lib, "lualib.lib")来添加 //打印stack中的数据 void CheckStack(lua_State* L) { int top = lua_gettop(L);//stack的大小 //遍历stack所有层 for (int i = 1; i <= top; i++) { int type = lua_type(L, i); switch (type) { case LUA_TSTRING://字符串 printf("%s\n", lua_tostring(L, i)); break; case LUA_TBOOLEAN://布尔值 printf(lua_toboolean(L, i) ? "true\n" : "false\n"); break; case LUA_TNUMBER://数字 printf("%d\n", lua_tonumber(L, i)); break; default://其他值 printf("%s\n", lua_typename(L, i)); break; } } printf("\n"); } int _tmain(int argc, _TCHAR* argv[]) { //打开lua lua_State* L = luaL_newstate(); //加载lib文件 luaL_openlibs(L); //向栈中压入内容 lua_pushboolean(L, 1); lua_pushstring(L, "hehe"); lua_pushnumber(L, 100); //打印栈中内容 CheckStack(L); //将index为1的内容再次压入栈中 lua_pushvalue(L, 1); CheckStack(L); //删除index为2的元素 lua_remove(L, 2); CheckStack(L); //设置栈顶为16(这个空了的地方貌似被补成每个类型一种,其余为空了) lua_settop(L, 16); CheckStack(L); //结束 lua_close(L); system("pause"); return 0; }结果:
true
hehe
0
true
hehe
0
true
true
0
true
true
0
true
string
table
function
userdata
thread
proto
(null)
(null)
(null)
(null)
(null)
(null)
(null)
请按任意键继续. . .
--配置文件,包含两个全局变量 arg1 = 1 arg2 = 2
// LuaTest.cpp : 定义控制台应用程序的入口点。 // #include "stdafx.h" #include <stdio.h> #include <windows.h> //因为Lua是C的函数,而我们的程序是C++的,所以要使用extern "C"引入头文件 extern "C"{ #include "lua.h" #include "lualib.h" #include "lauxlib.h" #include "luaconf.h" } //注意还需要添加之前编译好的Lualib.lib文件,这里通过项目->属性->连接器->附加依赖项添加了 //否则需要 #pragma comment(lib, "lualib.lib")来添加 //打印stack中的数据 void CheckStack(lua_State* L) { int top = lua_gettop(L);//stack的大小 //遍历stack所有层 for (int i = 1; i <= top; i++) { int type = lua_type(L, i); switch (type) { case LUA_TSTRING://字符串 printf("%s\n", lua_tostring(L, i)); break; case LUA_TBOOLEAN://布尔值 printf(lua_toboolean(L, i) ? "true\n" : "false\n"); break; case LUA_TNUMBER://数字 printf("%d\n", lua_tonumber(L, i)); break; default://其他值 printf("%s\n", lua_typename(L, i)); break; } } printf("\n"); } //读取lua脚本文件的函数(此处作为一个配置文件) void LoadLua(lua_State* L , const char* filename, int * arg1, int * arg2) { if (luaL_loadfile(L, filename) || lua_pcall(L, 0, 0, 0)) printf("loadfile failed! %s\n", lua_tostring(L, -1));//如果失败,栈顶为错误信息 //获得全局变量,压入栈中 lua_getglobal(L, "arg1"); lua_getglobal(L, "arg2"); //判断一下素不素想要的类型 if (!lua_isnumber(L, -2)) printf("arg1 is not number\n"); if (!lua_isnumber(L, -1)) printf("arg2 is not number\n"); //提取栈中的参数值 *arg1 = lua_tointeger(L, -2); *arg2 = lua_tointeger(L, -1); } int _tmain(int argc, _TCHAR* argv[]) { //打开lua lua_State* L = luaL_newstate(); //加载lib文件 luaL_openlibs(L); int i, j = 0; LoadLua(L, "test.lua", &i, &j); printf("i = %d, j = %d\n", i , j); //结束 lua_close(L); system("pause"); return 0; }
简单解释一下:
luaL_loadfile是加载lua文件,作为一个程序块,但是并不执行。
lua_pcall运行编译好的程序块。
lua_getglobal通过名称获得全局变量的值,压入栈中。
这样就通过C程序加载lua文件,达到了加载配置文件的目的,虽然看起来比较麻烦,不过封装一下的话,还是很好用的。而且使用Lua作为配置文件,一方面是容易操作,不需要额外写一些配置文件读取的工具,另一方面是可以在配置文件中添加注释等等,甚至还可以写一些条件判断等等。
--配置文件,包含两个全局变量 struct = {arg1 = 1, arg2 = 2}
// LuaTest.cpp : 定义控制台应用程序的入口点。 // #include "stdafx.h" #include <stdio.h> #include <windows.h> //因为Lua是C的函数,而我们的程序是C++的,所以要使用extern "C"引入头文件 extern "C"{ #include "lua.h" #include "lualib.h" #include "lauxlib.h" #include "luaconf.h" } //注意还需要添加之前编译好的Lualib.lib文件,这里通过项目->属性->连接器->附加依赖项添加了 //否则需要 #pragma comment(lib, "lualib.lib")来添加 //打印stack中的大小 void CheckStack(lua_State* L) { int top = lua_gettop(L);//stack的大小 printf("The size of the stack is %d\n", top); } //读取一个table的数据,table更加结构化 void LoadLua(lua_State* L , const char* filename, int * arg1, int * arg2) { //和直接读取一样,先加载file,然后执行 if (luaL_loadfile(L, filename) || lua_pcall(L, 0, 0, 0)) printf("loadfile failed! %s\n", lua_tostring(L, -1));//如果失败,栈顶为错误信息 CheckStack(L);//此时stack为空 //将struct结构体放入栈中 lua_getglobal(L, "struct"); if (! lua_istable(L, -1)) printf("struct is not table!\n"); CheckStack(L);//此时栈大小为1 //从struct中提取arg1放在栈顶 lua_getfield(L, -1, "arg1"); if (! lua_isnumber(L, -1)) printf("arg1 is not number!\n"); CheckStack(L);//此时栈大小为2 *arg1 = lua_tointeger(L, -1); //上一个元素用完了,就把栈顶元素弹出 lua_pop(L, 1); CheckStack(L);//此时栈大小为1 //提取arg2放在栈顶 lua_getfield(L, -1, "arg2"); if (! lua_isnumber(L, -1)) printf("arg2 is not number!\n"); CheckStack(L); *arg2 = lua_tointeger(L, -1); } int _tmain(int argc, _TCHAR* argv[]) { //打开lua lua_State* L = luaL_newstate(); //加载lib文件 luaL_openlibs(L); int i, j = 0; LoadLua(L, "test.lua", &i, &j); printf("i = %d, j = %d\n", i , j); //结束 lua_close(L); system("pause"); return 0; }
还是上次的那两个数据,不过这次他们被放在了一个table里面,即使有相同的N组,也可以用N个table来存储,不用担心杂乱的问题。
简单分析一下C读取table的过程:
还是通过loadfile加载进来,然后仍然是getglobal获得全局变量,但是这次的全局变量是一个table,在进行下一步操作之前先检查一下是否真是个table,然后,通过另一个函数getfiled()获得table中特定字段的内容,放在栈顶。读取一个之后,将其弹出,然后再次获得下一个字段的内容,读取,以此类推。
赶脚这个table类型的配置文件就跟XML差不多了。一直没找到C++下的好的XML解析器,实在不行以后就用Lua把。
--add fucntion function add(a, b) return a + b end
// LuaTest.cpp : 定义控制台应用程序的入口点。 // #include "stdafx.h" #include <stdio.h> #include <windows.h> //因为Lua是C的函数,而我们的程序是C++的,所以要使用extern "C"引入头文件 extern "C"{ #include "lua.h" #include "lualib.h" #include "lauxlib.h" #include "luaconf.h" } //注意还需要添加之前编译好的Lualib.lib文件,这里通过项目->属性->连接器->附加依赖项添加了 //否则需要 #pragma comment(lib, "lualib.lib")来添加 //打印stack中的数据 void CheckStack(lua_State* L) { int top = lua_gettop(L);//stack的大小 //遍历stack所有层 for (int i = 1; i <= top; i++) { int type = lua_type(L, i); switch (type) { case LUA_TSTRING://字符串 printf("%s ", lua_tostring(L, i)); break; case LUA_TBOOLEAN://布尔值 printf(lua_toboolean(L, i) ? "true " : "false "); break; case LUA_TNUMBER://数字 printf("%g ", lua_tonumber(L, i)); break; default://其他值 printf("%s ", lua_typename(L, i)); break; } } printf("\n"); printf("The size of the stack is %d\n", top); } //读取一个table的数据,table更加结构化 void LoadLua(lua_State* L , const char* filename, int * arg1, int * arg2) { //和直接读取一样,先加载file,然后执行 if (luaL_loadfile(L, filename) || lua_pcall(L, 0, 0, 0)) printf("loadfile failed! %s\n", lua_tostring(L, -1));//如果失败,栈顶为错误信息 CheckStack(L);//此时stack为空 //将struct结构体放入栈中 lua_getglobal(L, "struct"); if (! lua_istable(L, -1)) printf("struct is not table!\n"); CheckStack(L);//此时栈大小为1 //从struct中提取arg1放在栈顶 lua_getfield(L, -1, "arg1"); if (! lua_isnumber(L, -1)) printf("arg1 is not number!\n"); CheckStack(L);//此时栈大小为2 *arg1 = lua_tointeger(L, -1); //上一个元素用完了,就把栈顶元素弹出 lua_pop(L, 1); CheckStack(L);//此时栈大小为1 //提取arg2放在栈顶 lua_getfield(L, -1, "arg2"); if (! lua_isnumber(L, -1)) printf("arg2 is not number!\n"); CheckStack(L); *arg2 = lua_tointeger(L, -1); } //调用Lua的函数 double add(lua_State* L , double x, double y) { //压入函数和参数 //提取lua中的函数,放入栈中 lua_getglobal(L, "add"); //将两个参数压入栈中 lua_pushnumber(L, x); lua_pushnumber(L, y); CheckStack(L); //进行调用 if (lua_pcall(L, 2, 1, 0) != 0) printf("function called failed! %s\n", lua_tostring(L, -1)); CheckStack(L); //从栈中提取结果 if (!lua_isnumber(L, -1)) printf("function must return a double number!\n"); double result = lua_tonumber(L, -1); lua_pop(L, 1); CheckStack(L); return result; } //读取lua文件并执行函数 void GetLuaFunctiorn(lua_State* L, const char* filename) { if (luaL_loadfile(L, filename) || lua_pcall(L, 0, 0, 0)) printf("load file failed! %s\n", lua_tostring(L, -1)); double resulet = add(L, 1.5, 2.5); printf("result is %f\n", resulet); } int _tmain(int argc, _TCHAR* argv[]) { //打开lua lua_State* L = luaL_newstate(); //加载lib文件 luaL_openlibs(L); //使用函数 GetLuaFunctiorn(L, "test.lua"); //结束 lua_close(L); system("pause"); return 0; }