Lua 是一门脚本语言,它有着一个重要的特性就是:它很容易嵌入其它语言。现在,越来越多的 C++ 服务器和客户端融入了脚本的支持,尤其在网游领域,脚本语言已经渗透到了方方面面,比如你可以在你的客户端增加一个脚本,这个脚本将会帮你在界面上显示新的数据,亦或帮你完成某些任务,亦或帮你查看别的玩家或者 NPC 的状态。对于游戏测试来说,大量繁杂的工作都可以由 Lua 代劳,因此在测试领域它也是一把利器。
关于 Lua 的一些重要基础知识:
Lua 有重要之重要的概念,就是栈。Lua 与别的语言交互以及交换数据,是通过栈完成的。其实简单的解释一下,你可以把栈想象成一个箱子,你要给他数据,就要按顺序一个个的把数据放进去,当然,Lua 执行完毕,可能会有结果返回给你,那么 Lua 还会利用你的箱子,一个个的继续放下去。而你取出返回数据呢,要从箱子顶上取出,如果你想要获得你的输入参数呢?那也很简单,按照顶上返回数据的个数,再按顺序一个个的取出,就行了。不过这里提醒大家,关于栈的位置,永远是相对的,比如 -1 代表的是当前栈顶,-2 代表的是当前栈顶下一个数据的位置。栈是数据交换的地方,一定要有一些栈的概念。
首先、编译 Lua
首先下载 Lua 5.2.x(官网链接),一般情况下,很容易就可以通过 make macosx
或者 make linux
来编译。这样会生成 liblua.a
静态库文件,我们随后要连接这个库。
其次、准备环境
首先,我们要在开发工具中连接 liblua.a
,然后要将 Lua 源代码文件的位置添加到编译器的头文件搜索目录列表里面去,这样我们的编译器才能找到 Lua 的头文件。
然后、创建程序
我们需要创建一个 C++ 的主程序,以便同 Lua 进行通信。
cpp 程序如下:
#include <iostream> #include <lua.hpp> extern "C" { static int l_cppfunction(lua_State *L) { double arg = luaL_checknumber(L,1); lua_pushnumber(L, arg * 0.5); return 1; } } using namespace std; int main(int argc, const char * argv[]) { lua_State *L; L = luaL_newstate(); cout << ">> 载入(可选)标准库,以便使用打印功能" << endl; luaL_openlibs(L); cout << ">> 载入文件,暂不执行" << endl; if (luaL_loadfile(L, "luascript.lua")) { cerr << "载入文件出现错误" << endl; cerr << lua_tostring(L, -1) << endl; lua_pop(L,1); } cout << ">> 从 C++ 写入数据 cppvar" << endl; lua_pushnumber(L, 1.1); lua_setglobal(L, "cppvar"); cout << ">> 执行 lua 文件" << endl << endl; if (lua_pcall(L,0, LUA_MULTRET, 0)) { cerr << "执行过程中出现错误" << endl; cerr << lua_tostring(L, -1) << endl; lua_pop(L,1); } cout << ">> 从 Lua 读取全局变量 luavar 到 C++" << endl; lua_getglobal(L, "luavar"); double luavar = lua_tonumber(L,-1); lua_pop(L,1); cout << "C++ 从 Lua 读取到的 luavar = " << luavar << endl << endl; cout << ">> 从 C++ 执行 Lua 的方法 myfunction" << endl; lua_getglobal(L, "myluafunction"); lua_pushnumber(L, 5); lua_pcall(L, 1, 1, 0); cout << "函数返回值是:" << lua_tostring(L, -1) << endl << endl; lua_pop(L,1); cout << ">> 从 Lua 执行 C++ 的方法" << endl; cout << ">>>> 首先在 Lua 中注册 C++ 方法" << endl; lua_pushcfunction(L,l_cppfunction); lua_setglobal(L, "cppfunction"); cout << ">>>> 调用 Lua 函数以执行 C++ 函数" << endl; lua_getglobal(L, "myfunction"); lua_pushnumber(L, 5); lua_pcall(L, 1, 1, 0); cout << "函数返回值是:" << lua_tonumber(L, -1) << endl << endl; lua_pop(L,1); cout << ">> 释放 Lua 资源" << endl; lua_close(L); return 0; }
其次,是 lua 文件,我们将它命名为 luascript.lua
print("Hello from Lua") print("Lua code is capable of reading the value set from C++", cppvar) luavar = cppvar * 3 function myluafunction(times) return string.rep("(-)", times) end function myfunction(arg) return cppfunction(arg) end
运行 cpp 文件,结果如下:
>> 载入(可选)标准库,以便使用打印功能 >> 载入文件,暂不执行 >> 从 C++ 写入数据 cppvar >> 执行 lua 文件 Hello from Lua Lua code is capable of reading the value set from C++ 1.1 >> 从 Lua 读取全局变量 luavar 到 C++ C++ 从 Lua 读取到的 luavar = 3.3 >> 从 C++ 执行 Lua 的方法 myfunction 函数返回值是:(-)(-)(-)(-)(-) >> 从 Lua 执行 C++ 的方法 >>>> 首先在 Lua 中注册 C++ 方法 >>>> 调用 Lua 函数以执行 C++ 函数 函数返回值是:2.5 >> 释放 Lua 资源
下面做一下简要讲解:
初始化
lua_State *L; L = luaL_newstate(); luaL_openlibs(L); // 载入(可选)标准库,以便使用打印功能 if (luaL_loadfile(L, "luascript.lua")) { // 载入文件,暂不执行 cerr << "载入文件出现错误" << endl; cerr << lua_tostring(L, -1) << endl; lua_pop(L,1); }
上述代码创建 lua_State
并载入标准库,同时载入代码 luascript.lua
。
从 C++ 向 Lua 添加变量
lua_pushnumber(L, 1.1); lua_setglobal(L, "cppvar"); if (lua_pcall(L,0, LUA_MULTRET, 0)) { cerr << "执行过程中出现错误" << endl; cerr << lua_tostring(L, -1) << endl; lua_pop(L,1); }
在 C++ 中通过 lua_setglobal
在 Lua 中设置一个全局变量 cppvar
。因为 C++ 和 Lua 通过 lua_State
中的堆栈来交换数据,所以要先 push
数据到堆栈,然后调用 lua_setglobal
,这样就将数据赋给相应的值。
设置完全局的 cppvar
之后,执行 lua_pcall
来运行我们的 Lua 代码文件,之后,Lua 就可以使用 cppvar
变量。在 Lua 代码中,还创建了新的全局变量 luavar
可以供 C++ 访问。
从 C++ 中读取 Lua 变量
lua_getglobal(L, "luavar"); double luavar = lua_tonumber(L,-1); lua_pop(L,1); cout << "C++ 从 Lua 读取到的 luavar = " << luavar << endl << endl;
要从 Lua 中读取数据,我们先要使用 lua_getglobal
将数据放到栈顶,然后将栈顶数据通过 lua_tonumber
转为 double,然后通过 lua_pop
将其移除出栈顶。
从 C++ 中调用 Lua 函数
lua_getglobal(L, "myluafunction"); lua_pushnumber(L, 5); lua_pcall(L, 1, 1, 0); cout << "函数返回值是:" << lua_tostring(L, -1) << endl << endl; lua_pop(L,1);
由了前面的过程,这个比较容易理解了:首先我们通过 lua_getglobal
取得方法名称,即将其放到栈顶,然后通过 lua_pushnumber
给参数赋值,然后通过 lua_pcall
执行。
执行之后,再去栈顶获取返回结果,然后 lua_pop
将其移除。
从 Lua 中调用 C++ 函数
lua_pushcfunction(L,l_cppfunction); lua_setglobal(L, "cppfunction"); lua_getglobal(L, "myfunction"); lua_pushnumber(L, 5); lua_pcall(L, 1, 1, 0); cout << "函数返回值是:" << lua_tonumber(L, -1) << endl << endl; lua_pop(L,1);
这一段先是使用 lua_pushcfunction
来将 C++ 的方法 l_cppfunction
传递给 Lua,然后通过 lua_setglobal
给予其在 Lua 中的方法名称 cppfunction,接下来执行就很简单了。
释放
lua_close(L);
以上就是一个简单的 Lua 与 C++ 交互的简单例子,算是入门的基础吧。我们可以看到,C++ 和 Lua 能够很自由的进行通信,而我们也可以很方便的修改 Lua 文件代码来实现对 C++ 程序流程的直接控制,对游戏开发、调试有很大的好处。
好文章,顶一下