zoukankan      html  css  js  c++  java
  • lua 和 c

    lua程序其实本身并不能执行,它必须依靠c语言编写的解释器来解释执行,或者说解释器为lua脚本的执行,提供了一个运行环境(lua_state),其中包括函数堆栈,内存分配和回收等机制。

    理论上,lua可以使用c提供的功能,如果需要在lua中使用我们特需的功能,我们可以通过编写自己的c库来进行扩展,当然,c也可以通过操作栈的方式来操作lua,这就是lua的强大之处。

    在c中通过lua_state中的栈来操作lua

    首先需要先引入头文件:

    extern "C" {

    #include "lua/lua.h"

    #include "lua/lauxlib.h"

    #include "lua/lualib.h"

    }

     

    创建一个运行lua脚本的环境:

    lua_State *L = luaL_newstate();

    luaL_openlibs(L); // 打开标准库

     

    编译并且调用lua脚本,成功以后就可以用L来操作lua数据了:

    if (luaL_loadfile(L, "./res/lua1.lua") || lua_pcall(L, 0, 0, 0)) { // 如果运行成功,两个函数都返回0,失败返回非0 

            lua_error(L);

        }

     

    获取lua文件中的全局变量:

    lua_getglobal(L, "width"); // 获取变量名为width的全局变量,并压入栈,由于是第一个入栈,可以通过索引1或者-1来引用

    LUA_NUMBER width = lua_tonumber(L, -1); // 获取栈中的width数据

     

    获取lua文件中的table,如果需要获取数组元素,只能使用方式一,并使用lua_pushinteger(L, number):

    方式一:

    lua_getglobal(L, "size"); // 假如全局变量中有一个size的table,其中有"x"和"y"字段,必须先将table入栈

    lua_pushstring(L, "x"); // 将''x"字段入栈

    lua_gettable(L, -2); // 从table中获取"x"字段的值,table现在的索引为-2,并将x的值入栈

    lua_pushsting(L, "y"); // 将"y"字段入栈

    lua_gettable(L, -3); // 获取y的值并入栈,table现在的索引为-3

    方式二:

    lua_getglobal(L, "size");

    lua_getfield(L, -1, "x");

    lua_getfield(L, -2, "y");

    获取数据:

    LUA_NUMBER x = lua_tonumber(L, "x");

    LUA_NUMBER y = lua_tonumber(L, "y");

     

    除了获取lua中的全局变量,我们还可以创建新的全局变量:

    1. 创建一个number类型的全局变量:

    lua_pushnumber(L, 123); // 先将一个number入栈

    lua_setglobal(L, "count"); // 将123设置为全局变量count的值,并将123出栈

    lua_getglobal(L, "count"); // 获取count的值并入栈

    LUA_NUMBER count = lua_tonumber(L, -1);

    2. 创建一个table类型的全局变量:

    lua_newtable(L); // 创建一个新的table,并入栈

    lua_pushnumber(L, 111); // 将一个number入栈

    lua_setfield(L, -2, "x"); // 将111赋值给x字段,并将111出栈

    lua_setglobal(L, "point"); // 将新的table的全局变量名设置为point,table出栈

    lua_getglobal(L, "point"); // 获取新创建的table,point也相当于lua脚本中的一个全局变量了

     

    c调用lua中的函数,函数调用同样需要操作栈来实现:

    lua中的一个函数:

    function lua_func (x, y)

      print("lua_func", x, y)

      return {x = x, y = y}

    end

    c调用lua_func:

    lua_getglobal(L, "lua_func"); // 同样需要先获取函数,并入栈,其实lua函数和普通类型一样,只是函数是一种函数类型,函数执行体就相当于函数类型的值

    lua_pushnumber(L, 12); // 将参数入栈,从前向后的顺序,现在入栈的是将会传递给x的参数

    lua_pushnumber(L, 24); // 将会传递给y的参数

    if (lua_pcall(L, 2, 1, 0)) { // 调用成返回0,否则返回非0值。2代表2个参数,1代表一个返回值,0是只发生错误时候,错误处理函数在堆栈中的索引,因此如果需要进行错误处理,需要先

                  将错误出来函数入栈

      lua_error(L); // 如果调用失败,lua会将一条错误消息入栈

    }

    lua_getfield(L, -1, "x"); // 由于返回值是一个table,所以需要将table中的字段提取出来,并入栈

    lua_getfield(L, -2, "y");

    编写一个辅助函数,可以查看当时的堆栈情况:

    static void dump(lua_State* L) {
        for (int i = 1, top = lua_gettop(L); i <= top; ++i) {
            int type = lua_type(L, i);
            switch (type) {
                case LUA_TSTRING:
                    printf("%d %s 
    ", i, lua_tostring(L, i));
                    break;
                case LUA_TBOOLEAN:
                    printf("%d %s 
    ", i, lua_toboolean(L, i) ? "true" : "false");
                    break;
                case LUA_TNUMBER:
                    printf("%d %f 
    ", i, lua_tonumber(L, i));
                    break;
                case LUA_TTABLE:
                    printf("%d table 
    ", i);
                    break;
                default:
                    printf("%d type:%d %s 
    ", i, type, lua_typename(L, i));
                    break;
            }
        }
    }

    1. 在c中操作lua的全局变量:

    lua文件:

    width = 200
    height = 500
    color = {r = 255, g = 56, b = 123, a = 255}
    point  = {}

    c文件:

      // 创建一个lua状态,用于保存脚本的所有数据
       lua_State *L = luaL_newstate(); luaL_openlibs(L); // 打开标准库
      // 编译并执行lua脚本
    if (luaL_loadfile(L, "./res/lua1.lua") || lua_pcall(L, 0, 0, 0)) { lua_error(L); } lua_getglobal(L, "width"); // 将width,height变量入栈 lua_getglobal(L, "height"); lua_getglobal(L, "color"); // 将color入栈,color是一个数组 lua_getfield(L, 3, "r"); // 将color中的字段r g b a入栈 lua_getfield(L, 3, "g"); lua_getfield(L, 3, "b"); lua_getfield(L, 3, "a"); if (!lua_isnumber(L, 1) || !lua_isnumber(L, 2) || !lua_istable(L, 3)) { lua_error(L); } int width = lua_tonumber(L, 1); // 获取栈中的数据 int height = lua_tonumber(L, 2); int r = lua_tonumber(L, 4); int g = lua_tonumber(L, 5); int b = lua_tonumber(L, 6); int a = lua_tonumber(L, 7); lua_getglobal(L, "point"); // 将point入栈,point是一个数组 lua_pushnumber(L, 100); // 为point数组赋值一个x字段,值尾100 lua_setfield(L, 8, "x"); lua_pushnumber(L, 120); // 赋值一个y字段,值尾120 lua_setfield(L, 8, "y"); lua_getfield(L, 8, "x"); // 将pint的x,y字段入栈 lua_getfield(L, 8, "y"); int px = lua_tonumber(L, -2); // 获取栈中的数据 int py = lua_tonumber(L, -1);

    // 创建一个新的table,全局变量名叫size,并设置size的x,y字段
      lua_newtable(L);

        lua_pushnumber(L, 10);

        lua_setfield(L, -2, "w");

        lua_pushnumber(L, 20);

        lua_setfield(L, -2, "h");

        lua_setglobal(L, "size");

        lua_getglobal(L, "size");

        lua_getfield(L, -1, "w");

        lua_getfield(L, -2, "h");

        
        dump(L);

    2. 在c中操作lua的函数:

    lua文件:

    function lua_func (x, y)
        print("lua_func", x, y)
        return {x = x, y = y}
    end
    
    function lua_error (...)
        print("lua_error")
    end

    c文件:

     lua_State *L = luaL_newstate();
        luaL_openlibs(L);
        
        if (luaL_loadfile(L, "./res/lua1.lua") || lua_pcall(L, 0, 0, 0)) {
            lua_error(L);
        }
        
        lua_getglobal(L, "lua_error");
        
        lua_getglobal(L, "lua_func");
        lua_pushnumber(L, 12);
        lua_pushnumber(L, 24);
        if (lua_pcall(L, 2, 1, 1)) {
            lua_error(L);
        }
        
        lua_getfield(L, -1, "x");
        lua_getfield(L, -2, "y");

    在lua中调用c函数

    先定一个c函数,需要符合lua的对c函数要求,其函数原型是:typedef int (*lua_Function) (lua_State* L):

    static int setPoint(lua_State* L) {

        lua_Number x = luaL_checknumber(L, 1); // 检测lua调用时,传入的参数,此时函数调用的所有参数已经在栈中

        lua_Number y = luaL_checknumber(L, 2);

        lua_newtable(L); // 创建一个table并入栈,作为返回值使用

        lua_pushnumber(L, x); // 为新创建的table赋值

        lua_setfield(L, -2, "x");

        lua_pushnumber(L, y);

        lua_setfield(L, -2, "y");

        

        return 1; // 表明返回值只有一个,解释器会返回距离栈顶的数据,函数调用完成后,会在自动清理栈相关的数据

    }

     

    在调用前,在lua的运行环境中注册一个c函数,相当于告诉lua函数的调用地址:

    lua_pushcfunction(L, setPoint);

    lua_setglobal(L, "set_point");

     

    然后就可以在lua中进行调用:

    local point = set_point(123, 456)

    print(point.x, point.y)

     

    注册c函数的模块,假如在一个模块中有多个函数,其实就是在一个全局的table中设置相关的函数而已

    先定义一个模块中的相关函数:

    static int setPoint(lua_State* L) {

      ...

      return 1;

    }

    static int printPoint(lua_State* L) {

      ...

      return 0;

    }

    static void register_point(lua_State* L) {

        lua_newtable(L);

        lua_pushcfunction(L, setPoint);

        lua_setfield(L, -2, "set_point");

        lua_pushcfunction(L, printPoint);

        lua_setfield(L, -2, "print_point");

        lua_setglobal(L, "Point"); // 为Point模块注册了两个函数

    }

    在lua中使用刚注册的c模块:

    local point = Point.set_point(123, 456)
    Point.print_point(point);

    在c中操作lua中的数组:

    1. void lua_rawgeti(lua_State* L, int index, int key);

    index表示table在栈中的位置,key表示数组的索引,相当于:

    lua_pushnumber(L, key);

    lua_rawget(L, index);

    2. void lua_rawseti(lua_State* L, int index, int key);

    index表示table在栈中的位置,key表示数组的索引,相当于:

    lua_pushnumber(L, key);

    lua_insert(L, -2);

    lua_rawset(L, index);

    实例:

    c代码:

    static int map_table (lua_State* L) {
        luaL_checktype(L, 1, LUA_TTABLE); // 第一个参数是数组
        luaL_checktype(L, 2, LUA_TFUNCTION); // 第二个参数是在遍历数组元素时候,对元素的处理函数,返回新的元素
        
        int n = luaL_len(L, 1); // 获取数组的长度
        for (int i = 1; i <= n; ++i) {
            lua_pushvalue(L, 2); // 拷贝一份函数对象,并压入栈顶,因为函数在执行完成后会自动出栈
            lua_rawgeti(L, 1, i); // 获取数组中索引为i的元素,并压入栈
            lua_call(L, 1, 1); // 调用刚拷贝的函数,函数调用相关谁出栈,返回值会入栈
            lua_rawseti(L, 1, i); // 将栈顶的返回值设置为数组元素的新值,返回值出栈
        }
        
        return 0;
    }

    lua代码:

    local t = {11, 22, 33}
    function lua_map (v)
        print("lua_map", v);
        return v + 1;
    end
    map_table(t, lua_map);
    
    for k, v in ipairs(t) do
        print(k, v)
    end

    c操作lua中的字符串:

    c文件:

    static int lua_split(lua_State* L) {
        const char* str = luaL_checkstring(L, 1);
        const char* split = luaL_checkstring(L, 2);
        
        lua_newtable(L); // 创建一个新表,用于返回值
        const char* e;
        int i = 0;
        while ((e = strchr(str, *split)) != nullptr) { // 找出分隔符的位置
            lua_pushlstring(L, str, e - str); // 将子串入栈
            lua_rawseti(L, -2, ++i); // 添加子串到数组中
            str = e + 1; // 更新字符串指针,准备匹配下一个分隔符的位置
        }
        lua_pushstring(L, str); // 添加最后一个子串
        lua_rawseti(L, -2, i+1);
        
        return 1;
    }

    lua文件:

    local str = "123, 4434, win, dfnoe,, dfdfd,"
    
    local t = split(str, ',');
    for k, v in pairs(t) do
        print(k, v)
    end

    如果需要动态为字符串分配内存空间,是一件即麻烦又有风险的事,如果直接使用lua为我们提供的buffer,就无需担心内存分配和溢出问题:

    luaL_Buffer B; 创建一个buffer

    void luaL_buffinit (lua_State* L, luaL_Buffer* B); 初始化一个buffer

    void luaL_addchar (luaL_Buffer* B, char c); 添加一个字符到buffer中

    void luaL_addlstring (luaL_Buffer* B, const char* str, size_t len); 添加定长的字符串到buffer中

    void luaL_addstring (luaL_Buffer* B, const char* str); 添加一个以“”结尾的字符串到buffer中

    void luaL_pushresult (luaL_Buffer* B); 更新buffer,并将其留在栈上

    void luaL_addvalue (luaL_Buffer* B); 直接将栈顶的值加入缓冲区,必须是字符串和数字

    实例:

    lua文件:

    local str = "i am a student, how about you!"
    
    local str2 = upper(str)
    print("str2:", str2)

    c文件:

    static int lua_upper(lua_State* L) {
        size_t len;
        const char* str = luaL_checklstring(L, -1, &len);
        
        luaL_Buffer buffer;
        luaL_buffinit(L, &buffer);
        
        for (int i = 0; i < len; ++i) {
            luaL_addchar(&buffer, toupper((unsigned char)(str[i])));
        }
        luaL_pushresult(&buffer);
        
        return 1;
    }
  • 相关阅读:
    es5预览本地文件、es6练习代码演示案例
    Java实现 LeetCode 838 推多米诺(暴力模拟)
    Java实现 LeetCode 838 推多米诺(暴力模拟)
    Java实现 LeetCode 838 推多米诺(暴力模拟)
    Java实现 LeetCode 837 新21点(DP)
    Java实现 LeetCode 837 新21点(DP)
    Java实现 LeetCode 837 新21点(DP)
    Java实现 LeetCode 836 矩形重叠(暴力)
    Subversion under Linux [Reprint]
    Subversion how[Reprint]
  • 原文地址:https://www.cnblogs.com/iRidescent-ZONE/p/5654315.html
Copyright © 2011-2022 走看看