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);

 

 

 

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM