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 first concern is how to represent array values in Lua. Lua provides a basic type specifically for this: userdata. A userdatum offers a raw memory area with no predefined operations in Lua.
The Lua API offers the following function to create a userdatum:
void *lua_newuserdata (lua_State *L, size_t size);The
lua_newuserdata
function allocates a block of memory
with the given size,
pushes the corresponding userdatum on the stack,
and returns the block address.
If for some reason you need to allocate memory by other means,
it is very easy to create a userdatum with the size of a pointer
and to store there a pointer to the real memory block.
We will see examples of this technique in the next chapter.
Using lua_newuserdata
,
the function that creates new arrays is as follows:
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 */ }(The
luaL_checkint
function
is a variant of luaL_checknumber
for integers.)
Once newarray
is registered in Lua,
you can create new arrays with a statement like
a = array.new(1000)
.
To store an entry,
we will use a call like array.set(array, index, value)
.
Later we will see how to use metatables to support the more conventional
syntax array[index] = value
.
For both notations, the underlying function is the same.
It assumes that indices start at 1, as is usual in Lua.
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; }The
luaL_argcheck
function checks a given condition,
raising an error if necessary.
So, if we call setarray
with a bad argument,
we get an elucidative error message:
array.set(a, 11, 0) --> stdin:1: bad argument #1 to `set' (`array' expected)
The next function retrieves an entry:
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; }We define another function to retrieve the size of an array:
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; }Finally, we need some extra code to initialize our library:
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; }Again, we use
luaL_openlib
, from the auxiliary library.
It creates a table with the given name ("array"
, in our example)
and fills it with the pairs name-function specified by the
array arraylib
.
After opening the library, we are ready to use our new type in 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
Running this implementation on a Pentium/Linux, an array with 100K elements takes 800 KB of memory, as expected; an equivalent Lua table needs more than 1.5 MB.
Copyright © 2003–2004 Roberto Ierusalimschy. All rights reserved. |