转自:http://www.benmutou.com/blog/archives/49
1. Lua的堆栈和全局表
我们来简单解释一下Lua的堆栈和全局表,堆栈大家应该会比较熟悉,它主要是用来让C++和Lua通信的,是的,它们并不认识对方,只能通过堆栈来沟通,就像写信一样。
Lua的全局表又是什么呢?可以想象成是一个map哈希表结构,比如Lua有一个变量:
name = “hello”
那么,全局表就存放了”name”和”hello”的对应关系,Lua可以通过name在全局表中查找到hello。应该是这样的~
2. Lua和C++的第一次通信
-- hello.lua 文件 myName = "beauty girl"
然后,C++想知道Lua叫什么名字,所以,它们必须要通信了。来看看通信流程:
请注意红色数字,代表通信顺序:
1) C++想获取Lua的myName字符串的值,所以它把myName放到Lua堆栈(栈顶),以便Lua能看到
2) Lua从堆栈(栈顶)中获取myName,此时栈顶再次变为空
3) Lua拿着这个myName去Lua全局表查找myName对应的字符串
4) 全局表返回一个字符串”beauty girl”
5) Lua把取得的“beauty girl”字符串放到堆栈(栈顶)
6) C++可以从Lua堆栈中取得“beauty girl”,也就是这位美丽的Lua小姐的名字了~
3. 引入头文件
我们来看看要在C++中使用Lua,需要些什么东西
/* 文件名: HelloLua.h 描 述: Lua Demo 创建人: 笨木头 (CSDN博客:http://blog.csdn.net/musicvs) 创建日期: 2012.12.24 */ #ifndef __HELLO_LUA_H_ #define __HELLO_LUA_H_ #include "cocos2d.h" extern "C" { #include <lua.h> #include <lualib.h> #include <lauxlib.h> }; using namespace cocos2d; class HelloLua : public CCLayer { public: CREATE_FUNC(HelloLua); virtual bool init(); static CCScene* scene(); }; #endif
4. 开始使用
来看看我们的cpp文件,我们要开始使用Lua了~!
#include "HelloLua.h" CCScene* HelloLua::scene() { CCScene* scene = CCScene::create(); CCLayer* layer = HelloLua::create(); scene->addChild(layer); return scene; } bool HelloLua::init() { lua_State* pL = lua_open(); luaopen_base(pL); luaopen_math(pL); luaopen_string(pL); /* 1.执行Lua脚本,返回0代表成功 */ /* 2.重置栈顶索引 */ /* 3.判断栈顶的值的类型是否为String, 返回非0值代表成功 */ /* 4.获取栈顶的值 */ lua_close(pL); return true; }
5. 执行Lua脚本
现在我们来一步步完善我们的代码,执行Lua脚本很简单,看看:
bool HelloLua::init() { lua_State* pL = lua_open(); luaopen_base(pL); luaopen_math(pL); luaopen_string(pL); /* 1.执行Lua脚本,返回0代表成功 */ int err = luaL_dofile(pL, "helloLua.lua"); CCLOG("open : %d", err); /* 2.重置栈顶索引 */ lua_settop(pL, 0); lua_getglobal(pL, "myName"); /* 3.判断栈顶的值的类型是否为String, 返回非0值代表成功 */ /* 4.获取栈顶的值 */ lua_close(pL); return true; }
6. 重置栈顶索引, 将全局变量放到堆栈中
lua_settop(pL, 0);是为了确认让栈顶的索引置为0,因为我们操作栈的时候是根据索引来操作的。置0之后,我们入栈的第一个元素的索引就是1。
那,lua_getglobal(pL, “myName”);又是什么呢?咋一看好像是从lua中取得myName这个全局变量的值,但并不是这样的,虽然最终也是这样。
我们之前说过了,Lua和C++是不能直接通信的,要通过堆栈来通信。
因此,lua_getglobal(pL, “myName”);只是把myName放到了栈中,然后lua就会通过myName去全局表寻找,找到myName对应的字符串“beauty girl”,再放到栈中。
7. C++取得字符串
我们来看看完整的代码:
bool HelloLua::init() { lua_State* pL = lua_open(); luaopen_base(pL); luaopen_math(pL); luaopen_string(pL); /* 1.执行Lua脚本,返回0代表成功 */ int err = luaL_dofile(pL, "helloLua.lua"); CCLOG("open : %d", err); /* 2.重置栈顶索引 */ lua_settop(pL, 0); lua_getglobal(pL, "myName"); /* 3.判断栈顶的值的类型是否为String, 返回非0值代表成功 */ int isstr = lua_isstring(pL, 1); CCLOG("isstr = %d", isstr); /* 4.获取栈顶的值 */ const char* str = lua_tostring(pL, 1); CCLOG("getStr = %s", str); lua_close(pL); return true; }
lua_getglobal已经完成了很多工作了,现在堆栈上就放着“beauty girl”字符串,我们只要去取就可以了。
获取堆栈的值有很多种方法,分别对应不同的变量类型:
lua_toboolean
lua_toNumber
lua_tocfunction
lua_tostring
我就不全部举例了,现在我们要用lua_tostring来获取栈顶的值。
最后,在AppDelegate.cpp中把默认启动场景设为我们的HelloLua场景,用调试模式运行项目,将看到以下日志:
open : 0
isstr = 1
getStr = beauty girl
堆栈索引
1. 正数索引,栈底是1
2. 负数索引,栈顶是-1
8. C++调用Lua函数
-- helloLua.lua文件 myName = "beauty girl" helloTable = {name = "mutou", IQ = 125} function helloAdd(num1, num2) return (num1 + num2) end;
C++调用helloAdd函数
/* C++调用lua的函数 */ void HelloLua::demo3() { lua_State* pL = lua_open(); luaopen_base(pL); /* 执行脚本 */ luaL_dofile(pL, "helloLua.lua"); /* 把helloAdd函数对象放到栈中 */ lua_getglobal(pL, "helloAdd"); /* 把函数所需要的参数入栈 */ lua_pushnumber(pL, 10); lua_pushnumber(pL, 5); /* 执行函数,第一个参数表示函数的参数个数,第二个参数表示函数返回值个数 , Lua会先去堆栈取出参数,然后再取出函数对象,开始执行函数 */ lua_call(pL, 2, 1); int iResult = lua_tonumber(pL, -1); CCLOG("iResult = %d", iResult); }
简单说明一下步骤:
1) 将helloAdd函数放到栈中:lua_getglobal(pL, “helloAdd”) 。(旁白:看吧,我就知道~!)
2) helloAdd有2个参数,我们要把参数传递给lua,所以2个参数都要放到栈里。
3) 第2和第3步已经把函数所需要的数据都放到栈里了,接下来只要告诉lua去栈里取数据,执行函数~!
9. Lua调用C++函数
创建一个c++函数先:
public: static int getNumber(int num); int HelloLua::getNumber( int num ) { CCLOG("getNumber num = %d", num); return num + 1; }
现在,我们想在Lua中调用这个函数,得多写一个函数。
public: static int cpp_GetNumber(lua_State* pL); int HelloLua::cpp_GetNumber( lua_State* pL ) { /* 从栈顶中取一个值 */ int num = (int)lua_tonumber(pL, 1); /* 调用getNumber函数,将返回值入栈 */ lua_pushnumber(pL, getNumber(num)); /* 返回值个数,getNumber只有一个返回值,所以返回1 */ return 1; }
这是怎么回事呢?我们很清楚,Lua和C++只能通过堆栈通信,所以Lua是不可能直接调用getNumber函数的,所以我们建立一个cpp_GetNumber函数作为中介。
cpp_GetNumber函数有一个lua_State* pL参数,有了这个参数,c++就能从Lua的堆栈中取值了
1) 首先,Lua脚本里会调用cpp_GetNumber函数。
2) 当cpp_GetNumber被调用时,一切又回到C++对Lua的操作了,栈顶里会存放函数所需要的参数,取出来用就可以的。
3) Lua调用cpp_GetNumber之后,需要一个结果,当然,这个结果同样只能存放在栈里,所以理所当然地要把getNumber的结果入栈。
4) 最后,cpp_GetNumber return了一个值,这个值不是函数的执行结果,而是getNumber需要返回值的个数(Lua支持多个返回值的函数)