![]() |
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. |
|
![]() |