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