Lua與C++的交互

參考:

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)后出:

image.png

堆棧索引的方式可是是正數(shù)也可以是負(fù)數(shù),區(qū)別是:正數(shù)索引1永遠(yuǎn)表示棧底,負(fù)數(shù)索引-1永遠(yuǎn)表示棧頂。

Lua??梢源鎯?chǔ)數(shù)字、字符串、表、閉包等,它們最終都用TValue這種數(shù)據(jù)結(jié)構(gòu)來保存:

image.png

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é)論:
  1. lua中,number、boolean、nil、light userdata四種類型的值是直接存在棧上元素里的,和垃圾回收無(wú)關(guān)。
  2. 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值

  1. 使用lua_getglocal來獲取值并將其壓棧(table還需使用lua_getfield)。
  2. 使用C API lua_toXXX將棧中元素取出轉(zhuǎn)成相應(yīng)類型的值。

2. C調(diào)用Lua函數(shù)

  1. 使用lua_getglocal來獲取函數(shù)并將其壓棧。
  2. 如果這個(gè)函數(shù)有參數(shù)的話,就需要依次將函數(shù)的參數(shù)也壓入棧。
  3. 調(diào)用lua_pcall開始調(diào)用函數(shù),調(diào)用完成以后,會(huì)將返回值壓入棧中。
  4. 取返回值,調(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;
}
image.png

Lua 調(diào)用 C++

Lua可以調(diào)用C++的函數(shù),步驟為:

  1. 將C的函數(shù)包裝成Lua環(huán)境認(rèn)可的函數(shù)
  2. 將包裝好的函數(shù)注冊(cè)到Lua環(huán)境中
  3. 像使用普通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;
}
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容