标签:
typedef struct NumArray { int size; double values[1]; /* variable part */ } NumArray;我们使用大小 1 声明数组的 values,由于 C 语言不允许大小为 0 的数组,这个 1 只 是一个占位符;我们在后面定义数组分配空间的实际大小。对于一个有 n 个元素的数组 来说,我们需要
sizeof(NumArray) + (n-1)*sizeof(double) bytes(由于原始的结构中己经包含了一个元素的空间,所以我们从 n 中减去 1)
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 */ }<span style="font-size:14px; font-family: Arial, Helvetica, sans-serif; background-color: rgb(255, 255, 255);"> </span>
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; }
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 的内存。
int luaL_newmetatable (lua_State *L, const char *tname); void luaL_getmetatable (lua_State *L, const char *tname); void *luaL_checkudata (lua_State *L, int index,const char *tname);
int luaopen_array (lua_State *L) { luaL_newmetatable(L, "LuaBook.array"); luaL_openlib(L, "array", arraylib, 0); return 1; }
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); luaL_getmetatable(L, "LuaBook.array"); lua_setmetatable(L, -2); a->size = n; return 1; /* new userdatum is already on the stack */ }lua_setmetatable 函数将表出栈,并将其设置为给定位置的对象的 metatable。在我们 的例子中,这个对象就是新的userdatum。
static NumArray *checkarray (lua_State *L) { void *ud = luaL_checkudata(L, 1, "LuaBook.array"); luaL_argcheck(L, ud != NULL, 1, "`array' expected"); return (NumArray *)ud; }使用 checkarray,新定义的 getsize 是更直观、更清楚:
static int getsize (lua_State *L) { NumArray *a = checkarray(L); lua_pushnumber(L, a->size); return 1; }
static double *getelem (lua_State *L) { NumArray *a = checkarray(L); int index = luaL_checkint(L, 2); luaL_argcheck(L, 1 <= index && index <= a->size, 2,"index out of range"); /* return element address */ return &a->values[index - 1]; }使用这个 getelem,函数 setarray 和 getarray 更加直观易懂:
static int setarray (lua_State *L) { double newvalue = luaL_checknumber(L, 3); *getelem(L) = newvalue; return 0; } static int getarray (lua_State *L) { lua_pushnumber(L, *getelem(L)); return 1; }现在,假如你尝试类似 array.get(io.stdin, 10)的代码,你将会得到正确的错误信息:
error: bad argument #1 to 'getarray' ('array' expected)
a = array.new(1000) print(a:size()) --> 1000 a:set(10, 3.4) print(a:get(10)) --> 3.4记住 a:size()等价于 a.size(a)。所以,我们必须使得表达式 a.size 调用我们的 getsize 函数。这儿的关键在于__index 元方法Cmetamethod)的使用。对于表来说,不管什么 时候只要找不到给定的 key,这个元方法就会被调用。对于 userdata 来讲,每次被访问 的时候元方法都会被调用,因为 userdata 根本就没有任何 key。
local metaarray = getmetatable(array.new(1)) metaarray. index = metaarray metaarray.set = array.set metaarray.get = array.get metaarray.size = array.size
static const struct luaL_reg arraylib_f [] = { {"new", newarray}, {NULL, NULL} }; static const struct luaL_reg arraylib_m [] = { {"set", setarray}, {"get", getarray}, {"size", getsize}, {NULL, NULL} };新版本打开库的函数 luaopen_array,必须创建一个 metatable,并将其赋值给自己的_index 域,在那儿注册所有的方法,创建并填充数组表:
int luaopen_array (lua_State *L) { luaL_newmetatable(L, "LuaBook.array"); lua_pushstring(L, " index"); lua_pushvalue(L, -2); /* pushes the metatable */ lua_settable(L, -3); /* metatable. index = metatable */ luaL_openlib(L, NULL, arraylib_m, 0); luaL_openlib(L, "array", arraylib_f, 0); return 1; }这里我们使用了 luaL_openlib 的另一个特征,第一次调用,当我们传递一个 NULL 作为库名时,luaL_openlib 并没有创建任何包含函数的表;相反,他认为封装函数的表 在栈内,位于临时的 upvalues 的下面。在这个例子中,封装函数的表是 metatable 本身, 也就是 luaL_openlib 放置方法的地方。第二次调用 luaL_openlib 正常工作:根据给定的 数组名创建一个新表,并在表中注册指定的函数(例子中只有一个函数 new)。
int array2string (lua_State *L) { NumArray *a = checkarray(L); lua_pushfstring(L, "array(%d)", a->size); return 1; }函数 lua_pushfstring 格式化字符串,并将其放到栈顶。为了在数组对象的 metatable中包含 array2string,我们还必须在 arraylib_m 列表中添加 array2string:
static const struct luaL_reg arraylib_m [] = { {" tostring", array2string}, {"set", setarray}, ... };28.4 访问数组
local metaarray = getmetatable(newarray(1)) metaarray. index = array.get metaarray. newindex = array.set
a = array.new(1000) a[10] = 3.4 -- setarray print(a[10]) -- getarray --> 3.4如果我们喜欢的话,我们可以在我们的 C 代码中注册这些元方法。我们只需要修改 我们的初始化函数:
<pre name="code" class="csharp">int luaopen_array (lua_State *L) { luaL_newmetatable(L, "LuaBook.array"); luaL_openlib(L, "array", arraylib, 0); /* now the stack has the metatable at index 1 and 'array' at index 2 */ lua_pushstring(L, " index"); lua_pushstring(L, "get"); lua_gettable(L, 2); /* get array.get */ lua_settable(L, 1); /* metatable. index = array.get */ lua_pushstring(L, " newindex"); lua_pushstring(L, "set"); lua_gettable(L, 2); /* get array.set */ lua_settable(L, 1); /* metatable. newindex = array.set */ return 0; }28.5 Light Userdata
void lua_pushlightuserdata (lua_State *L, void *p);尽管都是 userdata,light userdata 和 full userdata 有很大不同。Light userdata 不是一 个缓冲区,仅仅是一个指针,没有 metatables。像数字一样,light userdata 不需要垃圾收集器来管理她。
Lua_第27章 User-Defined Types in C
标签:
原文地址:http://blog.csdn.net/heyuchang666/article/details/51296713