![]() |
Programming in Lua | ![]() |
| Part IV. The C API Chapter 27. Techniques for Writing C Functions |
"Array", in Lua, is just a name for a table used in a specific way.
We can manipulate arrays using the same functions
we use to manipulate tables,
namely lua_settable and lua_gettable.
However, contrary to the general philosophy of Lua,
economy and simplicity,
the API provides special functions for array manipulation.
The reason for that is performance:
Frequently we have an array access operation
inside the inner loop of an algorithm (e.g., sorting),
so that any performance gain in this operation
can have a big impact on the overall performance of the function.
The functions that the API provides for array manipulation are
void lua_rawgeti (lua_State *L, int index, int key);
void lua_rawseti (lua_State *L, int index, int key);
The description of lua_rawgeti and lua_rawseti
is a little confusing,
as it involves two indices:
index refers to where the table is in the stack;
key refers to where the element is in the table.
The call lua_rawgeti(L, t, key) is equivalent to the sequence
lua_pushnumber(L, key);
lua_rawget(L, t);
when t is positive
(otherwise, you must compensate for the new item in the stack).
The call lua_rawseti(L, t, key) (again for t positive)
is equivalent to
lua_pushnumber(L, key);
lua_insert(L, -2); /* put `key' below previous value */
lua_rawset(L, t);
Note that both functions use raw operations.
They are faster and, anyway,
tables used as arrays seldom use metamethods.
As a concrete example of the use of these functions,
we could rewrite the loop body
from our previous l_dir function from
lua_pushnumber(L, i++); /* key */
lua_pushstring(L, entry->d_name); /* value */
lua_settable(L, -3);
to
lua_pushstring(L, entry->d_name); /* value */
lua_rawseti(L, -2, i++); /* set table at key `i' */
As a more complete example, the following code implements the map function: It applies a given function to all elements of an array, replacing each element by the result of the call.
int l_map (lua_State *L) {
int i, n;
/* 1st argument must be a table (t) */
luaL_checktype(L, 1, LUA_TTABLE);
/* 2nd argument must be a function (f) */
luaL_checktype(L, 2, LUA_TFUNCTION);
n = luaL_getn(L, 1); /* get size of table */
for (i=1; i<=n; i++) {
lua_pushvalue(L, 2); /* push f */
lua_rawgeti(L, 1, i); /* push t[i] */
lua_call(L, 1, 1); /* call f(t[i]) */
lua_rawseti(L, 1, i); /* t[i] = result */
}
return 0; /* no results */
}
This example introduces three new functions.
The luaL_checktype function (from lauxlib.h)
ensures that a given argument has a given type;
otherwise, it raises an error.
The luaL_getn function gets the size
of the array at the given index
(table.getn calls luaL_getn to do its job).
The lua_call function does an unprotected call.
It is similar to lua_pcall,
but in case of errors it throws the error,
instead of returning an error code.
When you are writing the main code in an application,
you should not use lua_call,
because you want to catch any errors.
When you are writing functions, however,
it is usually a good idea to use lua_call;
if there is an error,
just leave it to someone that cares about it.
| Copyright © 2003-2004 Roberto Ierusalimschy. All rights reserved. |
|
![]() |