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;
    }
  • 相关阅读:
    12月19号 init和class
    12月18号 property关键字
    12月18号 属性property
    12月17号 类和对象
    12月17号 OC语言准备
    12月16号 文件操作
    12月16号 双链表
    12月15号 单链表
    2015.12.07 ATM
    2015.12.03 命名规范 输入输出 运算符 条件语句 循环语句
  • 原文地址:https://www.cnblogs.com/iRidescent-ZONE/p/5654315.html
Copyright © 2011-2022 走看看