Here, we will briefly record the knowledge of closures in Lua and C closure calls.
Prerequisite: in Lua API Note 2, we have analyzed the structure of the Lua median, which is a combination of tvalue {value, TT}. If you have any questions, please take a look.
Some important data structures
There are two closure types in Lua: C closure and Lua closure.
The public part of the two closures:
# Define closureheader commonheader; lu_byte ISC; lua_byte nupvalues; gcobject * gclist; struct table env
/* Whether it is a C closure */* Number of upval * // * env of the closure, set/getenv is the Operating Method */
C closure Structure
Struct cclosure {
Closureheader;
Lua_cfunction F;
Tvalue upvalue [1];
}
The structure is relatively simple. F is a C function that meets the int lua_func (lua_state *) type.
Upvalue is the upvalue pressed when the C closure is created. The type is tvalue. We can know that upvalue can be of any Lua type.
Lua closure Structure
Struct lclosure {
Closureheader;
Strcut proto * P;
Upval * upvals [1];
}
The structure of Proto is complex. No analysis is performed here.
A unified closure structure, a consortium, indicates that a closure is either a C closure or a Lua closure, which is recognized by the ISC table.
Union closure {
Cclosure C;
Lclosure L;
}
Tangled Closure
Why does it look like a function when we call it a closure instead of a function? Why do we need to invent a word like "closure" That sounds so painful? I 've been stuck here for a long time. It's almost a year and a half ~~~ =. = I'm stupid ~~~ As I looked at the source code, I figured out some of my experiences in the research process [as easy as possible]:
1. Function Definition in C language: no objection to the abstract block of the function.
2. Lua extends the function:
A. You can bind several values with functions. These values are called upvalue.
PS: some people may think that the C ++ function object can also bind several values with the function, however, this problem is like "object-oriented in assembly". Lua provides support for upvalue at the language layer, just as C ++/Java provides support for classes and objects at the language level, of course, this frees us from the workload of programmers and is coupled with the Lua dynamic type, it is even easier.
B. Each function can be bound to an ENV (Environment.
PS: if the above upvalue can still be coding in C ++, then C ++, a special feature in the Dynamic Language of env context, does not have an obvious corresponding structure? Some people may think that Lua is written in C and can be implemented through coding. Okay =. =, "what you can do and what you do" is the same thing. You just want to walk from Beijing to Shanghai. It doesn't mean you have to do this. env is very important and useful. It can easily create a restricted environment, the legendary "sandbox ", I am more familiar with "a dynamic namespace mechanism ". this will not be analyzed for the moment.
Okay, now we can see
C Function {function abstraction}
Lua closure {function abstraction, upvalue, ENV}
Key points: Closure =={ function abstraction, upvalue, ENV}
As you can see, we all understand that if we abstract the {function in Lua, upvalue and env} are also called functions, it will not only lead to misunderstandings that they are the same as C functions, moreover, it does not express the rich connotation of the Lua function,Closure: "closed" refers to an object, a visible and tangible thing, an integral whole (First Class ); "package" indicates that it contains the function abstraction, upvalue, Env.The interesting fact here is that {function abstraction, upvalue, ENV} is an implementation feature of many dynamic languages. For example, Lua and JavaScript have such a structure, it is implemented first, and then named "closure. therefore, if you just want to understand the word closure, there is basically no way to understand it. It is useless to query the closure on the Internet. You can find several examples cited by the closure, after reading this, make sure that you feel "this is mysterious, but you still don't understand what a closure is". Why? Because it refers to an implementation structure feature, it is created to implement the first class function and context concept in a dynamic language.
I would rather say a few more words, as long as it is good to deepen understanding, there are two sentences: "I ride a bike to buy fruit" "I used to closure {function abstraction, upvalue, env} implements the first class function and context concept in the Dynamic Language. Closure and cycling are both a means of achieving your goal, to buy fruit, you think about the idea of "cycling", instead of buying fruit for cycling. it's not right to just stare at the bike. It's just a means.
The process of registering a C function with Lua is implemented through the lua_pushcclosure (L, F, n) function.
Process: 1. create a sizeof (cclosure) + (n-1) * sizeof (tvalue) memory, which is cclosure + tvalue [N], and perform GC bookkeeping [This is too important. Why should Lua control all the variables in its own world because it needs GC bookkeeping to manage memory?], ISC = 1 indicates a C closure.
2. C-> F = f bind C function .---------Closure. Function abstraction = f
3. Env = env of the current closure [this indicates that the created closure inherits the environment for creating its closure]. -----------Closure. Env = env
4. assign n elements on the stack to the C-> upvalue [] array. The order is that the values of the first stack are placed at the starting position of the upvalue array, c-> nupvalues specifies the number of upvalue closures to be modified. ----------Closure. upvalue = upvalue
5. Pop up n elements on the stack and press the newly created closure to the top of the stack.
The entire process is relatively simple. allocate memory, fill in attributes, link to GC monitoring, bind C functions, bind upvalue, and bind an env C closure, please refer to the preceding description of the closure.
Now let's parse the process of calling the C closure. [Note that only calls to the C closure are involved.]
Lua closure call information structure:
Struct callinfo {
Stkid base;/* base for this function */---- stack base called by the closure
Stkid func;/* function index in the stack */---- position of the closure to be called on the stack
Stkid top;/* Top for this function */---- the stack usage limit of the closure, that is, when lua_push * is used, you have to look at the points. If there are too many pushes, you can expand the stack by lua_checkstack.
Const instruction * savedpc; ---- if another closure is called in this closure again, this value will save the next instruction so that the execution can continue when the response is returned.
Int nresults;/* expected number of results from this function */---- number of values to be returned by the closure
Int tailcils;/* Number of tail CILS lost under this entry */---- used for tail recursion.
}
It can be seen from the annotation that this structure is relatively simple, and its function is to maintain information about a function call. In fact, it is the same as the stack frame of a C function call, the base-> EBP, func-> stack index of the function to be called, savedpc-> EIP, top, nresults, and tailcils have no obvious correspondence.
During Lua initialization, A callinfo array is allocated, and l-> base_ci is used to point to the first element of the array. L-> end_ci is used to point to the last pointer of the array, use L-> size_ci to record the current size of the array. L-> Ci records the call information of the currently called closure.
The following describes how to call a C closure:
Scenario: C function int lua_test (lua_state * l ){
Int A = lua_tonumber (L, 1 );
Int B = lua_tonumber (L, 2 );
A = A + B;
Lua_pushnumber (L, );
}
It has been registered in Lua to form a C closure named "test". We will call it below
Lual_dostring (L, "c = test (3, 4 )")
1. First, we translate it into the corresponding C API
1. Initial Stack
Lua_getglobal (L, "test ")
Lua_pushnumber (L, 3)
Lua_pushnumber (L, 4)
2. pushed into the function and parameter Stack
Lua_call (l, 2, 1)
3. Call the stack at the beginning of lua_test
4. End stack of the call
Lua_setglobal (L, "C ")
5. Retrieve the stack of the call result
What we want to know is the lua_call function process.
1. the consistency of Lua is once again shocking. Whether it is dostring or dofile, a closure is formed. That is to say, the closure is the basic component of Lua for organizing the structure, this feature makes the structure in Lua consistent and is a concise and powerful concept.
2. according to 1, A = test (3, 4) is actually organized into a closure placed on the top of the Lua stack [during the convenience period, name this Lua closure BB], that is to say, dostring actually calls the BB closure, and test is called only when the BB closure is executed.
[Save the current information to callinfo of the current function]
3. at the time of calling test, L-> Ci records the call information of the BB closure. Therefore, first place the next command to be executed in L-> Ci-> savedpc, to continue execution after the test is returned.
4. Take the test C closure Cl on the stack and use Cl-> ISC = 1 to determine that it is indeed a C closure.
[Enter a new callinfo and deploy the stack]
5. assign a callinfo Ci from L to record test call information and set its value to L-> Ci, which indicates that a new function call has started, here we also need to specify the position of test in the stack, L-> base = Ci-> func + 1. Note that these assignment values are very important, the resulting stack status is converted from Figure 2 to Figure 3. It can be seen that l-> base points to the first parameter, and Ci-> base also points to the first parameter, therefore, in test, we call the lua_gettop function to return 2, because when we call it, there are only two elements on its stack frame, realizing the parameter passing from Lua to C.
[Call the actual function]
6. arrange the stack, and find the corresponding Cl-> C-> F according to the closure (and the C closure of test) on the stack pointed by L-> Ci-> func, and call it to enter the C function lua_test
[Obtain the returned value to adjust the stack and return the original callinfo]
7. Based on the return value of lua_test, bring the test closure and parameter to the stack, and press the return value into and adjust L-> top
8. Restore L-> base, L-> Ci and L-> savedpc and continue execution.
Conclusion: when calling a new closure, 1. save the current information to callinfo of the current function. enter a new callinfo and layout the stack. call the actual function 4. gets the returned value to adjust the stack and returns the original callinfo
Lua function call 1 -- Detailed description of closures and C calls