This first edition was written for Lua 5.0. While still largely relevant for later versions, there are some differences.
The fourth edition targets Lua 5.3 and is available at Amazon and other bookstores.
By buying the book, you also help to support the Lua project.
Programming in Lua | ||
Part IV. The C API Chapter 28. User-Defined Types in C |
Our next step is to transform our new type into an object, so that we can operate on its instances using the usual object-oriented syntax, such as
a = array.new(1000) print(a:size()) --> 1000 a:set(10, 3.4) print(a:get(10)) --> 3.4
Remember that a:size()
is equivalent to a.size(a)
.
Therefore, we have to arrange for the expression a.size
to return our
getsize
function.
The key mechanism here is the __index
metamethod.
For tables, this metamethod is called whenever Lua cannot find
a value for a given key.
For userdata, it is called in every access,
because userdata have no keys at all.
Assume that we run the following code:
local metaarray = getmetatable(array.new(1)) metaarray.__index = metaarray metaarray.set = array.set metaarray.get = array.get metaarray.size = array.sizeIn the first line, we create an array only to get its metatable, which we assign to
metaarray
.
(We cannot set the metatable of a userdata from Lua,
but we can get its metatable without restrictions.)
Then we set metaarray.__index
to metaarray
.
When we evaluate a.size
,
Lua cannot find the key "size"
in object a
,
because the object is a userdatum.
Therefore, Lua will try to get this value from
the field __index
of the metatable of a
,
which happens to be metaarray
itself.
But metaarray.size
is array.size
,
so a.size(a)
results in array.size(a)
,
as we wanted.
Of course, we can write the same thing in C.
We can do even better:
Now that arrays are objects,
with their own operations,
we do not need to have those operations in the table
array
anymore.
The only function that our library still has to export is new
,
to create new arrays.
All other operations come only as methods.
The C code can register them directly as such.
The operations getsize
, getarray
, and setarray
do not change from our previous approach.
What will change is how we register them.
That is, we have to change the function that opens the library.
First, we need two separate function lists,
one for regular functions and one for methods:
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} };The new version of
luaopen_array
,
the function that opens the library,
has to create the metatable,
to assign it to its own __index
field,
to register all methods there,
and to create and fill the array
table:
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; }Here we use another feature from
luaL_openlib
.
In the first call, when we pass NULL
as the library name,
luaL_openlib
does not create any table to pack the functions;
instead, it assumes that the package table is on the stack,
below any occasional upvalues.
In this example, the package table is the metatable itself,
which is where luaL_openlib
will put the methods.
The next call to luaL_openlib
works regularly:
It creates a new table with the given name (array
)
and registers the given functions there
(only new
, in this case).
As a final touch,
we will add a __tostring
method to our new type,
so that print(a)
prints array
plus the size of
the array inside parentheses (for instance, array(1000)
).
The function itself is here:
int array2string (lua_State *L) { NumArray *a = checkarray(L); lua_pushfstring(L, "array(%d)", a->size); return 1; }The
lua_pushfstring
function formats the string
and leaves it on the stack top.
We also have to add array2string
to the list arraylib_m
,
to include it in the metatable of array objects:
static const struct luaL_reg arraylib_m [] = { {"__tostring", array2string}, {"set", setarray}, ... };
Copyright © 2003–2004 Roberto Ierusalimschy. All rights reserved. |