PIL--24 - An Overview of the C API C API概览

来源:互联网 发布:mac双系统bootcamp联网 编辑:程序博客网 时间:2024/05/20 13:10
24 - An Overview of the C API C API概览
Lua is an embedded language. That means that Lua is not a stand-alone package, but a library that can be linked with other applications so as to incorporate Lua facilities into these applications.
Lua 是一个嵌入式语言。这意味着lua不是一个独立包,而是能够被链接到其他应用的库,从而在其他应用中可以利用lua的功能。
You may be wondering: If Lua is not a stand-alone program, how come we have been using Lua stand alone through the whole book? The solution to this puzzle is the Lua interpreter (the executable lua). This interpreter is a tiny application (with less than five hundred lines of code) that uses the Lua library to implement the stand-alone interpreter. This program handles the interface with the user, taking her files and strings to feed them to the Lua library, which does the bulk of the work (such as actually running Lua code).
你可能很惊奇:如果lua不是一个独立的程序,那我们如何能够在书中一直独立的使用lua?谜题的答案是lua解释器(可执行的lua)。这个解释器是应用lua的库实现独立解释器的极小的程序(少于500行代码)。该程序处理用户接口,将字符串和文件送给lua库,库作了大量的工作(比如真正的运行lua代码)。
This ability to be used as a library to extend an application is what makes Lua an extension language. At the same time, a program that uses Lua can register new functions in the Lua environment; such functions are implemented in C (or another language) and can add facilities that cannot be written directly in Lua. This is what makes Lua an extensible language.
作为可以扩展应用能力的库使得lua变成可扩展语言。同时,使用lua的程序可以注册新函数到lua环境;这些函数使用C(或者另一种语言)写成,能够加入不能直接用lua实现的功能,这使得lua变成一个可扩充语言。
These two views of Lua (as an extension language and as an extensible language) correspond to two kinds of interaction between C and Lua. In the first kind, C has the control and Lua is the library. The C code in this kind of interaction is what we call application code. In the second kind, Lua has the control and C is the library. Here, the C code is called library code. Both application code and library code use the same API to communicate with Lua, the so called C API.
这两种视图(作为可扩展语言也可以作为可扩充语言)对应于c和lua间的两种交互。在第一种情况下,c作为控制和lua作为库。在这种交互形式下的c代码我们称之为应用代码。在第二种情况下,lua作为控制和c作为库。这时候c作为库代码。应用代码和库代码使用了同样的被称为C API的API同lua交互。
The C API is the set of functions that allow C code to interact with Lua. It comprises functions to read and write Lua global variables, to call Lua functions, to run pieces of Lua code, to register C functions so that they can later be called by Lua code, and so on. (Throughout this text, the term "function" actually means "function or macro". The API implements several facilities as macros.)
C API是允许C代码同lua交互的函数集。包括读写lua的全局变量,调用lua函数,运行lua代码片断,注册可以被lua调用的C函数等等。(在整本书中,术语函数实际上是指”函数和宏”,该API用宏实现了几个功能)
The C API follows the C modus operandi, which is quite different from Lua. When programming in C, we must care about type checking (and type errors), error recovery, memory-allocation errors, and several other sources of complexity. Most functions in the API do not check the correctness of their arguments; it is your responsibility to make sure that the arguments are valid before calling a function. If you make mistakes, you can get a "segmentation fault" error or something similar, instead of a well-behaved error message. Moreover, the API emphasizes flexibility and simplicity, sometimes at the cost of ease of use. Common tasks may involve several API calls. This may be boring, but it gives you full control over all details, such as error handling, buffer sizes, and the like.
C API允许与lua用法十分不同的C惯用法,当C编程的时候,我们必须关注类型检查(和类型错误),错误恢复,内存分配错误以及其他几个导致复杂的源头。API中绝大部分函数不检查参数的正确性;确保调用函数前的参数的正确性是用户的责任。如果你犯错,不会得到一个行为良好的错误消息,而是会得到一个段错误或者类似的东西。而且,API强调灵活性和简单性,付出代价就是易用性了。普通的任务可能涉及到几个API调用,可能很烦琐,但给了你对细节的完全控制,比如错误处理,缓冲区大小这些。
As its title says, the goal of this chapter is to give an overview of what is involved when you use Lua from C. Do not bother understanding all the details of what is going on now. Later we will fill in the details. Nevertheless, do not forget that you can find more details about specific functions in the Lua reference manual. Moreover, you can find several examples of the use of the API in the Lua distribution itself. The Lua stand-alone interpreter (lua.c) provides examples of application code, while the standard libraries (lmathlib.c, lstrlib.c, etc.) provide examples of library code.
如标题所言,本章是给出当从C中使用lua涉及什么的概论。不要为理解下面将要讨论的所有细节所困惑,后面我们会补充细节。尽管如此,不要忘记你可以从lua参考手册中发现特定函数的更多细节。此外,你可以发现lua发布版本身所带的几个使用API的例子代码,Lua独立解释器(lua.c)提供了应用代码的例子,同时标准库(lmathlib.c,lstrlib.c等)提供了库代码的例子。
From now on, we are wearing a C programmers' hat. When we talk about "you", we mean you when programming in C, or you impersonated by the C code you write.
从现在开始,我们就戴着c程序员的帽子(从c程序员的角度对待lua)。当我们谈到“你”,我们指用c编程的你,或者你写的人格化的c代码的。
A major component in the communication between Lua and C is an omnipresent virtual stack. Almost all API calls operate on values on this stack. All data exchange from Lua to C and from C to Lua occurs through this stack. Moreover, you can use the stack to keep intermediate results too. The stack helps to solve two impedance mismatches between Lua and C: The first is caused by Lua being garbage collected, whereas C requires explicit deallocation; the second results from the shock between dynamic typing in Lua versus the static typing of C. We will discuss the stack in more detail in Section 24.2.
Lua和C间的通讯的一个主要部分就是无处不在的虚拟栈。几乎所有的API调用都操作栈上的值。从lua到C和从C到Lua的数据交换都通过这个栈。此外,你也可以使用这个栈来保存中间结果。栈解决了Lua和C之间的不匹配:第一个是由Lua的垃圾收集引起的,由于C需要显示的释放;第二个是由于Lua的动态类型和C的静态类型冲突引起的。我们会在24.2节讨论栈的细节。
24.1 - A First Example一个例子
We will start this overview with a simple example of an application program: a stand-alone Lua interpreter. We can write a primitive stand-alone interpreter as follows:
我们将以一个简单的应用程序例子开始综述:
    #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();   /* opens Lua 打开Lua*/
      luaopen_base(L);             /* opens the basic library 打开基本库*/
      luaopen_table(L);            /* opens the table library 打开表库*/
      luaopen_io(L);               /* opens the I/O library 打开I/O库*/
      luaopen_string(L);           /* opens the string lib. 代开字符串库*/
      luaopen_math(L);             /* opens the math lib. 打开数学库*/
    
      while (fgets(buff, sizeof(buff), stdin) != NULL) {
        error = luaL_loadbuffer(L, buff, strlen(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;
    }
The header file lua.h defines the basic functions provided by Lua. That includes functions to create a new Lua environment (such as lua_open), to invoke Lua functions (such as lua_pcall), to read and write global variables in the Lua environment, to register new functions to be called by Lua, and so on. Everything defined in lua.h has the lua_ prefix.
头文件lua.h定义了lua提供的基本函数。包括创建新的Lua环境的函数(比如lua_open),调用Lua函数(比如lua_pcall),读写Lua环境中的全局变量,注册可被Lua调用的新函数等等。在lua.h中所有定义都有lua_前缀。
The header file lauxlib.h defines the functions provided by the auxiliary library (auxlib). All its definitions start with luaL_ (e.g., luaL_loadbuffer). The auxiliary library uses the basic API provided by lua.h to provide a higher abstraction level; all Lua standard libraries use the auxlib. The basic API strives for economy and orthogonality, whereas auxlib strives for practicality for common tasks. Of course, it is very easy for your program to create other abstractions that it needs, too. Keep in mind that the auxlib has no access to the internals of Lua. It does its entire job through the official basic API.
头文件luaxlib.h定义了辅助库(auxlib)提供的函数。所有定义以luaL_开始(比如luaL_loadbuffer)。辅助库使用了lua.h提供的基本API来提供更高一层的抽象,所有的Lua标准库使用auxlib。基本API是简洁并正交的,auxlib库则注重通常任务的实际性。当然,你的程序也可以很容易的按照需要创建其他抽象。牢记auxlib没有存取Lua的核心,它完全通过官方的基本API来实现的。
The Lua library defines no global variables at all. It keeps all its state in the dynamic structure lua_State and a pointer to this structure is passed as an argument to all functions inside Lua. This implementation makes Lua reentrant and ready to be used in multithreaded code.
Lua库没有定义任何全局变量,它的所有状态都保存在lua_State的动态结构中,指向结构的指针被作为参数传递到Lua内部的所有函数中。这种实现使得Lua可重入,可以被使用于多线程代码。
The lua_open function creates a new environment (or state). When lua_open creates a fresh environment, this environment contains no predefined functions, not even print. To keep Lua small, all standard libraries are provided as separate packages, so that you do not have to use them if you do not need to. The header file lualib.h defines functions to open the libraries. The call to luaopen_io, for instance, creates the io table and registers the I/O functions (io.read, io.write, etc.) inside it.
lua_open函数创建了一个新环境(或者状态)。当lua_open创建新环境,环境未包含预定义的函数,即使print也没有。为了保持Lua小巧,所有标准库都是做为包的形式提供的,在不需要的时候可以不使用。Lualib.h头文件定义了打开库的函数。举例来说,luaopen_io调用就会创建io表并注册I/O函数(io.read,io.write等)到Lua环境中。
After creating a state and populating it with the standard libraries, it is time to interpret the user input. For each line the user enters, the program first calls luaL_loadbuffer to compile the code. If there are no errors, the call returns zero and pushes the resulting chunk on the stack. (Remember that we will discuss this "magic" stack in detail in the next section.) Then the program calls lua_pcall, which pops the chunk from the stack and runs it in protected mode. Like luaL_loadbuffer, lua_pcall returns zero if there are no errors. In case of errors, both functions push an error message on the stack; we get this message with lua_tostring and, after printing it, we remove it from the stack with lua_pop.
创建了状态并且打开了标准库后,就可以解释用户输入了。对用户输入的每一行,程序首先调用luaL_loadbuffer编译代码。如果没有错误,调用返回0,并且把结果片段(chunk)推入栈内(我们下一节会仔细讨论魔法栈)。然后程序调用lua_pcall从栈中弹出段并在保护模式下执行该段。如同luaL_loadbuffer、lua_pcall一样,如果没有错误就返回0。如果出错,这些函数都把一个错误消息推入栈内,我们使用lua_tostring获得该消息,在打印后就可以用lua_pop把它从栈上移除。
Notice that, in case of errors, this program simply prints the error message to the standard error stream. Real error handling can be quite complex in C and how to do it depends on the nature of your application. The Lua core never writes anything directly to any output stream; it signals errors by returning error codes and error messages. Each application can handle these signals in a way most appropriate for its needs. To simplify our discussions, we will assume for now a simple error handler like the following one, which prints an error message, closes the Lua state, and exits from the whole application:
请注意在错误的情况下,程序只是简单的把错误消息打印到标准错误流。C中真正的错误处理程序可能十分复杂,依赖于应用程序的特性。Lua核心从不向任何输出流写任何东西;它只是通过错误码和错误消息来标志错误发生。每个应用程序可以用适合需要的办法来处理这些标志。为了简化讨论,我们现在假定一个如下的简单的错误处理程序,打印错误消息,关闭Lua状态然后退出整个应用。
    #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);
    }
Later we will discuss more about error handling in the application code.
后面我们会讨论更多的应用代码中的错误处理。
Because you can compile Lua both as C and as C++ code, lua.h does not include this typical adjustment code that is present in several other C libraries:
因为你可以用C或者C++方式编译Lua,lua.h不包含几个其他C库中常见的典型调整代码。
    #ifdef __cplusplus
    extern "C" {
    #endif
       ...
    #ifdef __cplusplus
    }
    #endif
Therefore, if you have compiled Lua as C code (the most common case) and are using it in C++, you must include lua.h as follows:
因此,如果你已经把Lua作为C代码编译(最普遍的情况)并在C++中使用,你必须用如下方式包含lua.h:
    extern "C" {
    #include <lua.h>
    }
A common trick is to create a header file lua.hpp with the above code and to include this new file in your C++ programs.
一个常用的技巧就是创建一个包含上述代码的lua.hpp的头文件,并将该头文件包含到你的C++程序中。
24.2 - The Stack
We face two problems when trying to exchange values between Lua and C: the mismatch between a dynamic and a static type system and the mismatch between automatic and manual memory management.
当在Lua和C之间交换值的时候,我们面临两个问题:动态和静态类型系统间的不匹配以及自动和手动内存管理之间的不匹配。
In Lua, when we write a[k] = v, both k and v can have several different types (even a may have different types, due to metatables). If we want to offer this operation in C, however, any settable function must have a fixed type. We would need dozens of different functions for this single operation (one function for each combination of types for the three arguments).
在Lua中,我们写 a[k]=v的时候,k和v能有几个不同的类型(由于元表,甚至一个可以有不同类型)。如果我们想要在C中提供如此操作,然而,任何settable函数必须有固定类型,导致我们需要一打不同的函数来提供这个操作(三个参数的类型每一种组合都需要一个函数)。
We could solve this problem by declaring some kind of union type in C, let us call it lua_Value, that could represent all Lua values. Then, we could declare settable as
我们可以通过在C中声明一些组合类型的方式解决这个问题,我们称之为lua_Value,这种类型可以表示所有的Lua值。现在我们能用下面的方式声明settable:
    void lua_settable (lua_Value a, lua_Value k, lua_Value v);
This solution has two drawbacks. First, it can be difficult to map such a complex type to other languages; Lua has been designed to interface easily not only with C/C++, but also with Java, Fortran, and the like. Second, Lua does garbage collection: If we keep a Lua value in a C variable, the Lua engine has no way to know about this use; it may (wrongly) assume that this value is garbage and collect it.
这个解决方案有两个缺点:首先,难于映射如此复杂的类型到其他语言中去;Lua设计不光需要跟C/C++接口容易,也需要跟java,fortran这些语言。其次,Lua有垃圾收集:如果我们在C变量中保存Lua值,Lua引擎无法知道这种使用,它可能错误的假定该值是垃圾并收集它。
Therefore, the Lua API does not define anything like a lua_Value type. Instead, it uses an abstract stack to exchange values between Lua and C. Each slot in this stack can hold any Lua value. Whenever you want to ask for a value from Lua (such as the value of a global variable), you call Lua, which pushes the required value on the stack. Whenever you want to pass a value to Lua, you first push the value on the stack, and then you call Lua (which will pop the value). We still need a different function to push each C type on the stack and a different function to get each value from the stack, but we avoid the combinatorial explosion. Moreover, because this stack is managed by Lua, the garbage collector knows which values C is using.
因此,Lua API不定以任何类似lua_Value类型的东西。取而代之的是它使用一个抽象栈在Lua和C之间交换值。栈中的每个槽可以保存任意的Lua值。当你想要从Lua中获取一个值的时候(比如一个全局变量的值),你调用Lua把要求的值推到栈上。当你想要把一个值传递给Lua的时候,你首先把值推到栈上,然后调用Lua(Lua从栈上弹出值)。
我们仍然需要不同的函数将每一种C类型推到栈上以及不同的函数从栈上获取每一个值,但组合爆炸被避免了。而且,由于栈由Lua管理,垃圾收集器知道C正在使用哪一个值。
Nearly all functions in the API use the stack. As we saw in our first example, luaL_loadbuffer leaves its result on the stack (either the compiled chunk or an error message); lua_pcall gets the function to be called from the stack and leaves any occasional error message there.
几乎API中的所有函数都使用栈。如我们在第一个例子中所见,luaL_loadbuffer将结果留在栈中(或者被编译的片断或者错误消息);lua_pcall从栈上得到被调用的函数并留下任何临时错误消息。
Lua manipulates this stack in a strict LIFO discipline (Last In, First Out; that is, always through the top). When you call Lua, it only changes the top part of the stack. Your C code has more freedom; specifically, it can inspect any element inside the stack and even insert and delete elements in any arbitrary position.
Lua以严格的LIFO的规程操作栈(后进先出,也就是总是通过栈顶)。当调用Lua的时候,它仅仅交换栈顶。你的C代码有更多的自由;特别的,它可以监测栈里的任何元素,甚至在任意位置插入和删除元素。
24.2.1 - Pushing Elements推元素
The API has one push function for each Lua type that can be represented in C: lua_pushnil for the constant nil, lua_pushnumber for numbers (double), lua_pushboolean for booleans (integers, in C), lua_pushlstring for arbitrary strings (char *), and lua_pushstring for zero-terminated strings:
API为每一个Lua类型都提供了一个C语言的push函数:lua_pushnil用于常量nil,lua_pushnumber用于数字(双精度),lua_pushboolean用于布尔值(C中的整型),lua_pushlstring用于任意字符串(char*),以及lua_pushstring用于零结尾的字符串:
    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_t length);
    void lua_pushstring (lua_State *L, const char *s);
There are also functions to push C functions and userdata values on the stack; we will discuss them later.
也有用于推C函数和用户数据userdata到栈上的函数,我们稍候讨论。
Strings in Lua are not zero-terminated; in consequence, they can contain arbitrary binary data and rely on an explicit length. The official function to push a string onto the stack is lua_pushlstring, which requires an explicit length as an argument. For zero-terminated strings, you can use also lua_pushstring, which uses strlen to supply the string length. Lua never keeps pointers to external strings (or to any other object, except to C functions, which are always static). For any string that it has to keep, Lua either makes an internal copy or reuses one. Therefore, you can free or modify your buffer as soon as these functions return.
Lua中的字符串是非零结尾的,因此,它们能包括任意的二进制数据并依赖于显示的长度。把字符串推到栈上的正式函数是lua_pushlstring,需要一个长度作为参数。对于零结束的字符串,你可以使用lua_pushstring,它使用了strlen来获得字符长度。Lua从来不持有外部串的指针(或者任何其他对象,除了总是静态的C函数)。对于必须持有的字符串,Lua或者内部拷贝一份或者重用一个。因此在这些函数返回的时候你可以释放或者修改这些缓存区。
Whenever you push an element onto the stack, it is your responsibility to ensure that the stack has space for it. Remember, you are a C programmer now; Lua will not spoil you. When Lua starts and any time that Lua calls C, the stack has at least 20 free slots (this constant is defined as LUA_MINSTACK in lua.h). This is more than enough for most common uses, so usually we do not even think about that. However, some tasks may need more stack space (e.g., for calling a function with a variable number of arguments). In such cases, you may want to call
每当你推一个元素到栈上,你有责任确认栈有空间可以容纳它。记住,现在你是C程序员,Lua不会宠着你的。当Lua启动和Lua调用C函数的时候,栈至少有20个可用槽(该常量在lua.h中由LUA_MINSTACK定义)。这对于对大多数使用来说都是足够的,所以我们甚至不用考虑它。然而,一些任务可能需要更多的栈空间(例如调用变参数函数),在那种情况下,你可能需要调用如下函数:
    int lua_checkstack (lua_State *L, int sz);
which checks whether the stack has enough space for your needs. (More about that later.)
这个函数检查栈是否有足够空间使用(后面有更多相关内容)
24.2.2 - Querying Elements查询元素
To refer to elements in the stack, the API uses indices. The first element in the stack (that is, the element that was pushed first) has index 1, the next one has index 2, and so on. We can also access elements using the top of the stack as our reference, using negative indices. In that case, -1 refers to the element at the top (that is, the last element pushed), -2 to the previous element, and so on. For instance, the call lua_tostring(L, -1) returns the value at the top of the stack as a string. As we will see, there are several occasions when it is natural to index the stack from the bottom (that is, with positive indices) and several other occasions when the natural way is to use negative indices.
为了访问栈中元素,API使用了索引。栈中的第一个元素(也就是说第一个被推进栈的元素)有索引1,下一个有索引2,依此类推。我们也可以使用栈顶作为参考以负索引来访问元素。这种情况下,-1指栈顶元素(最后一个被推进的元素),-2指前一个,依此类推。举例来说,调用lua_tostring(L,-1)将栈顶的值作为一个字符串返回。我们将要看到,有几种情况下采用栈底做为索引是很自然的(也就是说使用正的索引),其他几种情况下,使用负的索引是自然的方式。
To check whether an element has a specific type, the API offers a family of functions lua_is*, where the * can be any Lua type. So, there are lua_isnumber, lua_isstring, lua_istable, and the like. All these functions have the same prototype:
为了检测一个元素是否特定类型元素,API提供了一组lua_is*函数,其中*可以使任意的lua类型。所以,有lua_isnumber,lua_isstring,lua_istable诸如此类的函数。所有这些函数有同样的原型:
    int lua_is... (lua_State *L, int index);
The lua_isnumber and lua_isstring functions do not check whether the value has that specific type, but whether the value can be converted to that type. For instance, any number satisfies lua_isstring.
Lua_isnumber和lua_isstring函数不检查是否值是特定类型的,而是值是否可以被转换到该类型。例如,任何数字都满足lua_isstring。
There is also a function lua_type, which returns the type of an element in the stack. (Some of the lua_is* functions are actually macros that use this function.) Each type is represented by a constant defined in the header file lua.h: LUA_TNIL, LUA_TBOOLEAN, LUA_TNUMBER, LUA_TSTRING, LUA_TTABLE, LUA_TFUNCTION, LUA_TUSERDATA, and LUA_TTHREAD. This function is mainly used in conjunction with a switch statement. It is also useful when we need to check for strings and numbers without coercions.
也有一个lua_type函数可以返回栈上元素的类型(一些lua_is*函数其实是使用了该函数的宏).每个类型由头文件lua.h中的常量定义所表示:LUA_TNIL, LUA_TBOOLEAN, LUA_TNUMBER, LUA_TSTRING, LUA_TTABLE, LUA_TFUNCTION, LUA_TUSERDATA以及LUA_TTHREAD.该函数主要跟switch语句一起使用。在不强制转换的情况下,用于检查字符串和数字也是很有用的。
To get a value from the stack, there are the lua_to* functions:
为了从栈上取得一个值,有如下的lua_to*函数:
    int            lua_toboolean (lua_State *L, int index);
    double         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);
It is OK to call them even when the given element does not have the correct type. In this case, lua_toboolean, lua_tonumber and lua_strlen return zero and the others return NULL. The zero is not useful, but ANSI C provides us with no invalid numeric value that we could use to signal errors. For the other functions, however, we frequently do not need to use the corresponding lua_is* function: We just call lua_to* and then test whether the result is not NULL.
即使在给定元素没有正确的类型的情况下调用这些函数也没有问题。这种情况下,lua_toboolean,lua_tonumber和lua_strlen返回零,其他的返回NULL。0并无用处,但是ANSI C没有提供用于表示错误的非法数值。对于其他函数,我们常常不必调用相应的lua_is*函数,我们只需要调用lua_to*,然后测试结果是否为NULL。
The lua_tostring function returns a pointer to an internal copy of the string. You cannot change it (there is a const there to remind you). Lua ensures that this pointer is valid as long as the corresponding value is in the stack. When a C function returns, Lua clears its stack; therefore, as a rule, you should never store pointers to Lua strings outside the function that got them.
Lua_tostring函数返回一个指向字符串内部拷贝的指针,你不可以改变它(有一个const提醒你)。只要相应的值在栈上,Lua确保该指针的有效性。当C函数返回时,Lua清除栈,因此,作为一条规则,你不应当在得到Lua字符串的函数外面保存指针。
Any string that lua_tostring returns always has a zero at its end, but it can have other zeros inside it. The lua_strlen function returns the correct length of the string. In particular, assuming that the value at the top of the stack is a string, the following assertions are always valid:
lua_tostring返回的任何字符串都有一个零结尾,但是可以在内部包含其他的零。Lua_strlen函数返回该串的正确长度。特别的,假定栈顶的值是一个字符串,接下来的断言总是成立的。
    const char *s = lua_tostring(L, -1);   /* any Lua string */
    size_t l = lua_strlen(L, -1);          /* its length */
    assert(s[l] == '/0');
    assert(strlen(s) <= l);
24.2.3 - Other Stack Operations其他栈操作
Besides the above functions, which interchange values between C and the stack, the API offers also the following operations for generic stack manipulation:
除了上面的在C和栈之间交互值的函数外,API也提供了如下一些通用的栈操作:
    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 lua_gettop function returns the number of elements in the stack, which is also the index of the top element. Notice that a negative index -x is equivalent to the positive index gettop - x + 1.
Lua_gettop返回栈中元素个数,也就是栈顶元素的索引值。注意负的索引-x等价于正索引gettop-x+1。
lua_settop sets the top (that is, the number of elements in the stack) to a specific value. If the previous top was higher than the new one, the top values are discarded. Otherwise, the function pushes nils on the stack to get the given size. As a particular case, lua_settop(L, 0) empties the stack. You can also use negative indices with lua_settop; that will set the top element to the given index. Using this facility, the API offers the following macro, which pops n elements from the stack:
lua_settop设置栈顶(也就是栈中元素数目)到一个特定值位置。如果前一个栈顶比新的更高,那么栈顶值被抛弃。否则,函数推nil到栈上以达到指定尺寸。作为一个特别情况,lua_settop(L,0)清空栈。你也可以在lua_settop中使用负索引,这导致将栈顶元素放入制定索引。使用这个功能,API提供了如下的宏从栈中弹出n个元素:
    #define lua_pop(L,n) lua_settop(L, -(n)-1)
The lua_pushvalue function pushes on the top of the stack a copy of the element at the given index; lua_remove removes the element at the given index, shifting down all elements on top of that position to fill in the gap; lua_insert moves the top element into the given position, shifting up all elements on top of that position to open space; finally, lua_replace pops a value from the top and sets it as the value of the given index, without moving anything. Notice that the following operations have no effect on the stack:
Lua_pushvalue函数将给定索引处的元素的拷贝推到栈顶;lua_remove移除给定索引处的元素,该位置上面的元素下移以填补间隔;lua_insert将栈顶元素移动到给定位置,该位置顶部元素上移;最终,lua_replace从栈顶弹出值并把它作为给定位置处的值,不移动任何事物。注意接下来的操作对于栈没有副作用:
    lua_settop(L, -1); /* set top to its current value */
    lua_insert(L, -1); /* move top element to the top */
To illustrate the use of those functions, here is a useful helper function that dumps the entire content of the stack:
为了演示这些函数的使用,给出一个有用的辅助函数,该函数打印栈的所有内容:
    static void stackDump (lua_State *L) {
      int i;
      int top = lua_gettop(L);
      for (i = 1; i <= top; i++) { /* repeat for 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 a separator */
      }
      printf("/n"); /* end the listing */
    }
This function traverses the stack from bottom to top, printing each element according to its type. It prints strings between quotes; for numbers it uses a `%g´ format; for other values (tables, functions, etc.) it prints only their types (lua_typename converts a type code to a type name).
该函数自低而上的遍历栈,根据类型打印每一个元素。打印引号间的字符串,对数字使用%g格式,对于其他的值(表,函数等),只打印他们的类型(lua_typename将类型代码转换到类型名)
The following program uses stackDump to further illustrate the manipulation of the API stack:
接下来的程序使用stackDump更进一步的演示API栈的操纵:
    #include <stdio.h>
    #include <lua.h>
    
    static void stackDump (lua_State *L) {
      ...
    }
    
    int main (void) {
      lua_State *L = lua_open();
      lua_pushboolean(L, 1); lua_pushnumber(L, 10);
      lua_pushnil(L); lua_pushstring(L, "hello");
      stackDump(L);
                      /* true 10 nil `hello' */
    
      lua_pushvalue(L, -4); stackDump(L);
                       /* true 10 nil `hello' true */
    
      lua_replace(L, 3); stackDump(L);
                       /* true 10 true `hello' */
    
      lua_settop(L, 6); stackDump(L);
                       /* true 10 true `hello' nil nil */
    
      lua_remove(L, -3); stackDump(L);
                       /* true 10 true nil nil */
    
      lua_settop(L, -5); stackDump(L);
                       /* true */
    
      lua_close(L);
      return 0;
    }
24.3 - Error Handling with the C API使用C API进行错误处理
Unlike C++ or Java, the C language does not offer an exception handling mechanism. To ameliorate this difficulty, Lua uses the setjmp facility from C, which results in a mechanism similar to exception handling. (If you compile Lua with C++, it is not difficult to change the code so that it uses real exceptions instead.)
不像C++ 或者Java, C语言不提供异常处理机制。为改善该困难,Lua使用了C中的setjmp功能,形成了类似异常处理的机制。(如果你用C++编译Lua,改变代码以使用真正的异常并不困难。)
All structures in Lua are dynamic: They grow as needed, and eventually shrink again when possible. That means that the possibility of a memory-allocation failure is pervasive in Lua. Almost any operation may face this eventuality. Instead of using error codes for each operation in its API, Lua uses exceptions to signal these errors. That means that almost all API functions may throw an error (that is, call longjmp) instead of returning.
Lua中的所有结构都是动态的:它们按需增长,可能的时候缩小。这意味着内存分配失败的可能性到处都是,几乎任何操作都面临这个不测事件。Lua使用异常指示这些错误,而不是在API的每一个操作中使用错误代码。这意味着几乎所有API函数都可能抛出一个错误(被称为longjmp)而不是返回。
When we write library code (that is, C functions to be called from Lua), the use of long jumps is almost as convenient as a real exception-handling facility, because Lua catches any occasional error. When we write application code (that is, C code that calls Lua), however, we must provide a way to catch those errors.
当我们写库代码的时候(也就是说从lua中调用c函数),使用长跳转几乎如同真实的异常处理功能一样方便,因为Lua捕获任何偶然的错误。当我们写应用程序代码的时候(也就是说c代码调用lua函数),就必须提供方法捕获这些错误。
24.3.1 - Error Handling in Application Code应用代码的错误处理
Typically, your application code runs unprotected. Because its code is not called by Lua, Lua cannot set an appropriate context to catch errors (that is, it cannot call setjmp). In such environments, when Lua faces an error like "not enough memory", there is not much that it can do. It calls a panic function and, if the function returns, exits the application. (You can set your own panic function with the lua_atpanic function.)
典型的,应用代码无保护的运行。由于它的代码并没有被Lua调用,Lua无法设置合适的内容来捕捉错误(也就是不能调用setjmp)。在那样的环境下,当Lua面对一个诸如“没有足够内存”的错误的时候,无法作更多处理。它调用一个应急函数,如果函数返回,退出应用。(你可以用lua_atpanic设置你自己的应急函数。)
Not all API functions throw exceptions. The functions lua_open, lua_close, lua_pcall, and lua_load are all safe. Moreover, most other functions can only throw an exception in case of memory-allocation failure: For instance, luaL_loadfile fails if there is not enough memory for a copy of the file name. Several programs have nothing to do when they run out of memory, so they may ignore these exceptions. For those programs, if Lua runs out of memory, it is OK to panic.
并非所有API函数抛出异常。函数lua_open,lua_close,lua_pcall和lua_load都是安全的。而且,绝大部分其他函数也只能抛出内存分配失败的异常:例如,luaL_loadfile只有在没有拷贝一个文件名所用空间的时候才会失败。一些程序当无内存可用的时候什么也不错,所以他们可以忽略这些异常。对这些程序而言,如果Lua用完内存,应急是可行的。
If you do not want your application to exit, even in case of a memory-allocation failure, then you must run your code in protected mode. Most (or all) of your Lua code typically runs through a call to lua_pcall; therefore, it runs in protected mode. Even in case of memory-allocation failure, lua_pcall returns an error code, leaving the interpreter in a consistent state. If you also want to protect all your C code that interacts with Lua, then you can use lua_cpcall. (See the reference manual for further details of this function; see file lua.c in the Lua distribution for an example of its use.)
即使在内存分配失败的情况下,如果你不想程序退出,你必须在保护模式下运行代码。绝大部分(或者所有)你的Lua代码典型的通过一个lua_pcall调用运行,因而运行在保护模式下。甚至在内存分配失败的情况下,lua_pcall返回一个错误代码,留下解释器在一个一致的状态。如果你想要保护所有跟Lua交互的c代码,可以使用lua_pcall。(看参考手册获得该函数的更多细节,看Lua发行版中lua.c文件作为使用样例)
24.3.2 - Error Handling in Library Code库代码的错误处理
Lua is a safe language. That means that, no matter what you write, no matter how wrong it is, you can always understand the behavior of a program in terms of Lua itself. Moreover, errors are detected and explained in terms of Lua, too. You can contrast that with C, where the behavior of many wrong programs can only be explained in terms of the underling hardware and error positions are given as a program counter.
Lua是安全的语言。这意味着无论你写什么,无论写的多错,你总可以按照Lua本身的理解程序的行为。而且,错误也会按照Lua的方式被检测并且被解释。你可以将之与C比较,那些地方许多错误程序的行为只能按照底层硬件来解释,错误位置用程序计数器的方式给出。
Whenever you add new C functions to Lua, you can break that safety. For instance, a function like poke, which stores an arbitrary byte at an arbitrary memory address, can cause all sorts of memory corruption. You must strive to ensure that your add-ons are safe to Lua and provide good error handling.
无论何时你加新的C函数到Lua,你可能打破安全性。例如,一个类似poke的在任意一个内存地址存储一个任意字节的函数,能导致各种内存讹误。你必须努力确保你添加到Lua的东西是安全的并提供好的错误处理。
As we discussed earlier, each C program has its own way to handle errors. When you write library functions for Lua, however, there is a standard way to handle errors. Whenever a C function detects an error, it simply calls lua_error, (or better yet luaL_error, which formats the error message and then calls lua_error). The lua_error function clears whatever needs to be cleared in Lua and jumps back to the lua_pcall that originated that execution, passing along the error message.
如同前面讨论的,每个C程序有它自己的处理错误的方式。然而,当你写Lua的库函数的的时候,有一个标准的方法处理错误。无论何时C函数检测到错误,简单的调用lua_error(或者更好的方法是调用luaL_error来格式化错误消息然后调用lua_error)。lua_error清除Lua中所有需要清除的并跳回到引起这次执行的lua_pcall处,错误消息也沿其传送。
原创粉丝点击