• Lua与C/C++互操作


    Lua通过一个“虚拟栈”(Lua State)与C/C++程序进行数据交互。

    当在Lua里面操作这个栈的时候,每次操作的都是栈的顶部。而Lua的C API则有更多的控制权,可非常灵活地操纵这个栈的任意位置。

    c/c++调用lua实际上是:c/c++获取全局表中的lua变量或函数,然后把数据放入栈中,lua再去栈中取数据,然后返回数据对应的值到栈顶,再由栈顶返回c++。

    lua调c/c++也一样:先将c/c++的函数注册到lua解释器中,然后lua再去调用它们。

    栈(Lua State)

    注1:绝对索引是从1开始由栈底到栈顶依次增长的

    注2:相对索引是从-1开始由栈顶到栈底依次递减的(在lua API函数内部会将相对索引转换为绝对索引)

    注3:上图栈的容量为7,栈顶绝对索引为5,有效索引范围为:[1, 5],可接受索引范围为:[1, 7]

    注4:Lua虚拟机指令里寄存器索引是从0开始的,而Lua API里的栈索引是从1开始的,因此当需要把寄存器索引当成栈索引使用时,要进行+1

    栈是FILO(先进后出)的。栈中每个元素为一个TValue类型。64位系统下,sizeof(TValue)=16sizeof(Value)=8

    其中boolean(布尔)、integer(整型)、double(浮点)、light userdatalight c function是直接存在栈上的

    TStringUdataClosureTablelua state在栈上只是一个指针,都为GC类型,当没有被引用时会被lua的GC系统自动回收,具体结构如下:

    将不同类型的变量压栈

    static int Square(lua_State* L) 
    {
        double d = lua_tonumber(L, 1); /* get argument */
        lua_pushnumber(L, d*d); /* push result */
        return 1; /* number of results */
    }
    
    #include <string.h>
    static int CClosureStrLen(lua_State* L)
    {
        const char* upval = lua_tostring(L, lua_upvalueindex(1));// get first upvalue
    
        lua_pushnumber(L, (int)strlen(upval)); /* push result */
        return 1;
    }
    
    typedef struct Rect
    {
        float w, h;
    } Rect;
    
    Rect g_rc;
    
    
    /************** 测试代码 **************/
    lua_settop(L, 0); //把栈上所有元素移除
    
    lua_pushnil(L); // 把nil压栈
    
    lua_pushboolean(L, 1); // 把布尔值true压栈
    
    lua_pushinteger(L, 35);// 把整型数35压栈
    
    lua_pushnumber(L, 12.8);// 把浮点数12.8压栈
    
    lua_pushcfunction(L, Square);// 把c函数Square压栈
    
    lua_pushlightuserdata(L, &g_rc);// 把全局变量Rect g_rc的指针压栈
    
    lua_pushstring(L, "Hello!");// 把字符串Hello!压栈
    
    lua_newtable(L);// 创建一个空表并压栈
    
    lua_newthread(L);// 创建一个Thread并压栈
    
    lua_newuserdata(L, sizeof(Rect)); //创建一个内存块为Rect的full userdata,并压栈
    
    lua_pushstring(L, "cclosure upvalue");// 创建一个字符串upvalue,内容为cclosure upvalue,并压栈
    lua_pushcclosure(L, CClosureStrLen, 1);// 创建有1个upvalue的c函数闭包(upvalue为栈顶元素),成功后将栈顶1个upvalue出栈,并将自己入栈

    打印栈

    #include "lobject.h"
    #include "lstate.h"
    
    const TValue luaO_nilobject_ = { NILCONSTANT };
    
    /* value at a non-valid index */
    #define NONVALIDVALUE        cast(TValue *, luaO_nilobject)
    /* test for pseudo index */
    #define ispseudo(i)        ((i) <= LUA_REGISTRYINDEX)
    
    static TValue* index2addr(lua_State* L, int idx) {
        CallInfo* ci = L->ci;
        if (idx > 0) {
            TValue* o = ci->func + idx;
            api_check(L, idx <= ci->top - (ci->func + 1), "unacceptable index");
            if (o >= L->top) return NONVALIDVALUE;
            else return o;
        }
        else if (!ispseudo(idx)) {  /* negative index */
            api_check(L, idx != 0 && -idx <= L->top - (ci->func + 1), "invalid index");
            return L->top + idx;
        }
        else if (idx == LUA_REGISTRYINDEX)
            return &G(L)->l_registry;
        else {  /* upvalues */
            idx = LUA_REGISTRYINDEX - idx;
            api_check(L, idx <= MAXUPVAL + 1, "upvalue index too large");
            if (ttislcf(ci->func))  /* light C function? */
                return NONVALIDVALUE;  /* it has no upvalues */
            else {
                CClosure* func = clCvalue(ci->func);
                return (idx <= func->nupvalues) ? &func->upvalue[idx - 1] : NONVALIDVALUE;
            }
        }
    }
    
    void DumpLuaStack(lua_State* L)
    {
        int ad = -1;
        int i = lua_gettop(L);
        printf("
    ----------------  Stack Dump ----------------
    ");
        while (i) {
            StkId o = index2addr(L, i);
            int t = lua_type(L, i);
            switch (t) {
            case LUA_TSTRING:
                printf("%d[%d]:'%s'
    ", i, ad, lua_tostring(L, i));
                break;
            case LUA_TBOOLEAN:
                printf("%d[%d]: %s
    ", i, ad, lua_toboolean(L, i) ? "true" : "false");
                break;
            case LUA_TNUMBER:
                printf("%d[%d]: %g
    ", i, ad, lua_tonumber(L, i));
                break;
            case LUA_TFUNCTION:
                if (ttislcf(o)) {
                    printf("%d[%d]: c %p
    ", i, ad, fvalue(o)); // lua_CFunction
                }
                else if (ttisCclosure(o))
                {
                    printf("%d[%d]: c closure %p
    ", i, ad, clCvalue(o)->f); // CClosure
                }
                else if (ttisLclosure(o))
                {
                    Proto* pinfo = clLvalue(o)->p;
                    printf("%d[%d]: lua closure %s[%d,%d]
    ", i, ad, getstr(pinfo->source), pinfo->linedefined, pinfo->lastlinedefined);
                }
                break;
            case LUA_TTABLE:
                printf("%d[%d]: table:%p
    ", i, ad, hvalue(o));
                break;
            case LUA_TLIGHTUSERDATA:
                printf("%d[%d]: light userdata:%p
    ", i, ad, pvalue(o));
                break;
            case LUA_TUSERDATA:
                printf("%d[%d]: full userdata:%p
    ", i, ad, uvalue(o));
                break;
            case LUA_TTHREAD:
                printf("%d[%d]: thread:%p
    ", i, ad, thvalue(o));
                break;
            default: printf("%d[%d]: %s
    ", i, ad, lua_typename(L, t)); break;
            }
            i--; ad--;
        }
        printf("---------------------------------------------
    ");
    }

    上面示例的不同类型变量压栈的最终结果如下:

    C++解释执行lua文件

    Test1.lua内容如下:

    print("Hello, Lua!")

    LuaTest.cpp代码如下:

    #include <stdio.h>
    extern "C" {
    #include "lua.h"
    #include "lualib.h"
    #include "lauxlib.h"
    }
    
    int main(int argc, char *argv[])
    {
        lua_State* L = lua_open(); // 创建一个lua虚拟机
        luaL_openlibs(L);  // 打开状态机L中的所有 Lua 标准库
        luaL_dofile(L, "Test1.lua"); // 载入Test.lua并解释执行
        lua_close(L); // 关闭lua虚拟机
        return 0;
    }

    注:luaL_dofile函数实际上是执行了luaL_loadfile来加载lua文件,加载成功之后会编译该代码块为一个Lua闭包放置在栈顶,并返回0。由于返回值为假,会继续调用lua_pcall来执行该Lua闭包,最后把该Lua闭包弹出栈。

    #define luaL_dofile(L, fn) 
        (luaL_loadfile(L, fn) || lua_pcall(L, 0, LUA_MULTRET, 0))

    C++访问lua

    C++调用lua函数

    Test2.lua内容如下:

    function add(x,y)
        return x + y
    end
    
    mytable={}
    
    function mytable.StaticFunc()
        print("mytable.StaticFunc called.")
    end
    
    function mytable:Func()
        print("mytable:Func self:", self)
    end

    LuaTest.cpp代码如下:

    #include <stdio.h>
    extern "C" {
    #include "lua.h"
    #include "lualib.h"
    #include "lauxlib.h"
    }
    
    int main(int argc, char *argv[])
    {
        lua_State* L = lua_open();
        luaL_openlibs(L);
        luaL_dofile(L, "Test2.lua");
    
        lua_getglobal(L, "add"); // 获取全局函数add,并压入栈顶
        lua_pushinteger(L, 30); // 将整型的值30压入栈顶
        lua_pushinteger(L, 50); // 将整型的值50压入栈顶
        lua_call(L, 2, 1); // 对栈顶的30和50执行add函数调用,执行完后将30, 50,add弹出栈,将结果80压栈  注:2为参数个数,1为返回值个数
    
        int sum = (int)lua_tointeger(L, -1); // 将栈顶80赋值给sum变量
        lua_pop(L, 1); // 从栈顶弹出1个元素
    
        printf("The sum is: %d
    ", sum);
    
        // 调用mytable表的静态函数
        lua_getglobal(L, "mytable"); // 将名为mytable的全局table变量的值压栈
        lua_pushstring(L, "StaticFunc"); // 将函数名为StaticFunc压栈
        lua_gettable(L, -2); // 从索引为-2处的表中,读取key(在栈顶处)为StaticFunc的函数名  读取成功后,将key出栈,并将读取到的函数名入栈
        lua_call(L, 0, 0); // 执行完后将StaticFunc弹出栈  注: 第一个0表示参数个数为0,第二个0表示无返回值
    
        // 调用mytable表的成员函数  采用新方法获取函数名
        lua_getfield(L, -1, "Func");// 从索引为-1处的表中,读取key(在栈顶处)为Func的函数名  成功后将读取到的函数名入栈
        lua_pushvalue(L, -2); // 将索引为-2处的表复制一份并压入栈顶
        lua_call(L, 1, 0); // 执行完后将Func弹出栈  注: 1表示参数个数,即self指针,为当前table,第二个0表示无返回值
    
        lua_close(L);
        return 0;
    } 

    C++读写Lua中的全局变量

    Test3.lua内容如下:

    sayhi="Hello Lua!"
    mytable={sex = "male", age=18}

    LuaTest.cpp代码如下:

    #include <stdio.h>
    extern "C" {
    #include "lua.h"
    #include "lualib.h"
    #include "lauxlib.h"
    }
    
    int main(int argc, char *argv[])
    {
        lua_State* L = lua_open();
        luaL_openlibs(L);
        
        luaL_dofile(L, "Test3.lua");
        
        lua_settop(L, 0); //把栈上所有元素移除
    
        // ******读取名为sayhi的字符串全局变量的值******
        lua_getglobal(L, "sayhi");// 将名为sayhi的全局string变量的值压栈
    
        if (lua_isstring(L, -1) != 0)
        {
            const char* str = lua_tostring(L, -1); // 从栈顶读取字符串内容
            printf("str: %s
    ", str);
        }
    
        // ******修改名为sayhi的字符串全局变量的值******
        lua_pop(L, 1); // 从栈顶弹出1个元素
    
        lua_pushstring(L, "Welcome Lua!"); // 将Welcome Lua!字符串压栈
        lua_setglobal(L, "sayhi");// 将栈顶的元素设置给全局变量sayhi
    
        lua_getglobal(L, "sayhi");
        const char* str2 = lua_tostring(L, -1); // str2为Welcome Lua!
    
        // ******读取名为mytable的table全局变量中的内容******
        lua_getglobal(L, "mytable"); // 将名为mytable的全局table变量的值压栈
        lua_pushstring(L, "sex"); // 将字符串sex压栈
        lua_gettable(L, -2); // 从索引为-2处的表中,读取key(在栈顶处)为sex的元素  读取成功后,将key出栈,并将读取到的元素入栈
    
        // 另外一种方式:读取table中的某个key的值
        lua_getfield(L, -2, "age");// 从索引为-2处的表中,读取key(在栈顶处)为age的元素  成功后将读取到的元素入栈
        int age = (int)lua_tointeger(L, -1); // 从栈顶读取age 为18
        const char* sex = lua_tostring(L, -2); // 从索引为-2处读取sex  为male
    
        // ******修改名为mytable的table全局变量中的内容******
        lua_pop(L, 2); // 从栈顶弹出2个元素
        lua_pushstring(L, "age"); // 将字符串age压栈
        lua_pushinteger(L, 20); // 将整数20压栈
        lua_settable(L, -3); // 设置索引为-3处的表的key(在栈顶下一个,即age)对应的value为20(在栈顶处)  设置成功后,将栈顶的2个元素都出栈
        
        // 另外一种方式:设置table中的某个key的值
        lua_pushstring(L, "female"); // 将字符串female压栈
        lua_setfield(L, -2, "sex"); // 设置索引为-3处的表的key为sex对应的value为female(在栈顶处) 设置成功后,将栈顶的female出栈
    
        lua_getfield(L, -1, "sex"); // 从索引为-1处的表中,读取key(在栈顶处)为sex的元素  成功后将读取到的元素入栈
        lua_getfield(L, -2, "age"); // 从索引为-2处的表中,读取key(在栈顶处)为age的元素  成功后将读取到的元素入栈
        int age2 = (int)lua_tointeger(L, -1); // 从栈顶读取age2 为20
        const char* sex2 = lua_tostring(L, -2); // 从索引为-2处读取sex  为female
    
        lua_close(L);
        return 0;
    }

    Lua访问C++(无dll)

    Lua调用C++全局函数

    Test4.lua内容如下:

    local avg, sum = average(10,20,30,40,50)
    print("The average is ", avg)
    print("The sum is ", sum)

    LuaTest.cpp代码如下:

    #include <stdio.h>
    extern "C" {
    #include "lua.h"
    #include "lualib.h"
    #include "lauxlib.h"
    }
    
    static int Average(lua_State *L)
    {
        int n = lua_gettop(L);  // 获取栈上元素的个数
        double sum = 0;
        for (int i = 1; i <= n; ++i)
        {
            sum += lua_tonumber(L, i); // 依次去除索引为1到n的元素,并累加到sum
        }
        lua_pushnumber(L, sum / n); // 将sum/n计算得到平均值压栈
        lua_pushnumber(L, sum);  // 将sum压栈
    
        return 2;  // 表明有2个返回值
    }
    
    int main(int argc, char *argv[])
    {
        lua_State* L = lua_open();
        luaL_openlibs(L);
        
        lua_register(L, "average", Average); // 将c++的Average函数注册成lua中的average方法
        
        luaL_dofile(L, "Test4.lua");
    
        lua_close(L);
        return 0;
    }

    注:lua_register先用lua_pushcfunction把在c++函数压入栈中,然后调用lua_setglobal来设置栈顶的c++函数对应lua函数名,最后弹出栈顶的c++函数。

    这样就可以把lua函数和c++函数建立绑定关系,使得在后续的lua脚本中使用lua函数名来调用该c++函数。

    #define lua_register(L,n,f) (lua_pushcfunction(L, (f)), lua_setglobal(L, (n)))

    Lua调用C++成员函数

    下面示例也是MyArray的userdata的实现

    Test5.lua内容如下:

    function MyArrayTest(size)
      local a1 = myarray.new(size)
      myarray.set(a1, 1, 25.6)
      print(myarray.size(a1))
      print(myarray.get(a1, 1))
    end

    LuaTest.cpp代码如下:

    #include <stdio.h>
    extern "C" {
    #include "lua.h"
    #include "lualib.h"
    #include "lauxlib.h"
    }
    
    class MyArray
    {
    public:
        int GetSize() { return size; }
        void SetSize(int insize) { size = insize; }
    
        double GetAt(int index) { return values[index]; }
        void SetAt(int index, double value) { values[index] = value; }
    private:
        int size;
        double values[1];
    };
    
    static int newarray(lua_State* L)
    {
        int n = (int)luaL_checkinteger(L, 1); // 检查栈上索引为1处的元素是否为整型,并返回该元素
        size_t nbytes = sizeof(MyArray) + (n - 1) * sizeof(double);
        MyArray* a = (MyArray*)lua_newuserdata(L, nbytes); // 创建一个userdata,并压栈
        a->SetSize(n);// 设置数组的大小
        return 1;//表示有1个返回值
    }
    
    static int setarray(lua_State* L)
    {
        MyArray* a = (MyArray*)lua_touserdata(L, 1);// 获取栈上索引为1处的元素,并转换为userdata指针
        int index = (int)luaL_checkinteger(L, 2);// 检查栈上索引为1处的元素是否为整型,并返回该元素
        double value = luaL_checknumber(L, 3);// 检查栈上索引为1处的元素是否为number类型,并返回该元素
    
        luaL_argcheck(L, a != nullptr, 1, "'array' expected");
        luaL_argcheck(L, 1 <= index && index <= a->GetSize(), 2, "index out of range");
        a->SetAt(index-1, value); // 设置数组索引为index-1处的值为value
    
        return 0; //表示没有返回值
    }
    
    static int getarray(lua_State* L)
    {
        MyArray* a = (MyArray*)lua_touserdata(L, 1);// 获取栈上索引为1处的元素,并转换为userdata指针
        int index = (int)luaL_checkinteger(L, 2);// 检查栈上索引为1处的元素是否为整型,并返回该元素
    
        luaL_argcheck(L, a != nullptr, 1, "'array' expected");
        luaL_argcheck(L, 1 <= index && index <= a->GetSize(), 2, "index out of range");
        double value = a->GetAt(index-1);// 获取数组索引为index-1处的值
    
        lua_pushnumber(L, value); // 将获取的值压栈
    
        return 1;//表示有1个返回值
    }
    
    static int getsize(lua_State* L)
    {
        MyArray* a = (MyArray*)lua_touserdata(L, 1); // 获取栈上索引为1处的元素,并转换为userdata指针
        luaL_argcheck(L, a != nullptr, 1, "'array' expected");
        lua_pushnumber(L, a->GetSize());    // 获取数组的大小并压栈
    
        return 1;//表示有1个返回值
    }
    
    static const struct luaL_Reg MyArrayLib[] = {
        {"new", newarray},
        {"set", setarray},
        {"get", getarray},
        {"size", getsize},
        {nullptr, nullptr}
    };
    
    int luaopen_MyArray(lua_State* L)
    {
        luaL_newlib(L, MyArrayLib);
        return 1;
    }
    
    int main(int argc, char *argv[])
    {
        lua_State* L = lua_open();
        luaL_openlibs(L);
        luaL_requiref(L, "myarray", luaopen_MyArray, 1); // 将MyArray相关方法注册到全局表中,lua中的名为myarray
        
        luaL_dofile(L, "Test5.lua");
    
        lua_getglobal(L, "MyArrayTest"); // 将函数名MyArrayTest压栈
        lua_pushinteger(L, 1000); // 传入MyArray的size为1000 压栈
        lua_call(L, 1, 0); // 执行完后将MyArrayTest弹出栈  注: 1表示参数个数,第二个0表示无返回值
    
        lua_close(L);
        return 0;
    } 

    注:luaL_newlib中一共包含3个函数

    luaL_checkversion: 检查Lua版本是否一致
    luaL_newlibtable: 创建一个table并压入栈顶,这其实也是一个宏,实际上调用的是lua_createtable
    luaL_setfuncs: 将luaL_Reg函数列表设置给刚刚压入栈的表。luaL_Reg函数列表是一个名字(key)和函数指针(value)组成的数组。

    #define luaL_newlib(L,l)  
      (luaL_checkversion(L), luaL_newlibtable(L,l), luaL_setfuncs(L,l,0))

    Lua访问C++(独立dll模块)

    Test6.lua内容如下:

    local myarray=require "myLualib"
    
    local avg, sum = average(10,20,30,40,50)
    print("The average is ", avg)
    print("The sum is ", sum)
    
    function MyArrayTest(size)
      local a1 = myarray.new(size)
      myarray.set(a1, 1, 25.6)
      print(myarray.size(a1))
      print(myarray.get(a1, 1))
    end
    
    
    MyArrayTest(1000)

    注1:local myarray=require "myLualib"等价于以下代码

    local myarray = nil
    local fnluaopen_myLualib = package.loadlib("myLualib.dll","luaopen_myLualib") -- 查找myLualib.dll中名为luaopen_myLualib函数
    if fnluaopen_myLualib ~= nil then
        return myarray=fnluaopen_myLualib() -- 执行luaopen_myLualib函数
    end

    注2:为了保证能找到myLublib.dll,可将dll文件复制到Test5.lua文件的目录下

    myLualib.dll模块

    /*************************** myLualib.h ***************************/
    #pragma once
    extern "C" {
    #include "lua.h"
    #include "lualib.h"
    #include "lauxlib.h"
    }
    
    #ifdef MYLUALIB_EXPORTS
    #define MYLUALIB_API __declspec(dllexport)
    #else
    #define MYLUALIB_API __declspec(dllimport)
    #endif
     
    extern "C" MYLUALIB_API int luaopen_myLualib(lua_State *L);//定义导出函数
    
    
    /*************************** myLualib.cpp ***************************/
    #include "myLualib.h"
    static int Average(lua_State *L)
    {
        int n = lua_gettop(L);  // 获取栈上元素的个数
        double sum = 0;
        for (int i = 1; i <= n; ++i)
        {
            sum += lua_tonumber(L, i); // 依次去除索引为1到n的元素,并累加到sum
        }
        lua_pushnumber(L, sum / n); // 将sum/n计算得到平均值压栈
        lua_pushnumber(L, sum);  // 将sum压栈
    
        return 2;  // 表明有2个返回值
    }
    
    class MyArray
    {
    public:
        int GetSize() { return size; }
        void SetSize(int insize) { size = insize; }
    
        double GetAt(int index) { return values[index]; }
        void SetAt(int index, double value) { values[index] = value; }
    private:
        int size;
        double values[1];
    };
    
    static int newarray(lua_State* L)
    {
        int n = (int)luaL_checkinteger(L, 1); // 检查栈上索引为1处的元素是否为整型,并返回该元素
        size_t nbytes = sizeof(MyArray) + (n - 1) * sizeof(double);
        MyArray* a = (MyArray*)lua_newuserdata(L, nbytes); // 创建一个userdata,并压栈
        a->SetSize(n);// 设置数组的大小
        return 1;//表示有1个返回值
    }
    
    static int setarray(lua_State* L)
    {
        MyArray* a = (MyArray*)lua_touserdata(L, 1);// 获取栈上索引为1处的元素,并转换为userdata指针
        int index = (int)luaL_checkinteger(L, 2);// 检查栈上索引为1处的元素是否为整型,并返回该元素
        double value = luaL_checknumber(L, 3);// 检查栈上索引为1处的元素是否为number类型,并返回该元素
    
        luaL_argcheck(L, a != nullptr, 1, "'array' expected");
        luaL_argcheck(L, 1 <= index && index <= a->GetSize(), 2, "index out of range");
        a->SetAt(index-1, value); // 设置数组索引为index-1处的值为value
    
        return 0; //表示没有返回值
    }
    
    static int getarray(lua_State* L)
    {
        MyArray* a = (MyArray*)lua_touserdata(L, 1);// 获取栈上索引为1处的元素,并转换为userdata指针
        int index = (int)luaL_checkinteger(L, 2);// 检查栈上索引为1处的元素是否为整型,并返回该元素
    
        luaL_argcheck(L, a != nullptr, 1, "'array' expected");
        luaL_argcheck(L, 1 <= index && index <= a->GetSize(), 2, "index out of range");
        double value = a->GetAt(index-1);// 获取数组索引为index-1处的值
    
        lua_pushnumber(L, value); // 将获取的值压栈
    
        return 1;//表示有1个返回值
    }
    
    static int getsize(lua_State* L)
    {
        MyArray* a = (MyArray*)lua_touserdata(L, 1); // 获取栈上索引为1处的元素,并转换为userdata指针
        luaL_argcheck(L, a != nullptr, 1, "'array' expected");
        lua_pushnumber(L, a->GetSize());    // 获取数组的大小并压栈
    
        return 1;//表示有1个返回值
    }
    
    static const struct luaL_Reg MyArrayLib[] = {
        {"new", newarray},
        {"set", setarray},
        {"get", getarray},
        {"size", getsize},
        {nullptr, nullptr}
    };
    
    int luaopen_myLualib(lua_State* L)
    {
        lua_register(L, "average", Average); // 将c++的Average函数注册成lua中的average方法
        
        luaL_newlib(L, MyArrayLib);
        return 1;
    }

    注:MyLuaLib模块不要直接集成lua虚拟机的源代码,应导入lua虚拟机的dll来使用,否则会报如下错误

    multiple Lua VMs detected 

    exe宿主程序

    #include <stdio.h>
    extern "C" {
    #include "lua.h"
    #include "lualib.h"
    #include "lauxlib.h"
    }
    
    int main(int argc, char *argv[])
    {
        lua_State* L = lua_open();
        luaL_openlibs(L);
        
        luaL_dofile(L, "Test6.lua");
    
        lua_close(L);
        return 0;
    }

    参考

    游戏开发实现C++与Lua交互!

    lua教程(runoob) 

    Lua 5.3 Reference Manual(官方英文版)

    lua5.3参考手册(runoob)(中文版)

    Lua和C++交互详细总结

    Lua和C++交互总结(很详细)

  • 相关阅读:
    第七十四节,css边框与背景
    php中strlen和{}的效率对比
    for循环++i效率
    php优化技巧
    mysql中group_concat函数用法
    mysql引擎互转问题
    array_column php 函数
    php-mysql结果集函数比较
    javaweb 要学习的东西
    Java 泛型和类型安全的容器
  • 原文地址:https://www.cnblogs.com/kekec/p/12879896.html
Copyright © 2020-2023  润新知