第一版是为 Lua 5.0 编写的。虽然在很大程度上仍然适用于更高版本,但有一些不同之处。
第四版针对 Lua 5.3,可在 Amazon 和其他书店购买。
购买本书,您还可以帮助支持 Lua 项目。
用 Lua 编程 | ||
第四部分。C API 第 28 章。C 中的用户定义类型 |
我们的下一步是将新类型转换为对象,以便我们可以使用通常的面向对象语法对其实例进行操作,例如
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
元方法。对于表,每当 Lua 找不到给定键的值时,就会调用此元方法。对于用户数据,每次访问都会调用它,因为用户数据根本没有键。
假设我们运行以下代码
local metaarray = getmetatable(array.new(1)) metaarray.__index = metaarray metaarray.set = array.set metaarray.get = array.get metaarray.size = array.size在第一行中,我们创建一个数组,仅获取其元表,并将其分配给
metaarray
。(我们无法从 Lua 设置用户数据的元表,但我们可以毫无限制地获取其元表。)然后,我们将 metaarray.__index
设置为 metaarray
。当我们计算 a.size
时,Lua 无法在对象 a
中找到键 "size"
,因为该对象是用户数据。因此,Lua 将尝试从 a
的元表的 __index
字段获取此值,而该字段恰好是 metaarray
本身。但 metaarray.size
是 array.size
,因此 a.size(a)
产生 array.size(a)
,正如我们所愿。
当然,我们可以在 C 中编写相同的内容。我们甚至可以做得更好:现在数组是对象,有自己的操作,我们不再需要在表 array
中进行这些操作。我们的库仍然必须导出的唯一函数是 new
,用于创建新数组。所有其他操作仅作为方法提供。C 代码可以直接将它们注册为方法。
操作 getsize
、getarray
和 setarray
与我们之前的方法没有变化。改变的是我们注册它们的方式。也就是说,我们必须更改打开库的函数。首先,我们需要两个单独的函数列表,一个用于常规函数,一个用于方法
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
的新版本必须创建元表,将其分配给它自己的 __index
字段,在那里注册所有方法,并创建和填充 array
表
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
不会创建任何表来打包函数;相反,它假定包表在堆栈上,低于任何偶尔的向上值。在此示例中,包表是元表本身,luaL_openlib
将方法放在此处。对 luaL_openlib
的下一次调用正常工作:它使用给定的名称 (array
) 创建一个新表,并在那里注册给定的函数(此例中仅为 new
)。
作为最后一步,我们将向新类型添加一个 __tostring
方法,以便 print(a)
打印 array
加上括号内的数组大小(例如,array(1000)
)。函数本身在此处
int array2string (lua_State *L) { NumArray *a = checkarray(L); lua_pushfstring(L, "array(%d)", a->size); return 1; }
lua_pushfstring
函数格式化字符串并将其留在堆栈顶部。我们还必须将 array2string
添加到列表 arraylib_m
,以将其包含在数组对象的元表中
static const struct luaL_reg arraylib_m [] = { {"__tostring", array2string}, {"set", setarray}, ... };
版权所有 © 2003–2004 Roberto Ierusalimschy。保留所有权利。 |