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 27. Techniques for Writing C Functions |
While the registry implements global values, the upvalue mechanism implements an equivalent of C static variables, which are visible only inside a particular function. Every time you create a new C function in Lua, you can associate with it any number of upvalues; each upvalue can hold a single Lua value. Later, when the function is called, it has free access to any of its upvalues, using pseudo-indices.
We call this association of a C function with its upvalues a closure. Remember that, in Lua code, a closure is a function that uses local variables from an outer function. A C closure is a C approximation to a Lua closure. One interesting fact about closures is that you can create different closures using the same function code, but with different upvalues.
To see a simple example,
let us create a newCounter
function in C.
(We already defined this same function in Lua,
in Section 6.1.)
This function is a factory function:
It returns a new counter function each time it is called.
Although all counters share the same C code,
each one keeps its own independent counter.
The factory function is like this:
/* forward declaration */ static int counter (lua_State *L); int newCounter (lua_State *L) { lua_pushnumber(L, 0); lua_pushcclosure(L, &counter, 1); return 1; }The key function here is
lua_pushcclosure
,
which creates a new closure.
Its second argument is the base function (counter
, in the example)
and the third is the number of upvalues (1, in the example).
Before creating a new closure,
we must push on the stack the initial values for its upvalues.
In our example, we push the number 0 as the initial value for the
single upvalue.
As expected,
lua_pushcclosure
leaves the new closure on the stack,
so the closure is ready to be returned as the result of newCounter
.
Now, let us see the definition of counter
:
static int counter (lua_State *L) { double val = lua_tonumber(L, lua_upvalueindex(1)); lua_pushnumber(L, ++val); /* new value */ lua_pushvalue(L, -1); /* duplicate it */ lua_replace(L, lua_upvalueindex(1)); /* update upvalue */ return 1; /* return new value */ }Here, the key function is
lua_upvalueindex
(which is actually a macro),
which produces the pseudo-index of an upvalue.
Again, this pseudo-index is like any stack index,
except that it does not live in the stack.
The expression lua_upvalueindex(1)
refers to the index of
the first upvalue of the function.
So, the lua_tonumber
in function counter
retrieves the current value of the first (and only) upvalue as a number.
Then, function counter
pushes the new value ++val
,
makes a copy of it,
and uses one of the copies to replace the upvalue with the new value.
Finally, it returns the other copy as its return value.
Unlike Lua closures, C closures cannot share upvalues: Each closure has its own independent set. However, we can set the upvalues of different functions to refer to a common table, so that this table becomes a common place where those functions can share data.
Copyright © 2003–2004 Roberto Ierusalimschy. All rights reserved. |