27.3.3 - Upvalues

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.