美文网首页
Lua与C++的交互

Lua与C++的交互

作者: 凉夜lrs | 来源:发表于2021-01-20 15:05 被阅读0次

    参考:

    https://blog.csdn.net/v_xchen_v/article/details/77249332
    https://blog.csdn.net/xiaoluoshan/article/details/53155758

    Lua堆栈

    Lua和C++ 的交互机制的基础在于Lua提供了一个虚拟栈,C++ 和Lua之间的所有类型的数据交换都通过这个栈完成。无论何时C想从Lua中调用一个值,被请求的值将会被压入栈,无论何时C想要传递一个值给Lua,首先将整个值压栈,然后就可以在Lua中调用。栈的特点是先进后出:

    image.png

    堆栈索引的方式可是是正数也可以是负数,区别是:正数索引1永远表示栈底,负数索引-1永远表示栈顶。

    Lua栈可以存储数字、字符串、表、闭包等,它们最终都用TValue这种数据结构来保存:

    image.png

    TValue结构对应于lua中的所有数据类型,是一个{值, 类型} 结构,它把值和类型绑在一起,用tt记录value的类型,value是一个联合结构,由Value定义,可以看到这个联合有四个成员:

    • p:可以存一个指针, 实际上是lua中的light userdata结构
    • n:所有的数值存在这里
    • b:Boolean值存在这里
    • gc:gc是一个指针,它可以指向的类型由联合体GCObject定义,诸如table、thread、closure、string等需要内存管理垃圾回收的类型都存在这里
      可以得出如下结论:
    1. lua中,number、boolean、nil、light userdata四种类型的值是直接存在栈上元素里的,和垃圾回收无关。
    2. lua中,string、table、closure、userdata、thread存在栈上元素里的只是指针,,他们都会在生命周期结束后被垃圾回收。

    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函数来人工控制堆栈:

    /*
    ** 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()
    {
        //创建一个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;
        }
    
        //关闭state
        lua_close(L);
        return 0;
    }
    

    C++ 调用 Lua

    C++ 可以获取Lua中的值,可以调用Lua函数,还可以修改Lua文件。

    1. 获取Lua值

    1. 使用lua_getglocal来获取值并将其压栈(table还需使用lua_getfield)。
    2. 使用C API lua_toXXX将栈中元素取出转成相应类型的值。

    2. C调用Lua函数

    1. 使用lua_getglocal来获取函数并将其压栈。
    2. 如果这个函数有参数的话,就需要依次将函数的参数也压入栈。
    3. 调用lua_pcall开始调用函数,调用完成以后,会将返回值压入栈中。
    4. 取返回值,调用完毕。

    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;
    
    //显示栈内情况
    static void stackDump(lua_State* L);
    
    int main()
    {
        //创建一个state
        lua_State *L = luaL_newstate();
        luaL_openlibs(L);//打开lua给我们提供的标准库
    
        //读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;
    
        //读取数字
        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;
    
        //取函数
        lua_getglobal(L,"add");
        lua_pushnumber(L,15);
        lua_pushnumber(L,5);
        lua_pcall(L,2,1,0);//2-参数格式,1-返回值个数,调用函数,函数执行完,会将返回值压入栈中
        std::cout<<"5 + 15 = "<<lua_tonumber(L,-1)<<std::endl;
    
        //查看栈
        stackDump(L);
    
        //关闭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 调用 C++

    Lua可以调用C++的函数,步骤为:

    1. 将C的函数包装成Lua环境认可的函数
    2. 将包装好的函数注册到Lua环境中
    3. 像使用普通Lua函数那样使用注册函数

    1. 包装C函数

    将被调用的C函数从普通的C函数包装成Lua_CFunction格式,并需要在函数中将返回值压入栈中,并返回返回值个数:

    int (Lua_CFunction*)(lua_state*)
    {
        // c code        // 实现逻辑功能
        // lua_push code // 需要将返回值压入堆栈
        return n;        // n为具体的返回值个数,告诉解释器,函数向堆栈压入几个返回值
    }
    

    示例:

    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函数到Lua环境

    lua_register(L,"Add2Number",add);//将c函数add注册到全局table[Add2Number]中
    

    lua_register是一个宏,对应两个函数:lua_pushfunction(L,f)和lua_setglobal(L,n),将函数存放在一个全局table中。

    3. 示例(使用就不写了)

    #test.lua
    print("Hi! " .. sayHi("xchen"))
    
    #include "lua.hpp"
    #include <iostream>
    using namespace std;
    //C++中定义、实现函数sayHi
    int sayHi(lua_State *L)
    {
        //获取lua函数中的第一个参数
        string name = luaL_checkstring(L,1);
        //压栈
        lua_pushstring(L,name.data());
        return 1;
    }
    int main()
    {
        //创建一个state
        lua_State *L = luaL_newstate();
        luaL_openlibs(L);
    
        //为Lua注册名为第一个参数的函数,实际上是调用c++中第三个参数名的函数
        lua_register(L, "sayHi" ,sayHi);
    
        //读lua文件并运行Lua code
        int fret = luaL_dofile(L,"test.lua");
        if(fret)
        {
            std::cout<<"read lua file error!"<<std::endl;
        }
    
        //关闭state
        lua_close(L);
        return 0;
    }
    

    相关文章

      网友评论

          本文标题:Lua与C++的交互

          本文链接:https://www.haomeiwen.com/subject/leohzktx.html