• Lua 之 userdata


    Lua 之 userdata

    在Lua中可以通过自定义类型(user data)与C语言代码更高效、更灵活的交互,从而扩展Lua能够表达的类型。

    full userdata

    full userdata 表示一个原始的内存块,可以存储任何东西,它是一个类似于table的object,必须事先创建(也可以被垃圾收集器回收),它也有自己的metatable,它只等于其自身。

    可以为每种full userdata 创建一个唯一的元表,来辨别不同类型的userdata,每当创建了一个userdata后,就用相应的元表(放在Registry中)来标记它,而每得到一个userdata后,就检查它是否拥有正确的元表。

    Lua在释放full userdata所关联的内存时,若发现userdata对应的元表还有__gc元方法,则会调用这个方法,并以userdata自身作为参数传入。利用该特性,可以再回收userdata的同时,释放与此userdata相关联的资源。

    创建一个full userdata:

    void *lua_newuserdata (lua_State *L, size_t size);

    lua_newuserdata 分配指定大小的内存块,然后将其入栈,并返回内存块地址。

    Lua没有为user data预定义任何操作,所以,对user data的操作接口仍由C接口提供,并注册到Lua环境中,供Lua使用。

    下面是使用user data实现布尔数组的一个例子:

    // foo.c
    
    #include <lua.h>
    #include <lauxlib.h>
    #include <lualib.h>
    #include <limits.h>
    
    #define BITS_PER_WORD (CHAR_BIT * sizeof(int))
    #define I_WORD(i)     ((unsigned int)(i))/BITS_PER_WORD
    #define I_BIT(i)      (1 << ((unsigned int)(i)%BITS_PER_WORD))
    
    typedef struct NumArray {
        int size;
        unsigned int values[1];
    } NumArray;
    
    int newArray(lua_State* L)
    {
        int i, n;
    
        n = luaL_checkint(L,1);
    
        luaL_argcheck(L, n >= 1, 1, "invalid size.");
    
        size_t nbytes = sizeof(NumArray) + I_WORD(n - 1) * sizeof(int);
    
        NumArray* a = (NumArray*) lua_newuserdata(L,nbytes);
    
        a->size = n;
    
        for (i = 0; i < I_WORD(n - 1); ++i)
            a->values[i] = 0;
    
        luaL_getmetatable(L, "myarray");
    
        lua_setmetatable(L, -2);
    
        return 1;
    }
    
    int setArray(lua_State* L)
    {
        //1. Lua传给该函数的第一个参数必须是userdata,该对象的元表也必须是注册表中和myarray关联的table。
        //否则该函数报错并终止程序。
        NumArray* a = (NumArray*)luaL_checkudata(L,1,"myarray");
        int index = luaL_checkint(L,2) - 1;
    
        luaL_checkany(L,3);     // there are 3 arguments
        luaL_argcheck(L,a != NULL,1,"'array' expected.");
        luaL_argcheck(L,0 <= index && index < a->size,2,"index out of range.");
    
        if (lua_toboolean(L,3))
            a->values[I_WORD(index)] |= I_BIT(index);
        else
            a->values[I_WORD(index)] &= ~I_BIT(index);
    
        return 0;
    }
    
    int getArray(lua_State* L)
    {
        NumArray* a = (NumArray*)luaL_checkudata(L,1,"myarray");
        int index = luaL_checkint(L,2) - 1;
        luaL_argcheck(L, a != NULL, 1, "'array' expected.");
        luaL_argcheck(L, 0 <= index && index < a->size,2,"index out of range");
        lua_pushboolean(L,a->values[I_WORD(index)] & I_BIT(index));
        return 1;
    }
    
    int getSize(lua_State* L)
    {
        NumArray* a = (NumArray*)luaL_checkudata(L,1,"myarray");
        luaL_argcheck(L,a != NULL,1,"'array' expected.");
        lua_pushinteger(L,a->size);
        return 1;
    }
    
    int array2string(lua_State* L)
    {
        NumArray* a = (NumArray*)luaL_checkudata(L,1,"myarray");
        lua_pushfstring(L,"array(%d)",a->size);
        return 1;
    }
    
    static luaL_Reg arraylib_f [] = {
        {"new", newArray},
        {NULL, NULL}
    };
    
    static luaL_Reg arraylib_m [] = {
        {"set", setArray},
        {"get", getArray},
        {"size", getSize},
        {"__tostring", array2string}, //print(a)时Lua会调用该元方法。
        {NULL, NULL}
    };
    
    int luaopen_foo(lua_State* L)
    {
        //1. 创建元表,并将该元表指定给newArray函数新创建的userdata。在Lua中userdata也是以table的身份表现的。
        //这样在调用对象函数时,可以通过验证其metatable的名称来确定参数userdata是否合法。
        luaL_newmetatable(L,"myarray");
        lua_pushvalue(L,-1);
    
        //2. 为了实现面对对象的调用方式,需要将元表的__index字段指向自身,同时再将arraylib_m数组中的函数注册到
        //元表中,之后基于这些注册函数的调用就可以以面向对象的形式调用了。
        //lua_setfield在执行后会将栈顶的table弹出。
        lua_setfield(L, -2, "__index");
    
        //将这些成员函数注册给元表,以保证Lua在寻找方法时可以定位。NULL参数表示将用栈顶的table代替第二个参数。
        luaL_register(L, NULL, arraylib_m);
    
        //这里只注册的工厂方法。
        luaL_register(L,"testuserdata",arraylib_f);
    
        return 1;
    }

    编译为C模块,方便Lua调用:

    gcc foo.c -shared -fPIC -o foo.so  -llua-5.1 -I /usr/local/include/ 

    在Lua中使用上面定义的布尔数组:

    require "foo"
    
    local array = testuserdata.new(100)
    
    print(array:size())     -- 100
    
    for i=1,100 do
        array:set(i, i%5 == 0)
    end
    
    for i=1,100 do
        print(array:get(i))
    end

    在Lua中,user data是以table的形式使用。


    light userdata


    light userdata仅仅表示的是C指针(void*)

    light userdata 就像number类型一样,不需要创建(那自然也不会被垃圾收集器回收),也没有元表,它与所有表示同一指针的light userdata都相等;

    创建一个light userdata:

    void lua_pushlightuserdata (lua_State *L, void *p);

     

  • 相关阅读:
    namespace
    kubernetes环境搭建
    verdaccio私有仓库搭建
    mysql中间件proxysql
    MySQL存储引擎
    关于分布式系统
    转载:ETL讲解
    用原生JS 写Web首页轮播图
    javascript——记忆小便签
    转载:JavaScript 的 this 原理
  • 原文地址:https://www.cnblogs.com/chenny7/p/4077364.html
Copyright © 2020-2023  润新知