​

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
创建人: 笨木头

创建日期: 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

堆栈索引

 cocos2d-x 使用Lua_堆栈

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支持多个返回值的函数)