參考:
https://blog.csdn.net/v_xchen_v/article/details/77249332
https://blog.csdn.net/xiaoluoshan/article/details/53155758
Lua堆棧
Lua和C++ 的交互機(jī)制的基礎(chǔ)在于Lua提供了一個(gè)虛擬棧,C++ 和Lua之間的所有類型的數(shù)據(jù)交換都通過這個(gè)棧完成。無(wú)論何時(shí)C想從Lua中調(diào)用一個(gè)值,被請(qǐng)求的值將會(huì)被壓入棧,無(wú)論何時(shí)C想要傳遞一個(gè)值給Lua,首先將整個(gè)值壓棧,然后就可以在Lua中調(diào)用。棧的特點(diǎn)是先進(jìn)后出:

堆棧索引的方式可是是正數(shù)也可以是負(fù)數(shù),區(qū)別是:正數(shù)索引1永遠(yuǎn)表示棧底,負(fù)數(shù)索引-1永遠(yuǎn)表示棧頂。
Lua??梢源鎯?chǔ)數(shù)字、字符串、表、閉包等,它們最終都用TValue這種數(shù)據(jù)結(jié)構(gòu)來保存:

TValue結(jié)構(gòu)對(duì)應(yīng)于lua中的所有數(shù)據(jù)類型,是一個(gè){值, 類型} 結(jié)構(gòu),它把值和類型綁在一起,用tt記錄value的類型,value是一個(gè)聯(lián)合結(jié)構(gòu),由Value定義,可以看到這個(gè)聯(lián)合有四個(gè)成員:
- p:可以存一個(gè)指針, 實(shí)際上是lua中的light userdata結(jié)構(gòu)
- n:所有的數(shù)值存在這里
- b:Boolean值存在這里
- gc:gc是一個(gè)指針,它可以指向的類型由聯(lián)合體GCObject定義,諸如table、thread、closure、string等需要內(nèi)存管理垃圾回收的類型都存在這里
可以得出如下結(jié)論:
- lua中,number、boolean、nil、light userdata四種類型的值是直接存在棧上元素里的,和垃圾回收無(wú)關(guān)。
- lua中,string、table、closure、userdata、thread存在棧上元素里的只是指針,,他們都會(huì)在生命周期結(jié)束后被垃圾回收。
Lua API
/*
** access functions (stack -> C)
*/
LUA_API int (lua_isnumber) (lua_State *L, int idx);
LUA_API int (lua_isstring) (lua_State *L, int idx);
LUA_API int (lua_iscfunction) (lua_State *L, int idx);
LUA_API int (lua_isuserdata) (lua_State *L, int idx);
LUA_API int (lua_type) (lua_State *L, int idx);
LUA_API const char *(lua_typename) (lua_State *L, int tp);
LUA_API int (lua_equal) (lua_State *L, int idx1, int idx2);
LUA_API int (lua_rawequal) (lua_State *L, int idx1, int idx2);
LUA_API int (lua_lessthan) (lua_State *L, int idx1, int idx2);
LUA_API lua_Number (lua_tonumber) (lua_State *L, int idx);
LUA_API lua_Integer (lua_tointeger) (lua_State *L, int idx);
LUA_API int (lua_toboolean) (lua_State *L, int idx);
LUA_API const char *(lua_tolstring) (lua_State *L, int idx, size_t *len);
LUA_API size_t (lua_objlen) (lua_State *L, int idx);
LUA_API lua_CFunction (lua_tocfunction) (lua_State *L, int idx);
LUA_API void *(lua_touserdata) (lua_State *L, int idx);
LUA_API lua_State *(lua_tothread) (lua_State *L, int idx);
LUA_API const void *(lua_topointer) (lua_State *L, int idx);
/*
** push functions (C -> stack)
*/
LUA_API void (lua_pushnil) (lua_State *L);
LUA_API void (lua_pushnumber) (lua_State *L, lua_Number n);
LUA_API void (lua_pushinteger) (lua_State *L, lua_Integer n);
LUA_API void (lua_pushlstring) (lua_State *L, const char *s, size_t l);
LUA_API void (lua_pushstring) (lua_State *L, const char *s);
LUA_API const char *(lua_pushvfstring) (lua_State *L, const char *fmt,
va_list argp);
LUA_API const char *(lua_pushfstring) (lua_State *L, const char *fmt, ...);
LUA_API void (lua_pushcclosure) (lua_State *L, lua_CFunction fn, int n);
LUA_API void (lua_pushboolean) (lua_State *L, int b);
LUA_API void (lua_pushlightuserdata) (lua_State *L, void *p);
LUA_API int (lua_pushthread) (lua_State *L);
/*
** get functions (Lua -> stack)
*/
LUA_API void (lua_gettable) (lua_State *L, int idx);
LUA_API void (lua_getfield) (lua_State *L, int idx, const char *k);
LUA_API void (lua_rawget) (lua_State *L, int idx);
LUA_API void (lua_rawgeti) (lua_State *L, int idx, int n);
LUA_API void (lua_createtable) (lua_State *L, int narr, int nrec);
LUA_API void *(lua_newuserdata) (lua_State *L, size_t sz);
LUA_API int (lua_getmetatable) (lua_State *L, int objindex);
LUA_API void (lua_getfenv) (lua_State *L, int idx);
/*
** set functions (stack -> Lua)
*/
LUA_API void (lua_settable) (lua_State *L, int idx);
LUA_API void (lua_setfield) (lua_State *L, int idx, const char *k);
LUA_API void (lua_rawset) (lua_State *L, int idx);
LUA_API void (lua_rawseti) (lua_State *L, int idx, int n);
LUA_API int (lua_setmetatable) (lua_State *L, int objindex);
LUA_API int (lua_setfenv) (lua_State *L, int idx);
此外還提供了API函數(shù)來人工控制堆棧:
/*
** basic stack manipulation
*/
LUA_API int (lua_gettop) (lua_State *L);
LUA_API void (lua_settop) (lua_State *L, int idx);
LUA_API void (lua_pushvalue) (lua_State *L, int idx);
LUA_API void (lua_remove) (lua_State *L, int idx);
LUA_API void (lua_insert) (lua_State *L, int idx);
LUA_API void (lua_replace) (lua_State *L, int idx);
LUA_API int (lua_checkstack) (lua_State *L, int sz);
示例:
#include "lua.hpp"
#include <iostream>
int main()
{
//創(chuàng)建一個(gè)state
lua_State *L = luaL_newstate();
//入棧
lua_pushstring(L,"i am testing lua & c++");
lua_pushnumber(L,123);
//讀棧取值
if(lua_isstring(L,-2))//或if(lua_isstring(L,1))
{
std::cout<<lua_tostring(L,-2)<<std::endl;
}
if(lua_isnumber(L,-1))
{
std::cout<<lua_tonumber(L,-1)<<std::endl;
}
//關(guān)閉state
lua_close(L);
return 0;
}
C++ 調(diào)用 Lua
C++ 可以獲取Lua中的值,可以調(diào)用Lua函數(shù),還可以修改Lua文件。
1. 獲取Lua值
- 使用lua_getglocal來獲取值并將其壓棧(table還需使用lua_getfield)。
- 使用C API lua_toXXX將棧中元素取出轉(zhuǎn)成相應(yīng)類型的值。
2. C調(diào)用Lua函數(shù)
- 使用lua_getglocal來獲取函數(shù)并將其壓棧。
- 如果這個(gè)函數(shù)有參數(shù)的話,就需要依次將函數(shù)的參數(shù)也壓入棧。
- 調(diào)用lua_pcall開始調(diào)用函數(shù),調(diào)用完成以后,會(huì)將返回值壓入棧中。
- 取返回值,調(diào)用完畢。
3. 示例:
#luac.lua
name = "xchen"
version = 1
me = { name = "xchen", gender = "female"}
function add (a,b)
return a+b
end
#include "lua.hpp"
#include <iostream>
using namespace std;
//顯示棧內(nèi)情況
static void stackDump(lua_State* L);
int main()
{
//創(chuàng)建一個(gè)state
lua_State *L = luaL_newstate();
luaL_openlibs(L);//打開lua給我們提供的標(biāo)準(zhǔn)庫(kù)
//讀lua文件
int fret = luaL_dofile(L,"luac.lua");
if(fret)
{
std::cout<<"read lua file error!"<<std::endl;
}
//讀取變量
lua_getglobal(L,"name"); //string to be indexed
std::cout<<"name = "<<lua_tostring(L,-1)<<std::endl;
//讀取數(shù)字
lua_getglobal(L,"version"); //number to be indexed
std::cout<<"version = "<<lua_tonumber(L,-1)<<std::endl;
//讀取表
lua_getglobal(L, "me"); //table to be indexed
if(!lua_istable(L,-1))
{
std::cout<<"error:it is not a table"<<std::endl;
}
//取表中元素
lua_getfield(L, -1 ,"name");
std::cout<<"student name = "<<lua_tostring(L,-1)<<std::endl;
lua_getfield(L,-2,"gender");
std::cout<<"student gender = "<<lua_tostring(L,-1)<<std::endl;
//修改表中元素
lua_pushstring(L, "007");
lua_setfield(L,-4, "name");
lua_getfield(L, -3 ,"name");
std::cout<<"student newName = "<<lua_tostring(L,-1)<<std::endl;
//取函數(shù)
lua_getglobal(L,"add");
lua_pushnumber(L,15);
lua_pushnumber(L,5);
lua_pcall(L,2,1,0);//2-參數(shù)格式,1-返回值個(gè)數(shù),調(diào)用函數(shù),函數(shù)執(zhí)行完,會(huì)將返回值壓入棧中
std::cout<<"5 + 15 = "<<lua_tonumber(L,-1)<<std::endl;
//查看棧
stackDump(L);
//關(guān)閉state
lua_close(L);
return 0;
}
static void stackDump(lua_State* L) {
cout << "\nbegin dump lua stack" << endl;
int i = 0;
int top = lua_gettop(L);
for (i = 1; i <= top; ++i) {
int t = lua_type(L, i);
switch (t) {
case LUA_TSTRING: {
printf("'%s' ", lua_tostring(L, i));
}
break;
case LUA_TBOOLEAN: {
printf(lua_toboolean(L, i) ? "true " : "false ");
}
break;
case LUA_TNUMBER: {
printf("%g ", lua_tonumber(L, i));
}
break;
default: {
printf("%s ", lua_typename(L, t));
}
break;
}
}
cout << "\nend dump lua stack" << endl;
}

Lua 調(diào)用 C++
Lua可以調(diào)用C++的函數(shù),步驟為:
- 將C的函數(shù)包裝成Lua環(huán)境認(rèn)可的函數(shù)
- 將包裝好的函數(shù)注冊(cè)到Lua環(huán)境中
- 像使用普通Lua函數(shù)那樣使用注冊(cè)函數(shù)
1. 包裝C函數(shù)
將被調(diào)用的C函數(shù)從普通的C函數(shù)包裝成Lua_CFunction格式,并需要在函數(shù)中將返回值壓入棧中,并返回返回值個(gè)數(shù):
int (Lua_CFunction*)(lua_state*)
{
// c code // 實(shí)現(xiàn)邏輯功能
// lua_push code // 需要將返回值壓入堆棧
return n; // n為具體的返回值個(gè)數(shù),告訴解釋器,函數(shù)向堆棧壓入幾個(gè)返回值
}
示例:
int add(int a,int b)
{
return a+b;
}
int add(lua_state*L)
{
int a = lua_tonumber(-1);
int b = lua_tonumber(-2);
int sum = a+b;
lua_pushnumber(L,sum);
return 1;
}
2. 注冊(cè)C函數(shù)到Lua環(huán)境
lua_register(L,"Add2Number",add);//將c函數(shù)add注冊(cè)到全局table[Add2Number]中
lua_register是一個(gè)宏,對(duì)應(yīng)兩個(gè)函數(shù):lua_pushfunction(L,f)和lua_setglobal(L,n),將函數(shù)存放在一個(gè)全局table中。
3. 示例(使用就不寫了)
#test.lua
print("Hi! " .. sayHi("xchen"))
#include "lua.hpp"
#include <iostream>
using namespace std;
//C++中定義、實(shí)現(xiàn)函數(shù)sayHi
int sayHi(lua_State *L)
{
//獲取lua函數(shù)中的第一個(gè)參數(shù)
string name = luaL_checkstring(L,1);
//壓棧
lua_pushstring(L,name.data());
return 1;
}
int main()
{
//創(chuàng)建一個(gè)state
lua_State *L = luaL_newstate();
luaL_openlibs(L);
//為L(zhǎng)ua注冊(cè)名為第一個(gè)參數(shù)的函數(shù),實(shí)際上是調(diào)用c++中第三個(gè)參數(shù)名的函數(shù)
lua_register(L, "sayHi" ,sayHi);
//讀lua文件并運(yùn)行Lua code
int fret = luaL_dofile(L,"test.lua");
if(fret)
{
std::cout<<"read lua file error!"<<std::endl;
}
//關(guān)閉state
lua_close(L);
return 0;
}