chapter 26 Extending Your Application

来源:互联网 发布:网络诈骗的小品剧本 编辑:程序博客网 时间:2024/06/04 19:03
An important use of Lua is as a configuration language. In this chapter, we will
illustrate how we can use Lua to configure a program, starting with a simple

example and evolving it to perform increasingly complex tasks.


26.1 The Basics

As our first task, let us imagine a simple configuration scenario: your C program
has a window and you want the user to be able to specify the initial window size.
Clearly, for such simple task, there are several options simpler than using Lua,
such as environment variables or files with name-value pairs. But even using
a simple text file, you have to parse it somehow; so, you decide to use a Lua
configuration file (that is, a plain text file that happens to be a Lua program). In
its simplest form, this file can contain something like the next lines:


-- define window size
width = 200
height = 300


Now, you must use the Lua API to direct Lua to parse this file, and then
to get the values of the global variables width and height. Function load, in
Listing 26.1, does the job. The function assumes that you have already created a
Lua state, following what we saw in the previous chapter. It calls luaL_loadfile
to load the chunk from file fname, and then calls lua_pcall to run the compiled
chunk. In case of errors (e.g., a syntax error in your configuration file), these
functions push the error message onto the stack and return a non-zero error
code; our program then uses lua_tostring with index 1 to get the message
from the top of the stack. (We defined the error function in Section 25.1.)


Listing 26.1. Getting user information from a configuration file:


void load (lua_State *L, const char *fname, int *w, int *h) {
if (luaL_loadfile(L, fname) || lua_pcall(L, 0, 0, 0))
    error(L, "cannot run config. file: %s", lua_tostring(L, -1));
    lua_getglobal(L, "width"); --告诉Lua 我要拿global variable width and hegith, 然后
    lua_getglobal(L, "height");--Lua 就会把value put into the Stack,
    if (!lua_isnumber(L, -2))
    error(L, "'width' should be a number\n");
    if (!lua_isnumber(L, -1))
    error(L, "'height' should be a number\n");
    *w = lua_tointeger(L, -2);
    *h = lua_tointeger(L, -1);  --其实拿完value 后个人认为最好pop up the value.
}









After running the chunk, the program needs to get the values of the global
variables. For that, it calls lua_getglobal twice, whose single parameter (besides
the omnipresent lua_State) is the variable name. Each call pushes the
corresponding global value onto the stack, so that the width will be at index 2
and the height at index 1 (at the top). (Because the stack was previously empty,
you could also index from the bottom, using the index 1 for the first value and 2
for the second. By indexing from the top, however, your code does not need to assume
an empty stack.) Next, our example uses lua_isnumber to check whether
each value is numeric. It then calls lua_tointeger to convert such values to
integer, assigning them to their respective positions.



Is it worth using Lua for that task? As I said before, for such simple task, a
simple file with only two numbers in it would be easier to use than Lua. Even so,
the use of Lua brings some advantages. First, Lua handles all syntax details for
you; your configuration file can even have comments! Second, the user is already
able to do some complex configurations with it. For instance, the script may
prompt the user for some information, or it can query an environment variable
to choose a proper size:


-- configuration file
if getenv("DISPLAY") == ":0.0" then
width = 300; height = 300
else
width = 200; height = 200
end




26.2 Table Manipulation

Let us adopt that attitude: now, we want to configure a background color for
the window, too.


-- configuration file
width = 200
height = 300

background = {r=0.30, g=0.10, b=0}


BLUE = {r=0, g=0, b=1.0}
<other color definitions>
background = BLUE  --use the predefine color.


To get these values in C, we can do as follows:
lua_getglobal(L, "background"); --tell Lua C want background variable in the _GS, then Lua will put to the stack.
  if (!lua_istable(L, -1))
     error(L, "'background' is not a table");
  red = getcolorfield(L, "r");
  green = getcolorfield(L, "g");
  blue = getcolorfield(L, "b");



Listing 26.2. A particular getcolorfield implementation


#define MAX_COLOR 255
/* assume that table is on the stack top */
int getcolorfield (lua_State *L, const char *key)
{
    int result;
    lua_pushstring(L, key); /* push key */
    lua_gettable(L, -2); /* get background[key] */
    if (!lua_isnumber(L, -1))
    error(L, "invalid component in background color");
    result = (int)(lua_tonumber(L, -1) * MAX_COLOR);
    lua_pop(L, 1); /* remove number */
    return result;
}


           [key]  -1

stack :[table]  -2, when   lua_pushstring(L, key); /* push key */, and call    lua_gettable(L, -2);, Lua will return the table's key's corespondence value. that's push to the top.

......................



stack :   [table[key]]

              [table]






The Lua API offers one function, lua_gettable, that works for
all types. It takes the position of the table in the stack, pops the key from the
stack, and pushes the corresponding value. Our private getcolorfield, defined
in Listing 26.2, assumes that the table is at the top of the stack; so, after pushing
the key with lua_pushstring, the table will be at index 2. Before returning,
getcolorfield pops the retrieved value from the stack, leaving the stack at the
same level that it was before the call.



Because indexing a table with a string key, 注意是string key, is so common, Lua 5.1 introduced
a specialized version of lua_gettable for this case: lua_getfield. Using this
function, we can rewrite the two lines
   lua_pushstring(L, key);
   lua_gettable(L, -2); /* get background[key] */
as
   lua_getfield(L, -1, key);


还是需要我们pop 还原 table 的位置,,,,????No need...

(As we do not push the string onto the stack, the table index is still 1 when we
call lua_getfield.)



We will extend our example a little further and introduce color names for the
user. The user can still use color tables, but she can also use predefined names
for the more common colors. To implement this feature, we need a color table in
our C application:


struct ColorTable
{
    char *name;
    unsigned char red, green, blue;
} colortable[] = {
                    {"WHITE", MAX_COLOR, MAX_COLOR, MAX_COLOR},
                    {"RED", MAX_COLOR, 0, 0},
                    {"GREEN", 0, MAX_COLOR, 0},
                    {"BLUE", 0, 0, MAX_COLOR},
                    <other colors>
                    {NULL, 0, 0, 0} /* sentinel */
};



Our implementation will create global variables with the color names and
initialize these variables using color tables. The result is the same as if the user
had the following lines in her script:
WHITE = {r=1.0, g=1.0, b=1.0}
RED = {r=1.0, g=0, b=0}
<other colors>.............................


To set the table fields, we define an auxiliary function, setcolorfield; it
pushes the index and the field value on the stack, and then calls lua_settable:

/* assume that table is at the top */
void setcolorfield (lua_State *L, const char *index, int value) {
lua_pushstring(L, index); /* key */
lua_pushnumber(L, (double)value / MAX_COLOR); /* value */
lua_settable(L, -3); --那样是设置 stack 里面的 table,那 global variable 里面对应的table 是否被设置了呢?

                               --the answer is NO  setsetcolor function.

}


Like other API functions, lua_settable works for many different types,so it gets
all its operands from the stack.
It takes the table index as an argument and pops
the key and the value
,Stack 果然是无所不在,,,,The setcolorfield function assumes that before the call
the table is at the top of the stack (index 1); after pushing the index and the
value, the table will be at index 3.


Lua 5.1 also introduced a specialized version of lua_settable for string keys,
called lua_setfield. Using this new function, we can rewrite our previous
definition for setcolorfield as follows:
void setcolorfield (lua_State *L, const char *index, int value) {
lua_pushnumber(L, (double)value / MAX_COLOR);
lua_setfield(L, -2, index);
}


The next function, setcolor, defines a single color. It creates a table, sets the
appropriate fields, and assigns this table to the corresponding global variable:


void setcolor (lua_State *L, struct ColorTable *ct) {
    lua_newtable(L); /* creates a table, and push to the Stack on top */
    setcolorfield(L, "r", ct->red); /* table.r = ct->r */
    setcolorfield(L, "g", ct->green); /* table.g = ct->g */
    setcolorfield(L, "b", ct->blue); /* table.b = ct->b */
    lua_setglobal(L, ct->name); /* 'name' = table */
}


The lua_newtable function creates an empty table and pushes it on the stack;
the calls to setcolorfield set the table fields; finally, lua_setglobal pops the
table and sets it as the value of the global with the given name.

With these previous functions, the following loop will register all colors for
the configuration script:


int i = 0;
while (colortable[i].name != NULL)
setcolor(L, &colortable[i++]);


Remember that the application must execute this loop before running the script.

Listing 26.3 shows another option for implementing named colors. Instead
of global variables, the user can denote color names with strings, writing her
settings as background="BLUE". Therefore, background can be either a table or

a string. With this design, the application does not need to do anything before
running the user’s script. Instead, it needs more work to get a color. When it
gets the value of variable background, it must test whether the value has type
string, and then look up the string in the color table.


Listing 26.3. Colors as strings or tables:


lua_getglobal(L, "background");
if (lua_isstring(L, -1))  /* value is a string? */
{
        const char *name = lua_tostring(L, -1); /* get string */
        int i; /* search the color table */
        for (i = 0; colortable[i].name != NULL; i++)
        {
        if (strcmp(colorname, colortable[i].name) == 0)
        break;
        }
        if (colortable[i].name == NULL) /* string not found? */
        error(L, "invalid color name (%s)", colorname);
        else
        { /* use colortable[i] */
        red = colortable[i].red;
        green = colortable[i].green;
        blue = colortable[i].blue;
        }
}
else if (lua_istable(L, -1))
{
    red = getcolorfield(L, "r");
    green = getcolorfield(L, "g");
    blue = getcolorfield(L, "b");
}
else
error(L, "invalid value for 'background'");




26.3 Calling Lua Functions

A great strength of Lua is that a configuration file can define functions to be
called by the application. For instance, you can write an application to plot the
graph of a function and use Lua to define the function to be plotted. ,用LUA 去定义函数,用C 去作图。



The API protocol to call a function is simple: first, you push the function to
be called; second, you push the arguments to the call; then you use lua_pcall to
do the actual call; finally, you get the results from the stack.


As an example, let us assume that your configuration file has a function like
this:
   function f (x, y)
       return (x^2 * math.sin(y)) / (1 - x)
   end


You want to evaluate, in C, z=f(x,y) for given x and y. Assuming that you
have already opened the Lua library and run the configuration file, function f in
Listing 26.4 encapsulates this call.


Listing 26.4. Calling a Lua function from C:


/* call a function 'f' defined in Lua */
double f (lua_State *L, double x, double y)
{
    int isnum;
    double z;
    /* push functions and arguments */
    lua_getglobal(L, "f"); /* function to be called */
    lua_pushnumber(L, x); /* push 1st argument */
    lua_pushnumber(L, y); /* push 2nd argument */
    /* do the call (2 arguments, 1 result) */
    if (lua_pcall(L, 2, 1, 0) != LUA_OK)
    error(L, "error running function 'f': %s",
    lua_tostring(L, -1));
    /* retrieve result */
    z = lua_tonumberx(L, -1, &isnum);
    if (!isnum)
    error(L, "function 'f' must return a number");
    lua_pop(L, 1); /* pop returned value */
    return z;
}


The second and third arguments to lua_pcall are the number of arguments
you are passing and the number of results you want.
The fourth argument
indicates an error-handling function; we will discuss it in a moment. As in a Lua
assignment, lua_pcall adjusts the actual number of results to what you have
asked for, pushing nils or discarding extra values as needed. Before pushing
the results, lua_pcall removes from the stack the function and its arguments.
When a function returns multiple results, the first result is pushed first
; for

instance, if there are three results, the first one will be at index 3 and the last
at index 1.


If there is any error while lua_pcall is running, lua_pcall returns an error
code; moreover, it pushes the error message on the stack (but still pops the function
and its arguments).
Before pushing the message,也就如果发生错误,在把错误信息push to Stack 前,还是要把function and arguments pop 出来的。  however, lua_pcall calls
the message handler function, if there is one. To specify a message handler function,
we use the last argument of lua_pcall. A zero means no message handler
function; that is, the final error message is the original message. Otherwise, this
argument should be the index in the stack where the message handler function
is located. In such cases, the handler must be pushed in the stack somewhere
below the function to be called and its arguments
.这个错误处理函数要先于被call function and its argument 前被放入stack.


For normal errors, lua_pcall returns the error code LUA_ERRRUN.Two special
kinds of errors deserve different codes, because they never run the message
handler.
The first kind is a memory allocation error. For such errors, lua_pcall
always returns LUA_ERRMEM. The second kind is an error while Lua is running the
message handler itself. In this case, it is of little use to call the message handler
again, so lua_pcall returns immediately with a code LUA_ERRERR. Lua 5.2
differentiates a third kind of error: when a finalizer throws an error, lua_pcall
returns the code LUA_ERRGCMM (error in a GC metamethod). This code indicates
that the error is not directly related to the call itself.

也就是LUA_ERRMEM and LUA_ERRGCMM 发生的话是不会callmessage handler


26.4 A Generic Call Function

As a more advanced example, we will build a wrapper for calling Lua functions,
using the vararg facility in C. Our wrapper function, let us call it call_va,
takes the name of the function to be called, a string describing the types of the
arguments and results, then the list of arguments, and finally a list of pointers
to variables to store the results; it handles all the details of the API.
With this
function, we could write our previous example simply like this:
call_va(L, "f", "dd>d", x, y, &z);



The string “dd>d” means “two arguments of type double, one result of type
double”. This descriptor can use the letters ‘d’ for double, ‘i’ for integer, and
‘s’ for strings; a ‘>’ separates arguments from the results. If the function has no
results, the ‘>’ is optional.



Listing 26.5 shows the implementation of function call_va. Despite its
generality, this function follows the same steps of our first example: it pushes the
function, pushes the arguments (Listing 26.6), does the call, and gets the results
(Listing 26.7). Most of its code is straightforward, but there are some subtleties.
First, it does not need to check whether func is a function; lua_pcall will trigger
any error. Second, because it pushes an arbitrary number of arguments, it must
check the stack space. Third, because the function can return strings, call_va

cannot pop the results from the stack. It is up to the caller to pop them, after
it finishes using occasional string results (or after copying them to appropriate
buffers).


Listing 26.5. A generic call function:


#include <stdarg.h>
void call_va (lua_State *L, const char *func,
const char *sig, ...) {
va_list vl;
int narg, nres; /* number of arguments and results */
va_start(vl, sig);
lua_getglobal(L, func); /* push function */
<push arguments (Listing 26.6)>
nres = strlen(sig); /* number of expected results */
if (lua_pcall(L, narg, nres, 0) != 0) /* do the call */
error(L, "error calling '%s': %s", func,
lua_tostring(L, -1));
<retrieve results (Listing 26.7)>
va_end(vl);
}


Listing 26.6. Pushing arguments for the generic call function:

for (narg = 0; *sig; narg++) { /* repeat for each argument */
/* check stack space */
luaL_checkstack(L, 1, "too many arguments");
switch (*sig++) {
case 'd': /* double argument */
lua_pushnumber(L, va_arg(vl, double));
break;
case 'i': /* int argument */
lua_pushinteger(L, va_arg(vl, int));
break;
case 's': /* string argument */
lua_pushstring(L, va_arg(vl, char *));
break;
case '>': /* end of arguments */
goto endargs;
default:
error(L, "invalid option (%c)", *(sig - 1));
}
}
endargs:


Listing 26.7. Retrieving results for the generic call function:


nres = -nres; /* stack index of first result */
while (*sig)
{ /* repeat for each result */
    switch (*sig++)
    {
        case 'd': { /* double result */
        int isnum;
        double n = lua_tonumberx(L, nres, &isnum);
        if (!isnum)
        error(L, "wrong result type");
        *va_arg(vl, double *) = n;
        break;
        }
        case 'i': { /* int result */
        int isnum;
        int n = lua_tointegerx(L, nres, &isnum);
        if (!isnum)
        error(L, "wrong result type");
        *va_arg(vl, int *) = n;
        break;
        }
        case 's': { /* string result */
        const char *s = lua_tostring(L, nres);
        if (s == NULL)
        error(L, "wrong result type");
        *va_arg(vl, const char **) = s;
        break;
        }
        default:
        error(L, "invalid option (%c)", *(sig - 1));
    }
    nres++;
}


I need to get better understand of va_start etc...will be continue ......







































































0 0
原创粉丝点击