這是我的Lua系列教程的第二篇,本篇文章主要介紹C++和Lua相互傳遞資料。如果你還不知道怎麼在c/c++裡面調用Lua指令碼的話,請參考這篇文章. 本文主要介紹基礎資料型別 (Elementary Data Type)的傳遞,比如整形(int),字串(string)、數字(number)及bool值。
載入並運行Lua指令碼
由於在上一個教程裡面已經介紹過如何在C/C++裡面嵌入Lua,所以這一節就簡單的介紹一下程式怎麼用,配置就略過啦。
建立Lua虛擬機器
複製代碼 代碼如下:
lua_State *lua_state = luaL_newstate();
載入Lua庫
複製代碼 代碼如下:
static const luaL_Reg lualibs[] =
{
{"base", luaopen_base},
{"io", luaopen_io},
{NULL, NULL}
};
const luaL_Reg *lib = lualibs;
for(; lib->func != NULL; lib++)
{
luaL_requiref(lua_state, lib->name, lib->func, 1);
lua_pop(lua_state,1);
}
運行Lua指令碼
複製代碼 代碼如下:
std::string scriptPath = FileUtils::getInstance()->fullPathForFilename("hello.lua");
int status = luaL_loadfile(lua_state, scriptPath.c_str());
std::cout << " return: " << status << std::endl;
int result = 0;
if(status == LUA_OK)
{
result = lua_pcall(lua_state, 0, LUA_MULTRET, 0);
}
else
{
std::cout << " Could not load the script." << std::endl;
}
這裡我們使用的是luaL_loadfile而不是之前的luaL_dofile,其實luaL_dofile只是一個宏定義:
複製代碼 代碼如下:
#define luaL_dofile(L, fn) \
(luaL_loadfile(L, fn) || lua_pcall(L, 0, LUA_MULTRET, 0))
我們先調用luaL_loadfile可以判斷Lua指令碼是否載入成功,然後再調用lua_pcall來執行Lua指令碼。
C/C++調用Lua函數
首先,我們在hello.lua裡面定義一個lua函數:
複製代碼 代碼如下:
-- add two numbers
function add ( x, y )
return x + y
end
Lua的函數定義是以function為keyword,然後以end結尾,同時它的參數是沒有形參類型的,另外,Lua的函數可以返回多個值。不過我們這裡只返回了一個值。
接下來,讓我們看看如果在C++程式裡面調用這個函數:
複製代碼 代碼如下:
int luaAdd(lua_State *lua_state , int x, int y)
{
int sum;
//擷取lua裡面的add函數並把它放到lua的棧頂
lua_getglobal(lua_state, "add");
//往lua棧裡面壓入兩個參數
lua_pushnumber(lua_state, x);
lua_pushnumber(lua_state, y);
//調用lua函數,這裡的2是參數的個數,1是傳回值的個數
lua_call(lua_state, 2, 1);
//從棧頂讀取傳回值,注意這裡的參數是-1
sum = lua_tointeger(lua_state, -1);
//最後我們把傳回值從棧頂拿掉
lua_pop(lua_state, 1);
return sum;
}
然後,我們就可以在程式裡面調用它了:
複製代碼 代碼如下:
std::cout<< "2 + 1= " << luaAdd(lua_state,4,1)<<std::endl;
注意這個方法調用要在lua_pcall調用之後。
操作Lua全域變數
C++裡面擷取Lua全域變數的值
首先,我們在hello.lua裡面定義一個全域變數
複製代碼 代碼如下:
myname = "子龍山人"
然後我們在C++裡面訪問它:
複製代碼 代碼如下:
lua_getglobal(lua_state, "myname");
std::string myname = lua_tostring(lua_state, -1);
lua_pop(lua_state, 1);
std::cout<<"Hello: "<<myname<<std::endl;
這一次我們又是通過lua_getglobal來把myname這個全域變數壓到lua棧,然後用lua_tostring來取這個值。
C++裡面修改Lua全域變數的值
這次我們使用的是lua_setglobal來傳遞資料給Lua:
複製代碼 代碼如下:
lua_pushstring(lua_state, "World");
lua_setglobal(lua_state, "myname");
這時,我們只要在hello.lua的最開始部分,調用print(myname)就可以列印傳遞進來的值了。
C++傳遞Table給Lua
複製代碼 代碼如下:
lua_createtable(lua_state, 2, 0);
lua_pushnumber(lua_state, 1);
lua_pushnumber(lua_state, 49);
// lua_settable(lua_state, -3);
lua_rawset(lua_state, -3);
lua_pushnumber(lua_state, 2);
lua_pushstring(lua_state, "Life is a beach");
// lua_settable(lua_state, -3);
lua_rawset(lua_state, -3);
lua_setglobal(lua_state, "arg");
這裡我們傳遞了一個table給lua,這個table為{49,”Life is a beach”}。Lua table的索引是從1開始的,然後我們在lua指令碼裡面可以這樣子來訪問這個table:
複製代碼 代碼如下:
for i=1,#arg do
print(" ", i, arg[i])
end
這裡的#arg是獲得table的長度,然後使用arg[i]來擷取table的索引i處的value。
Lua返回多個值給C++
首先是Lua代碼:
複製代碼 代碼如下:
local temp = {9, "hehehej"}
-- temp[1]=9
-- temp[2]="See you space cowboy!"
return temp,9,1
然後是C++代碼:
複製代碼 代碼如下:
std::stringstream str_buf;
while(lua_gettop(lua_state))
{
str_buf.str(std::string());
str_buf << " ";
switch(lua_type(lua_state, lua_gettop(lua_state)))
{
case LUA_TNUMBER:
str_buf << "script returned the number: "
<< lua_tonumber(lua_state, lua_gettop(lua_state));
break;
case LUA_TTABLE:
str_buf << "script returned a table";
break;
case LUA_TSTRING:
str_buf << "script returned the string: "
<< lua_tostring(lua_state, lua_gettop(lua_state));
break;
case LUA_TBOOLEAN:
str_buf << "script returned the boolean: "
<< lua_toboolean(lua_state, lua_gettop(lua_state));
break;
default:
str_buf << "script returned an unknown-type value";
}
lua_pop(lua_state, 1);
std::cout << str_buf.str() << std::endl;
}
最後輸出結果為:
複製代碼 代碼如下:
[C++] Values returned from the script:
script returned the number: 1
script returned the number: 9
script returned a table
[C++] Closing the Lua state
在lua裡面return值的順序是table,9,1,而在C++裡面是倒過來的。因為我們是使用棧作為資料結構來傳遞資料,而棧是先進後出的。
下一篇文章,我們將介紹一下C++調用Lua的Table。