28.1 Userdata

我们首先关心的是如何在Lua中表示数组的值。Lua为这种情况提供专门提供一个基本的类型:userdata。一个userdatum提供了一个在Lua中没有预定义操作的raw内存区域。

Lua API提供了下面的函数用来创建一个userdatum

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

lua_newuserdata函数按照指定的大小分配一块内存,将对应的userdatum放到栈内,并返回内存块的地址。如果出于某些原因你需要通过其他的方法分配内存的话,很容易创建一个指针大小的userdatum,然后将指向实际内存块的指针保存到userdatum里。我们将在下一章看到这种技术的例子。

使用lua_newuserdata函数,创建新数组的函数实现如下:

static int newarray (lua_State *L) {

    int n = luaL_checkint(L, 1);

    size_t nbytes = sizeof(NumArray) + (n - 1)*sizeof(double);

    NumArray *a = (NumArray *)lua_newuserdata(L, nbytes);

    a->size = n;

    return 1;  /* new userdatum is already on the stack */

}

(函数luaL_checkint是用来检查整数的luaL_checknumber的变体)一旦newarrayLua中被注册之后,你就可以使用类似a = array.new(1000)的语句创建一个新的数组了。

为了存储元素,我们使用类似array.set(array, index, value)调用,后面我们将看到如何使用metatables来支持常规的写法array[index] = value。对于这两种写法,下面的函数是一样的,数组下标从1开始:

static int setarray (lua_State *L) {

    NumArray *a = (NumArray *)lua_touserdata(L, 1);

    int index = luaL_checkint(L, 2);

    double value = luaL_checknumber(L, 3);

 

    luaL_argcheck(L, a != NULL, 1, "`array' expected");

 

    luaL_argcheck(L, 1 <= index && index <= a->size, 2,

              "index out of range");

 

    a->values[index-1] = value;

    return 0;

}

luaL_argcheck函数检查给定的条件,如果有必要的话抛出错误。因此,如果我们使用错误的参数调用setarray,我们将得到一个错误信息:

array.set(a, 11, 0)

--> stdin:1: bad argument #1 to 'set' ('array' expected)

下面的函数获取一个数组元素:

static int getarray (lua_State *L) {

    NumArray *a = (NumArray *)lua_touserdata(L, 1);

    int index = luaL_checkint(L, 2);

 

    luaL_argcheck(L, a != NULL, 1, "'array' expected");

 

    luaL_argcheck(L, 1 <= index && index <= a->size, 2,

                         "index out of range");

 

    lua_pushnumber(L, a->values[index-1]);

    return 1;

}

我们定义另一个函数来获取数组的大小:

static int getsize (lua_State *L) {

    NumArray *a = (NumArray *)lua_touserdata(L, 1);

    luaL_argcheck(L, a != NULL, 1, "`array' expected");

    lua_pushnumber(L, a->size);

    return 1;

}

最后,我们需要一些额外的代码来初始化我们的库:

static const struct luaL_reg arraylib [] = {

    {"new", newarray},

    {"set", setarray},

    {"get", getarray},

    {"size", getsize},

    {NULL, NULL}

};

 

int luaopen_array (lua_State *L) {

    luaL_openlib(L, "array", arraylib, 0);

    return 1;

}

这儿我们再次使用了辅助库的luaL_openlib函数,他根据给定的名字创建一个表,并使用arraylib数组中的name-function对填充这个表。

打开上面定义的库之后,我们就可以在Lua中使用我们新定义的类型了:

a = array.new(1000)

print(a)                 --> userdata: 0x8064d48

print(array.size(a))     --> 1000

for i=1,1000 do

    array.set(a, i, 1/i)

end

print(array.get(a, 10))  --> 0.1

在一个Pentium/Linux环境中运行这个程序,一个有100K元素的数组大概占用800KB的内存,同样的条件由Lua 表实现的数组需要1.5MB的内存。


相关链接:
lua程序设计目录 - 中国lua开发者 - lua论坛