Lua_ the 23rd Chapter C API Overview

Source: Internet
Author: User

23rd Chapter C API Overview


Lua is an embedded language , meaning that Lua can be a standalone package or a library for embedding other applications . You might find it odd: if Lua is not just a standalone program, why are we using LUA standalone programs so far throughout the entire book? The answer to this question is the LUA interpreter (the executable LUA). The LUA interpreter is a standalone interpreter implemented using the LUA standard library, which is a small application (no more than 500 lines of code in total). The interpreter is responsible for the interface between the program and the user: Get the file or string from the user and pass it to the LUA standard library, which is responsible for the final code run.

Lua can be used as a library to extend the functionality of the application, which is why LUA can be used as an extensibility language . At the same time, LUA programs can register functions that are implemented in other languages, which may be implemented by the C language (or other languages), adding features that are not easily implemented by LUA. This makes LUA extensible. There are two ways to interact with C and LUA, which correspond to the two viewpoints above (Lua as an extensibility language and extensible language). The first , C as the application language, Lua is used as a library; The second , in turn, Lua is used as a program language and C as a library. In both of these ways, the C language uses the same API to communicate with LUA, so the C and LUA interactions are called C APIs.

The C API is a set of functions in which C code interacts with Lua. He has the following components: functions that read and write LUA global variables, functions that call LUA functions, functions that run LUA snippets, registers C functions that can then be called in Lua, and so on. (In this book, the term function actually refers to a function or a macro, and some functions of the API are implemented in a macro way)

The C API follows the syntax of the C language, which is different from Lua. When using C for programming, we must note that there are many problems with type checking, error handling, and memory allocation. Most of the functions in the API do not check the correctness of their parameters; you need to be responsible for ensuring that the parameters are valid before calling the function. If you pass the wrong argument, you may get the error message "segmentation fault\" or something like this, and there is no clear error message available. In addition, the API focuses on flexibility and simplicity, sometimes at the expense of convenience and practicality. A general task might involve many API calls, which can be annoying, but he gives you the ability to control all details, such as error handling, buffer size, and similar problems. As the title of this chapter shows, this chapter will cover previews of what is involved when calling Lua from C. However, there is a detailed description of the specified function in the Lua reference book. Also, in the LUA release you can see examples of API applications, and the LUA standalone Interpreter (LUA.C) provides examples of application code, while standard libraries (LMATHLIB.C, LSTRLIB.C, and so on) provide examples of library code.

when we talk about "you/You", we mean when you use C programming. The key content of communication between C and Lua lies in a virtual stack. Almost all API calls operate on the value on the stack, and all data exchanges between C and Lua are done through this stack. Alternatively, you can use the stack to save temporary variables. The use of stacks solves two uncoordinated problems between C and Lua : First , Lua automatically collects garbage, and C requires the allocation of storage units to be displayed, both of which cause contradictions. Second, Confusion caused by the inconsistency between dynamic types in Lua and static types in C. We will introduce the relevant contents of the branch in detail in section 24.2.

23.1 younger brother-a sample program

Let's start this preview with a simple application: an implementation of a standalone LUA interpreter. Let's write a simple interpreter with the following code:

<pre Name= "code" class= "CSharp" > #include <stdio.h> #include <lua.h> #include <lauxlib.h> #include <       lualib.h> int main (void) {char buff[256];       int error;  Lua_state *l =lua_open ();  /* Openslua */luaopen_base (L);   /* opensthe basic library*/luaopen_table (L);           /* opensthe table library*/luaopen_io (L);  /* opensthe I/o library*/luaopen_string (L);   /* Opensthe string lib.*/luaopen_math (L); /* Opensthe Math lib.*/while (fgets (buff, sizeof (buff), stdin)!=null) {error = Lual_loadbuffer (L, Buff, str Len (Buff), "line") | |       Lua_pcall (L, 0, 0, 0);            if (Error) {fprintf (stderr, "%s", Lua_tostring (L,-1));       Lua_pop (L, 1);/* POP error message from the stack*/}} lua_close (L); return 0;} 
     

        header file lua.h defines the underlying functions that LUA provides. These include functions that create a new LUA environment (such as Lua_open), functions that call LUA functions (such as Lua_pcall), functions that read/write global variables in the LUA environment, functions that register new functions that can be called by Lua code, and so on. All definitions in lua.h have a lua_ prefix.       Header file Lauxlib.h   defines the functions provided by the secondary library (auxlib). Similarly, all functions defined in it are preceded by Lual_ (for example, Lual_loadbuffer). The auxiliary library uses the underlying functions provided in LUA.H to provide a higher level of abstraction; All LUA standard libraries use Auxlib. The underlying API is dedicated to economy  and orthogonality, instead Auxlib is dedicated to the practicality of general tasks. Of course, creating other abstractions based on the needs of your program is also very easy. It is to be remembered that Auxlib does not have access to Lua's internal permissions. It accomplishes all of its work through the formal basic API. The Lua library does not define any global variables. All its states are stored in the dynamic structure lua_state, and pointers to this structure are used as a parameter to all LUA functions. This implementation allows Lua to re-enter (reentrant) and prepare for use in multi-threading.         function Lua_open Create a new environment (or state). Lua_open when creating a new environment, this environment does not include pre-defined functions, even print. To keep Lua slim, all standard libraries are available in separate packages, so you won't be forced to use them if you don't need them. The header file Lualib.h defines the functions that open these libraries. For example, call Luaopen_io to create an IO table and register I/O functions (io.read,io.write, etc.) into the LUA environment.       After creating a state and loading the standard library, you can interpret the user's input. For each line that the user enters, the C program first calls Lual_loadbuffer to compile the Lua code. If there is no error, this call returns 0 and presses the compiled chunk into the stack. RememberAfter we'll discuss the magical stacks in the next section, the C program calls Lua_pcall, which will pop the chunk out of the stack and run it in protected mode. Like Lual_laodbuffer, Lua_pcall returns zero without error. In the case of errors, both functions press an error message into the stack, and we can use lua_tostring to get the message, output it, and use Lua_pop to remove it from the stack.       Note that in the event of an error, the program simply outputs the error message to the standard error stream. In C, the actual error handling can be very complex and how it is handled depends on the application itself. The Lua core never outputs anything directly to the task output stream; it sends an error signal by returning an error code and an error message. Each application can handle these errors in the way that best suits them. For the sake of simplicity, now let's imagine a simple error handling, just like the code below, which simply outputs an error message, closes the luastate, and exits the entire application.

<pre name= "code" class= "CSharp" > #include <stdarg.h> #include <stdio.h> #include <stdlib.h> void error (Lua_state *l, const char *FMT, ...) {   va_list argp;     Va_start (ARGP,FMT);      vfprintf (stderr, ARGP);     Va_end (ARGP);       Lua_close (L);        Exit (exit_failure);}


Let's discuss in more detail how to handle errors in your app's code. Because you can put Lua and C + +

Code is compiled together, LUA.H does not contain these typical consolidation codes that appear in other C libraries:

<pre name= "code" class= "CSharp" > #ifdef  cplusplus extern "C" {#endif ... #ifdef  cplusplus} #endif

so if you compile it in C, but in C + +, you need to include lua.h as follows header file.

extern "C" {      #include <lua.h>}

A common technique is to create a LUA.HPP header file containing the above code and include the new header file in your C + + program.

23.2 Heaps of gore

When exchanging data between Lua and C, we face two problems: the mismatch between dynamic and static type systems and the inconsistency between automatic and dynamic memory management.

In Lua, when we write down A[k]=v, K and V can have several different types (because of the presence of Metatables, a can also have different types). If we want to provide a similar operation in C, no matter what, the function (settable) of the action table must have a state-determined type. We will need dozens of different functions to do this one (each combination of the three parameter types requires a function).
We can declare some union types in C to solve this problem, which we call Lua_value, which can describe all types of Lua values. Then we can declare this settable
void Lua_settable (Lua_value a,lua_value K, Lua_value v);
This solution has Two DisadvantagesFirst, it may be difficult to map such a complex type to another language; Lua is not only designed to be easy to interact with C + +, but also for Java,fortran and similar languages. Second,LUA is responsible for garbage collection: If we keep LUA values in C variables, the LUA engine has no way of understanding this usage; It may mistakenly consider a value to be garbage and collect him.

Therefore, the Lua API does not define any type similar to Lua_value. Alternative scheme, which uses an abstract stack to exchange values between Lua and C. Each record in the stack can hold any Lua value. Whenever you want to request a value from LUA (such as the value of a global variable), call Lua and the requested value will be pushed into the stack. Whenever you want to pass a value to Lua, first press the value into the stack and then call LUA (the value will be popped). We still need a different function to push each type of C into the stack and a different function to take a value from the stack (the only way out is not to eject), but we avoid the combined explosion (combinatorial explosion). Also, because the stack is managed by LUA, the garbage collector knows that the value is being used by C. Almost all API functions are used in the stack. As we saw in the first example, Lual_loadbuffer left its results on the stack (compiled chunk or an error message); Lua_pcall gets the function to be called from the stack and puts any temporary error information here.
LUA operates the stack with a strict LIFO rule (last in, first out; that is, always access the top of the stack). When you call Lua, it only changes the top part of the stack. Your C code has more freedom, and more specifically, you can query any element on the stack, or even insert and delete elements at any one location.

23.2.1 Push-in element

The API has a stack of functions that stack each LUA type that can be described in C: null (nil) with Lua_pushnil, numeric (double) with Lua_pushnumber, Boolean (in C with integers), and Lua_pushboolean, Any string (char* type, which allows to contain the ' \ S ' character) with the Lua_pushlstring,c language style (with the ' \ "end) string (const char*) with lua_pushstring:

<pre name= "code" class= "CSharp" >void Lua_pushnil (lua_state *l); void Lua_pushboolean (lua_state *l, int bool); void Lua_pushnumber (lua_state *l, double n); void lua_pushlstring (Lua_state *l, const char *s,size_tlength); void Lua_pushstring (lua_state *l, const char *s);

There are also functions that press the C function and the UserData value into the stack, which are discussed later. Strings in Lua are not zero-terminated; they depend on a definite length, so they can contain arbitrary binary data. The formal function of pressing strings into a string is lua_pushlstring, which requires a definite length as a parameter. For a zero-terminated string, you can use Lua_pushstring (which calculates the string length with strlen). Lua never keeps a pointer to an external string (or any other object except the C function one by one, which is always a static pointer). For all the strings it holds, Lua either makes an internal copy or re-uses the existing string.        Therefore, once these functions are returned, you are free to modify or release your buffer. Whenever you push an element onto the stack, it's your responsibility to make sure there's room on the stack to do it. Remember, you are a C programmer now; Lua doesn't spoil you. When Lua starts and calls C in Lua, there are at least 20 idle records on the stack (the Lua_minstack macro in lua.h defines this constant). For most common usage stacks is enough, so usually we don't have to think about it. In any case, some tasks may require more stack space C, such as a function that calls an indeterminate number of arguments. In this case, you might need to call the following function: int lua_checkstack (lua_state *l, int sz);  it detects if there is enough space on the stack that you need (more information about it later).

23.2.2 Query Elements

The API uses an index to access the elements in the stack. The first element in the stack (that is, the first one to be pushed into the stack) has index 1, the next has an index of 2, and so on. We can also use the top of the stack as a reference to access elements, using negative indexes. In this case, 1 indicates the top element of the stack (that is, the last one is pushed in), 2 points to its previous element, and so on. For example, call Lua_tostring (l,-1) to return the value of the top of the stack as a string. As we will see below, it is convenient to use positive index access stacks in some situations, and in some cases, it is more convenient to use a negative index to access the stack. The API provides a set of lua_is* functions to check whether an element is a specified type, * can be any LUA type. So there are lua_isnumber,lua_isstring,lua_istable and similar functions. All of these functions have the same prototype:

int lua_is ... (lua_state *l, int index);
The Lua_isnumber and lua_isstring functions do not check whether the value is a specified type, but rather whether it can be converted to the specified type. For example, any number type satisfies lua_isstring.
There is also a lua_type function that returns the type of the element in the stack. Some of the functions in clua_is* are actually macros defined by this function, and each type is defined as a constant in the Lua.h header file: Lua_tnil, Lua_tboolean, Lua_tnumber, lua_tstring, Lua_tt ABLE,
Lua_tfunction, Lua_tuserdata and Lua_tthread. This function is primarily used in conjunction with a switch statement. It's also useful when we need to really check strings and number types.
To get the value from the stack, here is the lua_to* function:
int           Lua_toboolean (lua_state *l, int index);d ouble        lua_tonumber (lua_state *l, int index); const char *  Lua_ ToString (lua_state *l, int index), size_t        Lua_strlen (lua_state *l, int index);
Even if the type of the given element is incorrect, it is not a problem to call these functions. In this case, Lua_toboolean, Lua_tonumber, and Lua_strlen return 0, and other functions return NULL. Since Ansic does not provide a valid value that can be used to determine the number of errors, the returned 0 is useless. For other functions, we generally do not need to use the corresponding lua_is* function: We just need to call lua_is* and test whether the returned result is null.
The lua_tostring function returns a pointer to the internal copy of the string. You cannot modify it (to remind you that there is a const). As long as the value of the pointer is within the stack, Lua guarantees that the pointer will remain valid. When a C function returns, Lua cleans up his stack, so there is a principle: never save pointers to Lua strings into external functions that access them.
The end of the string returned by Lua_string always has a character end flag of 0, but the middle of the string may also contain the actual length of the 0,lua_strlen return string. In special cases, assuming that the value at the top of the stack is a string, the following assertion (assert) is always valid:

const char *s = lua_tostring (L,-1);   /* Any Lua string*/size_t l =lua_strlen (L,-1); /* Its length */assert (s[l] = = ' + '); assert (strlen (s) <=l);

23.2.3 Other stack operations

The API also provides the following functions to perform the usual stack maintenance work, except for the above-mentioned functions of C and Stack exchange values:
int Lua_gettop (lua_state *l), void Lua_settop (lua_state *l, int index), void Lua_pushvalue (lua_state *l, int index); void Lua_remove (lua_state *l, int index), void Lua_insert (lua_state *l, int index), void Lua_replace (lua_state *l, int index);

The function Lua_gettop returns the number of elements in the stack, which is also the index of the top element of the stack. Note A negative index-X corresponds to a positive index of gettop-x+1. Lua_settop sets the top of the stack (that is, the number of elements in the stack) to a specified value. If the top of the stack starts above the top of the new stack, the top value is discarded. Otherwise, the function presses the corresponding number of NULL (nil) onto the stack in order to get the specified size. In particular, Lua_settop (l,0) empties the stack. You can also use a negative index as a parameter to call Lua_settop, which will set the stack top to the specified index. Using this technique, the API provides the following macro, which POPs n elements from the stack:

#define LUA_POP (L,n) lua_settop (L,-(n)-1)
The function Lua_pushvalue pushes a transfer to the top of the specified index on the stack, Lua_remove removes the element at the specified index position, and moves all of its above elements down to fill the space at that position; Lua_insert moves the top element of the stack to the specified index position, And the elements above the index position are all moved up to the left of the stack top is moved; Finally, the lua_replace pops the element value from the top of the stack and sets it to the specified index position without any movement. Note that the following actions have no effect on the stack:

Lua_settop (l,-1);   /* Settop to it current value*/Lua_insert (L,-1);  /*move Top Elementto the top * *
To illustrate the use of these functions, here is a useful helper function that dumps the contents of the entire stack:

static void Stackdump (Lua_state *l) {      int i;     int top = lua_gettop (L);     for (i = 1; I <= top; i++) {/* repeatfor each level */          int t = lua_type (L, i);          Switch (t) {case           lua_tstring:/* strings *             /printf ("'%s '", lua_tostring (L, i));             break;           Case Lua_tboolean:/  * Booleans *             /printf (Lua_toboolean (L, i)? "True": "false");            break;           Case Lua_tnumber:/* Numbers *             /printf ("%g", Lua_tonumber (L, i));             break;           Default:/  * Other values *             /printf ("%s", Lua_typename (L, T));             break;           }         printf ("  ");/* Put Aseparator      *   /} printf ("\ n");     /* End thelisting */}
This function traverses the entire stack from the bottom of the stack to the top of the stack, printing its value according to the type of each element. It outputs a string in quotation marks, output a number in%g format, and for other values (table, function, and so on) it simply outputs their type (Lua_typename converts a type code to a type name).
The following function takes advantage of stackdump to further illustrate the operation of the API stack.

#include <stdio.h> #include <lua.h>  static void Stackdump (Lua_state *l) {...} int main (void) {  lua_s Tate *l = Lua_open ();  Lua_pushboolean (L, 1); Lua_pushnumber (L, ten);   Lua_pushnil (l); Lua_pushstring (L, "hello");  Stackdump (L);      /* True nil ' hello ' *    /Lua_pushvalue (L,-4); Stackdump (l);      /* True, nil ' hello ' true *    /Lua_replace (L, 3); Stackdump (l);     /* True of ' hello ' *    /Lua_settop (L, 6); Stackdump (l);    /* true: ' Hello ' nil nil *    /Lua_remove (L,-3); Stackdump (l);   /* True NIL nil *    /Lua_settop (L,-5); Stackdump (l);   /* true *  /Lua_close (L); return 0;}




23.3 C API error Handling

Not like C + + or JAVA, C language does not provide an exception handling mechanism。 To improve this difficulty, LUA constructs a mechanism similar to exception handling using C's setjmp technique. (If you are compiling Lua in C + +, it is not difficult to modify the code to use the real exception.) )
All of the structures in LUA are dynamic: they grow on demand and eventually shrink when possible. This means that the likelihood of memory allocation failure is common in Lua. Almost any operation will face this kind of accident. The Lua API uses exceptions to emit these errors instead of generating an error code for each step of the operation. This means that all API functions may throw an error (that is, call longjmp) instead of returning.
When we write a library code (that is, the C function called by LUA), the usefulness of a long jump is almost as convenient as a real exception handler, because Lua crawls the occasional error of the task. When we write the application code (that is, the C code that calls Lua), we must provide a way to fetch the errors anyway.


error Handling in the 23.3.1 application

The typical scenario is that the applied code runs in non-protected mode. Because the applied code is not called by Lua, Lua catches the error (that is, LUA cannot call setjmp), depending on the context. In these cases, when Lua encounters an error like "Not enough memory", he does not know how to handle it. He can only invoke a panic function to exit the app. (You can use the Lua_atpanic function to set your own panic function)
Not all API functions throw exceptions, Lua_open, Lua_close, Lua_pcall, and lua_load are all safe, and most other functions can only throw exceptions if memory allocation fails: For example, lual_loadfile if there is no Enough memory to copy the specified file will fail. Some programs may need to ignore exceptions without doing any processing when they run out of memory. For these programs, panic is not a problem if Lua causes insufficient memory.
If you don't want your app to quit, you must run your code in protected mode, even if the memory allocation fails. Most or all of your LUA code runs by calling Lua_pcall, so it runs in protected mode. Even in the event of a memory allocation failure, LUA_PCALL returns an error code that causes the LUA interpreter to be in a harmonious (consistent) state. If you also want to protect your C code that interacts with LUA, you can use Lua_cpcall. (See the reference to the book for a deeper description of this function, with examples of its application in the release version of Lua's LUA.C file)

23.3. Error handling in Class 2 libraries

Lua is a safe language, which means that no matter what kind of code you have or how the code is wrong, you can know the behavior of the program based on Lua itself. In addition, errors are discovered and interpreted according to Lua. You can compare with C, many of the wrong programs in C can only be interpreted based on the location of the hardware or the error given by the program counter.

Whenever you add a new C function to Lua, you can break the original security. For example, a function like poke, which holds arbitrary bytes in any memory address, can cause memory to be paralyzed. You have to try to make sure that your plugin (add-ons) is safe for Lua and improves error handling.

As we discussed earlier, every C program has his own fault-and-not-handle approach, and there are some standard ways to handle errors when you're going to write a library function for Lua. Whenever the C function finds an error, simply call Lua_error (or Lual_error, which is better, because she invokes the former and formats the error message). The Lua_error function cleans up all the needed cleanup in Lua and then goes back to the original execution Lua_pcall with the error message.

Lua_ the 23rd Chapter C API Overview

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.