Note: The original text was also issued in the company's internal forum
The stack that LUA interacts with C is an important concept. The article first explains why you should introduce the LUA stack, then summarizes the APIs that are commonly used in the access stack, and uses the considerations of these APIs, and finally looks at the implementation of the stack from the LUA source code.
Lua Stack OverviewWe know that Lua is an embedded language, and all LUA programs eventually need to be parsed into bytecode form by the LUA interpreter (the LUA virtual machine) to execute. On the one hand, we can embed the LUA interpreter in an application (with the initiative), where LUA is used to facilitate the extension of the application and work with Lua, on the other hand, We can also use functions that are implemented in the C language (such as String.find ()) in the LUA program, which has the initiative in the LUA language program. In the above two descriptions, all involve the data exchange between Lua and C, and in the exchange of data between the two languages, we naturally face two problems, one is that Lua is a dynamic type language, there is no type-defined syntax in the LUA language, each value carries its own type information, and the C language is a static type language And the other is that Lua uses garbage collection, which automatically manages memory, while the C language requires the program to release the allocated memory itself, requiring the application to manage the memory itself. To solve these two problems, LUA introduces a virtual stack. To facilitate LUA interaction with C, such as invoking LUA functions in C code, LUA officially provides a range of APIs and libraries. These api,c languages make it easy to get the values from Lua, and it's easy to return the values to Lua, which, of course, is done through the stack as a bridge.API to access the LUA stackLUA provides a number of APIs for manipulating stacks, which allow us to push elements into the stack, query the elements in the stack, and modify the size of the stack. Here's a quick summary of common API usage, especially where you need to be aware of using these APIs.
1. Pressing elements into the stack
The API that pushes elements into the stack is usually named after the lua_push*, such as Lua_pushnunber, Lua_pushstring, Lua_pushcfunction, Lua_ Pushcclousre functions are pushing a LUA value into the top of the stack. Typically, when a C function is called in LUA code and a return value is returned from the called C functions, the called C function will usually use these interfaces, pressing the return value into the stack and returning it to LUA (of course, these C functions also require that a value be returned to tell Lua how many values are returned (pressed in). It is important to note that when you push an element into the stack, you should make sure that there is enough space in the stack to call Lua_checkstack to detect if there is enough space. These APIs essentially encapsulate the values in the C language into the stack, and for those elements that require garbage collection, a copy is generated in Lua (that is, the LUA virtual machine) when pressed into the stack. For example, lua_pushstring (lua_state *l, const char *s) pushes the medium stack into the ' \ 0 ' At the end of the string, after calling this function in C, we can arbitrarily free or modify the string that is pointed to by S, and there is no problem because LUA generates an internal copy during the execution of Lua_pushstring.In essence, LUA does not hold a pointer to an external string, nor does it hold a pointer to any other external object (except for the C function, because the C function is always static). In short, once the value of C is pressed into the stack, LUA generates the corresponding structure (essentially the corresponding data type implemented in LUA) and manages (for example, automatic garbage collection) This value, and will no longer depend on the original C value. 2. Get the elements in the stack
A function that gets a value from the stack is usually named after the lua_to*, such as Lua_tonumber, Lua_tostring, Lua_touserdata, Lua_tocfunction, and so on, to get a value from the index specified in the stack. Typically in C functions, you can use these interfaces to get the arguments passed to the C function from Lua. If the specified element does not have the correct type, calling these functions will not have a problem. In this case, Lua_toboolean, Lua_tonumber, Lua_tointeger, and Lua_objlen return 0, while other functions return null. For a function that returns NULL, you can directly pass the return value, that is, you know that the call is correct, and for a function that returns 0, you usually first need to use the Lua_is* series function to determine whether the call is correct. Note that both the lua_to* and lua_is* series functions attempt to convert the values in the stack to the corresponding elements. For example, Lua_isnumber does not check whether it is a numeric type, but rather checks whether it can be converted to a numeric type, and lua_isstring is similar, and lua_isstring returns true for any number. To really return the type of the elements in the stack, you can use the function Lua_type. Each type corresponds to a constant (lua_tnil,lua_tboolean,lua_tnumber, etc.), and these constants are defined in the header file lua.h.
It is worth mentioning that the lua_tolstring function, its function prototype is the const char *lua_tolstring (lua_state *l, int index, size_t *len). It will replace the LUA value indexed as index in the stack with a C string. If the parameter len is not NULL, *len will save the length of the string. The LUA value in the stack must be a string or number type, otherwise the function returns NULL. If the LUA value in the stack is number type, then the function essentially changes the value in the stack to string type, for this reason,when using Lua_next to traverse a table in a stack, it is particularly important to use lua_tolstring with key unless you know that the key is of type string. The pointer returned by the Lua_tolstring function points to a string inside the LUA virtual machine that ends with ' s ', but the middle of the string may also contain a character with a value of 0.because of the LUA garbage collection itself, when a string in the stack is ejected, the pointer returned by the function may not be valid anymore. It also shows that when a C function receives a string argument from Lua, in the C function, it cannot be popped from the stack while accessing the string, nor can it modify the string.
3. Functions of other operation Stacks
int Lua_call (lua_state *l, int nargs, int nresults);
Call the function in the stack, before calling Lua_call, the program must first ensure that the called function has been pushed into the stack, followed by the call function required parameters have been pressed into the stack sequentially, that is, the first parameter is pushed into the stack first, and so on. Nargs refers to the number of parameters that need to be pressed into the stack, when the function is called, the previously pressed functions and parameters are ejected from the stack and the results of the function execution are pressed into the stack sequentially, so the last result is pressed into the top of the stack, and the number of pressed stacks is adjusted according to the value of Nresults. Corresponding to the Lua_call is the Lua_pcall function, Lua_pcall will call the function in the stack in protected mode. The call in protected mode means that when any error occurs in the function being called, the error does not propagate, unlike Lua_call, which passes the error to the previous layer and lua_pcall the function in the called stack sends an error, Lua_pcall catches the error and presses an error message into the stack. and returns an error code. When writing the main function in an application, you should use Lua_pcall to invoke the function in the stack to catch all errors. When writing an extended C function for Lua, you should call Lua_call and return the error to the scripting layer. void Lua_createtable (lua_state *l, int narr, int nrec);
Create a new table and press it into the top of the stack, the parameters Narr and Nrec refer to how many elements of the new table will be and how many elements to hash, and Lua allocates memory for the new table based on this two value. Using these two parameters can improve the performance of creating a new table for knowing the table structure beforehand. For a table structure that is not known beforehand, you can use void lua_newtable (Lua_state *l), which is equivalent to lua_createtable (L, 0, 0).
In addition to the above mentioned C API, there are many other useful C APIs, such as the interface to operate table: Lua_getfield, Lua_setfield, lua_gettable, lua_settable and other interfaces, in the specific use, You can refer to the LUA manual.See the LUA stack from the sourceIn applications (such as those written in C + +), in order to load and execute LUA scripts, we first need to call Lual_newstate () in the application main function, which creates a stack of LUA interacting with C to initialize the LUA virtual machine. The function returns a pointer to the Lua_state type L, and the first parameter type of almost all APIs is lua_state*, and the value to be passed in is the pointer returned by the Lual_newstate () function, which is done to make each LUA state machine independent of each other. And does not share any data.Lua_state represents the execution state of a LUA program, which represents a new thread (note refers to the thread type in Lua, not the threads in the operating system), each thread has a separate data stack, a function call chain, and a separate debug hook and error handling method. Virtually all API operations are carried out around this structure, including lua_state data into the stack, extracting data from the stack, and performing functions in the stack. The structure lua_state is defined in lstate.h with the following code:
struct lua_state {
commonheader;
Lu_byte status;
Stkid top; /* point to the data stack, the first available space */
global_state *l_g;
Callinfo *ci; /* Saves the running state of the function currently being called */
const instruction *OLDPC;
Stkid Stack_last; /* point to the data stack, the last available space */
stkid stack; /* point to the beginning of the data stack */
int stacksize; /* Stack current size, note is not available in size */
unsigned short nny;
unsigned short nccalls;
Lu_byte Hookmask;
Lu_byte Allowhook;
int basehookcount;
int hookcount;
Lua_hook Hook;
Gcobject *openupval;
Gcobject *gclist;
struct LUA_LONGJMP *errorjmp;
ptrdiff_t Errfunc;
Callinfo base_ci; /* Save the first node of the call list */
};
Lua_state contains two important data structures, one is the data stack, the other is the call stack (essentially a doubly linked list). The data stack is essentially a dynamic array in which each element of the array is of type TValue. Any data type (nil,number,stirng,userdata,function, etc.) in Lua is implemented using the struct TValue. It is defined as follows (the source code uses a large number of macros and typedef to define the TValue, in order to facilitate reading, to expand it):
All data types in Lua are implemented by the struct TValue, which binds values and types together, and each data carries its own type information. The type of data saved with the member Tt_, the member Value_ is used to hold the data value, which is used by a union to achieve:
Union Value {
gcobject *gc; /*GC points to an object that is the type of data that needs to be garbage collected, such as table, String, or
void *p; /* The light UserData type in Lua, which essentially holds a pointer */
int b; /*boolean type *
/lua_cfunction F;/*lua light C functions (no upvalue), that is, just the function pointer */
double n; The number type in the/*lua */
};
As mentioned above, the data stack is created in the function stack_init (the lual_newstate that is called when the virtual machine is initialized is called by the Lua_newstate function, Lua_newstate calls the F_luaopen function, and finally f_ Luaopen function calls Stack_init to initialize the stack), the function Stack_init implemented in LSTATE.C, the code is as follows:
static void Stack_init (Lua_state *l1, lua_state *l) {
int i; Callinfo *ci;
/* Allocates space for the data stack and initializes the Lua_state member associated with the data stack *
/l1->stack = Luam_newvector (L, Basic_stack_size, TValue);
L1->stacksize = basic_stack_size;
for (i = 0; i < basic_stack_size; i++)
Setnilvalue (l1->stack + i); /* Erase new stack */
l1->top = l1->stack;
L1->stack_last = L1->stack + l1->stacksize-extra_stack;
/* Initialize Lua_state members associated with the call list */
CI = &L1->base_ci;
Ci->next = ci->previous = NULL;
Ci->callstatus = 0;
Ci->func = l1->top;
Setnilvalue (l1->top++); /* ' function ' entry for this ' CI ' */
ci->top = L1->top + lua_minstack;
L1->ci = CI;
}
The call stack is essentially implemented with a doubly linked list, and each node in the list is implemented with a CALLINFO structure, preserving the running state of the function being called. Structure Callinfo defined in Lstate.h, the code is as follows:
typedef struct CALLINFO {
stkid func;/* points to the called function's position in the stack */
stkid top;/* points to the called function can use the maximum stack space position, That is, the size of the call to a function can be stack space *
/struct callinfo *previous, *next;/* points to the previous node of the call list and the next node */short
nresults; /* The currently called function expects to return the number of results */
lu_byte callstatus; /* is used to identify whether the current call is a C function or a LUA function */
Union {
struct {/ * * When calling Lua to call a function the structure that holds the information */
stkid base;
const instruction *SAVEDPC;
} l;
struct {/ * The structure that holds information when calling C call function */
int ctx;
Lua_cfunction K;
ptrdiff_t Old_errfunc;
ptrdiff_t Extra;
Lu_byte Old_allowhook;
Lu_byte status;
} c;
} u;
} Callinfo;
From the definition of callinfo, it is possible to know that its member variables func and top also point to the location of the data stack, but the difference is that it is concerned about the location of the function call. And when using GDB to debug a C code embedded in Lua, we can get the complete LUA call list with the call list above, in addition to looking at the call stack information in C, in each node in the list, We can use the member variable func in Callinfo to get debugging information such as the file name and line number of each LUA function.
Summary
In the middle of the interaction between Lua and C, we can intuitively imagine that there is a space called the stack, whether it is C or LUA to the other needs of the data, all the first need to push the data into the stack, and then to get the corresponding data from the stack. In all data exchanges between Lua and C, each element of the stack can hold any type of LUA value, and the stack is managed by LUA.
Reference "LUA Program Design" (second edition) Http://www.codingnow.com/temp/readinglua.pdf http://www.lua.org/manual/5.2/ Manual.html http://www.cnblogs.com/ringofthec/tag/lua/
PS: point here is a test code to learn LUA interacting with C