标签:
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