操作流程
1.宿主语言建立Lua解释器(lua状态机)对象。
2.将宿主语言实现的Lua扩展(若有),如函数等,注册到Lua解释器中,供其使用。
3.读入Lua源程序或预先编译后的Lua程序(可以从文件、字符串、网络等任意来源)。
4.执行读入的Lua程序。
Lua与宿主语言的交互
宿主语言通过虚拟机,对Lua脚本中的变量实现增、删、读、写
宿主语言通过虚拟机调用Lua脚本中的函数
宿主语言定义新的数据类型供Lua脚本使用
Lua调用宿主语言编写的函数
基本用法
需要引入的头文件:
extern "C" //**使用C语言编译**!
{
#include
//Lua语言解析器
#include
//Lua标准库 #include
//Lua辅助工具 }
C++对Lua脚本的调用、解析,无交互
char* code = "for i=0, 5 do print(\'Hello, world!\') end";
void TestCWithLua()
{
cout << "/*******测试C++调用lua的代码*******/" << endl;
//1.创建lua解释器对象
lua_State* s = luaL_newstate(); /*lua_open();*/
//2.打开所有lua的库文件
luaL_openlibs(s);
//3.执行lua字符串代码
luaL_dostring(s, code);
//luaL_dofile(s, "LuaSrc\\testcwithlua.lua");
//4.关闭lua解释器对象
lua_close(s);
cout << "/*******结束测试*******/" << endl;
}
上例只实现了对Lua脚本的解析,并没有实现Lua与宿主语言的数据交换和互操作。
和典型的脚本语言引擎相同,Lua虚拟机是一个堆栈机,其一切运算基本都在堆栈上完成,这个堆栈也是Lua API的关键部分,是Lua与宿主语言交换数据的手段。
题外:宿主语言可以用字符串构建任意Lua脚本,实现向Lua程序传递任意数据,就像构建SQL语句一样,也不失是最“笨”的交互方式。
Lua堆栈
Lua虚拟机内部有一个堆栈,Lua API提供了对其的操作,不仅有出入栈操作,还可以以数组的形式,通过索引值随机读写栈元素,这是双方交换数据的主要方式。
用宿主语言可以编写供Lua调用的函数,宿主语言需要遵守调用约定,从栈中取得参数,最后也将结果入栈。将宿主函数通过lua_register注册入Lua虚拟机(这一过程实质为向Lua语言添加全局变量),就可以被Lua语言所调用。
宿主语言也可以将Lua函数压栈,再将参数依次压栈,最后使用lua_call,完成对Lua函数的调用。
Lua堆栈索引
若Lua虚拟机堆栈里有N个元素,则可以用 1 ~ N 从栈底向上索引,也可以用 -1 ~ -N 从栈顶向下索引,一般后者更加常用。
堆栈的每个元素可以为任意复杂的Lua数据类型,堆栈中没有元素的空位,隐含为包含一个“空”类型数据。
Lua调用C++
testluawithc.lua
a = 13
b = 5
q, r = p(a, b)
print(q, r)
int Divided(lua_State* s)//供Lua使用的函数通用原型
{
double a = lua_tonumber(s, -2);//取得第一个参数
double b = lua_tonumber(s, -1);//取得第二个参数
int ia = static_cast
(a);
int ib = static_cast
(b); int quot = ia / ib; int rem = ia % ib; lua_pushnumber(s, quot);//将第一个返回值入栈 lua_pushnumber(s, rem);//将第二个返回值入栈 return 2;//返回值为结果个数 } void TestLuaWithC() { cout << "/*******测试lua调用c++的代码*******/" << endl; lua_State* s = luaL_newstate(); luaL_openlibs(s); lua_register(s, "p", Divided); int ret = luaL_dofile(s, "LuaSrc\\testluawithc.lua"); //luaL_dostring(s, "a = 13 b = 5 q, r = p(a, b) print(q, r)"); lua_close(s); cout << "/*******结束测试*******/" << endl; }
注意:参数的传递,都是通过Lua栈。
由上例可见,可被Lua调用的宿主函数具有统一的原型:int f(lua_State *s),数据传递不通过其参数,而是通过堆栈;整型返回值指明了该函数真正向Lua返回的值的个数,即压栈的结果个数。函数返回后,Lua虚拟机会自动进行清栈工作,不需在函数内部来做。
显然,在Lua中函数可以有不止一个返回值,这在Lua语法中也有体现,可以将函数返回赋值给多个变量。
C++调用lua,lua全局变量和函数调用,有交互
testglobal.lua
show = function(m)
print('Lua has got: ' ..m)
return 'It is from Lua'
end
void TestGlobalAndCall()
{
cout << "/*******测试使用lua全局变量的代码*******/" << endl;
lua_State* s = luaL_newstate();
luaL_openlibs(s);
/*luaL_dostring(s, "show = function(m) \
print('Lua has got: ' ..m) \ //..字符串连接符
return 'It is from Lua' \
end");*/
luaL_dofile(s, "LuaSrc\\testglobal.lua");
lua_getglobal(s, "show");//获得全局变量show
lua_pushstring(s, "It is from C");//将字符串压栈
lua_call(s, 1, 1);//调用lua函数,1个参数,1个返回值
const char* result = lua_tostring(s, -1);//获取执行后的栈顶元素,即函数执行结果
cout << "C has got:" << result << endl;
lua_pop(s, 1);//弹出栈顶元素
lua_close(s);
cout << "/*****