The Lua C API

[ LiB ]

The Lua C API

Ah, the power of C. Anything that can be done directly in Lua can also be done in the Lua C API, including manipulating variables and tables, calling functions, controlling the garbage collector, or loading Lua from strings or files.

Typically, the Lua C library is compiled into an application or run as a shared library. This is the most common way of accessing Lua in a game program. Altogether, the Lua library is very small, so it is not uncommon to find the entire source tree included with a distributed game.

NOTE

TIP

If you want to delve deeper into the C family, check out C Programming for the Absolute Beginner , by Michael Vine, or C++ Programming for the Absolute Beginner , by Dirk Henkemans and Mark Lee.

Opening Up Lua

Before calling any API function, a pointer to the Lua state must be passed as the first argument. This pointer opens up Lua. The lua_open command (introduced in Chapter 6) is what fires up the Lua state. All API functions need to set lua_open up as their very first argument.

In order to use lua_open in a C environment, the lua.h file must be included. The lua.h file is a C header file that defines the Lua API. However, since Lua is ANSI C, any inclusions of the Lua library must be wrapped within an extern C command, otherwise the compiler will mangle the names and not be able to call the commands properly. This may sound complicated, but in practice it looks like this:

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

NOTE

Name Mangling

Compilers have a habit of modifying the names of functions and objects when compiling. This is done so that the compiler can include extra information, provide type linkage, and support function overloading. This modification is often called mangling . Particularly confusing is that each compiler has its own way of mangling names and laying out the compiled objects. This can cause problems when working with more than one language, as a second language cannot predict how a particular object or command may be mangled. Luckily, the extern command can be used to disable name mangling entirely.

When the Lua state machine is finished with its job, it should be closed using the lua_close() command. This command destroys all objects in the given Lua state via the garbage collector. Therefore, a full instance of Lua wrapped within C code looks something like this:

 extern "C" { #include <lua.h> } lua_state *Mylua lua_open (0) // Many lines of  // Useful Lua code that // Do something lua_close (MyLua) 

More or less, every function in the Lua API deals with the Lua state or the current state of the Lua interpreter (you will often hear Lua being referred to as a "state machine" when used in this way). The Lua state keeps track of functions, globals , and any interpreter- related information. When the Lua state is closed, all the Lua objects and any dynamic memory used by the state are freed.

Whenever Lua calls C, the called function gets a virtual stack. This stack contains any arguments to the C function, is used to pass values to and from C, and will hold any values the C functions push back. Stacks can hold more than one element and are represented by an index, the top element of which can be called with lua_gettop :

 Int lua_gettop (lua_State *L); 

NOTE

On some platforms, you may not need to call the close state, because resources are released normally when the program ends. Long-running programs or daemons may need to be released occasionally.

Stack Commands

Lua uses a stack to pass values to and from C. Each element in this stack represents a value ( nil , number , and so on) that Lua uses. The Lua API offers a number of useful commands for manipulating the stack, querying stack functions, and translating C to Lua. These commands are listed and summarized in Table 7.3.

Stack commands are normally given as arguments to the lua_State , a pointer to Lua ( *Lua ), and/or the appropriate index in the stack. Push functions receive a C value, convert it to a corresponding Lua value, and then push the result onto the stack.

The Lua stack is is the primary means of communication between C and Lua. There are no Lua type values in C, only functions that manipulate the stack. All values, functions, and so on are pushed onto or pulled from the stack.

Variables

Lua variables in the API do not need to be declared, and by default are considered global in scope unless specified otherwise. The variables that store Lua values are global values, local values, or table fields.

Local values can be declared anywhere within a block or chunk of Lua code. They are lexically scoped. This means the scope of variables begins at the first statement after their declaration and lasts until the end of the innermost block that includes the declaration.

Table 7.3. Lua API Stack Commands

Command

Type

Purpose

lua_concat ();

void

Concatenates the values at the top of a stack, pops them, and leaves the result at the top

lua_equal ();

int

Compares two items on the stack

lua_insert ();

void

Moves the top element to a given index

lua_isboolean ();

int

Returns 1 if the object is compatible, otherwise 0

lua_iscfunction ();

int

Returns 1 if the object is compatible, otherwise 0

lua_isfunction ();

int

Returns 1 if the object is compatible, otherwise 0

lua_isnil ();

int

Returns 1 if the object is compatible, otherwise 0

lua_isnumber ();

int

Returns 1 if the object is compatible, otherwise 0

lua_istable ();

int

Returns 1 if the object is compatible, otherwise 0

lua_isstring ();

int

Returns 1 if the object is compatible, otherwise 0

lua_isuserdata ();

int

Returns 1 if the object is compatible, otherwise 0

lua_islightuserdata ();

int

Returns 1 if the object is compatible, otherwise 0

lua_lessthan ();

int

Compares two items on the stack

lua_pushboolean ();

void

Pushes Boolean value onto the stack and returns a pointer to the Boolean

lua_pushcfunction ();

void

Pushes a C function onto the stack and returns a pointer to the function

lua_pushfstring ();

void

Pushes a formatted string onto the stack and returns a pointer to the string

lua_pushlightuserdata ();

void

Pushes light user data onto the stack and returns a pointer

lua_pushlstring ();

void

Makes an internal copy of given string, pushes, and returns a pointer to the string

lua_pushnil ();

void

Pushes a nil value onto the stack and returns a pointer to the value

lua_pushnumber ();

void

Pushes a numeric value onto the stack and returns a pointer to the number

lua_pushstring ();

void

Pushes proper C strings onto the stack and returns a pointer to the string

lua_pushvalue ();

void

Pushes a copy of an element to a given index

lua_pushvfstring ();

void

Pushes a string onto the stack and returns a pointer to the string

lua_rawequal ();

int

Compares values for primitive equality

lua_remove ();

void

Removes element at the given index

lua_replace ();

void

Replaces given index with given element

lua_settop ();

void

Sets the stack top to a given index

lua_State

struct

Dynamic structure that holds all Lua states

lua_totrhead();

int

Converts a value on the stack into a C thread

lua_strlen ();

int

Gets a string's length

lua_tocfunction ();

int

Converts a value on the stack into a C function

lua_tonumber ();

int

Converts a Lua value at given index to a C type number. Number is a double by default

lua_tostring ();

const char

Converts a Lua value at the given index to a C type string (in C a const *char )

lua_touserdata ();

void

Translates userdata to a specific C type

lua_type ();

int

Returns the type of a value in a stack


All global variables exist as fields in ordinary Lua tables called environment tables or simply environments . Functions written in C and exported to Lua all share a common global environment. Each function written in Lua has its own reference to an environment, so that all global variables in that function refer to that environment table. When a function is created, it inherits the environment from the function that created it.

Userdata

Userdata is used to represent C values. Lua supports two types, full userdata and light userdata. Full userdata represents a block of memory and light user data represents a pointer. Both are considered objects.

The lua_type command will return LUA_TUSERDATA for full userdata or LUA_TLIGHTUSERDATA for light userdata when checking an existing userdata. New userdata can be created with the lua_newuserdata () function:

 void *lua_newuserdata (lua_stat *MyLua, size_t size); 

This allocates a new memory block, pushes onto the stack a new userdata with the block address, and then returns the address.

Tables

The Lua API also has a few functions for manipulating metatables in objects. You create tables by calling the function lua_newtable . This function creates a new, empty table and then pushes it onto the stack. The function lua_gettable is provided for reading a value from a table that resides somewhere on the stack; when lua_gettable is given an index that points to the table, it will read and return the value.

Interestingly, in the Lua API, all global variables are kept within the ordinary Lua tables called environments. The initial environment that is created is called the global environment, and it can be pseudo-indexed at LUA_GLOBALSINDEX . Regular table operations can be used over an environment table to access and change these global values (using lua_pushstring , for example). The global environment of a thread can be changed using lua_replace .

The lua_getfenv and lua_setfenv functions are used to get and set the environment of Lua functions. First lua_getfenv pushes the environment table of the function on the stack at a given index, and then lua_setfenv pops a table from the stack and sets it as the new environment for the function at a given index.

There are a number of other useful Lua functions for dealing with tables. Lua_getmetatable pushes the metatable of an object on the stack, and lua_setmetatable sets the table on the top of a stack as a new metatable for that object and then pops the table. The lua_load command is used to load up Lua chunks . It automatically detects whether a chunk is text or binary, and then loads it accordingly .

 int lua_load (lua_State *MyLua, lua_reader, void *Mydata, const char *MyChunk); 

The function lua_rawget gets the real value of a table key. To store the value into a table that resides somewhere in the stack, the key and the value are pushed by calling lua_set table . The lua_rawest function is used to set the real value of any table index. Tables can be traversed with int lua_next , which pops a key from the stack and pushes a key-value pair from the table. If there are no more elements left, then lua_next returns a 0.

Tables are created by calling lua_newtable :

 void lua_newtable (lua_State *MyLua); 

Reading the value in a table on the stack is done by calling the lua_gettable command with a specific index:

 lua_gettable (lua_State *MyLua, int specific_index); 

Because of their universality and flexibility, tables are often used as arrays in the API.

NOTE

TIP

Some of you C buffs are probably wondering how Lua handles arrays. Lua does have functions to work with C arrays, which are treated as Lua tables and indexed by numbers . Lua basically turns Lua tables into arrays indexed by number keys. The API uses two commands to accomplish this: lua_rawgeti , to push the value of elements into the table at a given stack position, and lua_rawseti , for setting the value of elements of a table at a given stack position. The lua_getn command is a third function that will get the number of elements in the table/array.

Threads

Lua offers partial support for multiple threads. Since the support is pretty basic, you will often find programs that instead incorporate an existing C library offering full multi-threading .

Adding a new thread to the Lua state can be done by using the lua_newthread function:

 Lua_State *lua_newthread (lua_State *L); 

The lua_newthread function pushes the thread onto the stack and then returns a pointer to lua_State that represents this new thread. All the global objects are then shared between the different threads, but this new thread has its own independent runtime stack. Each thread also has an independent global environment table.

Manipulating an existing thread can be accomplished by using the lua_resume and lua_yield functions, which allow one to suspend or resume running threads. Lua threads can be closed using the lua_closethread () function.

Calling Functions

When C and Lua are working in tandem, both C and Lua functions can be called. For C functions to work, you must do the following:

  1. Register the C function with Lua.

  2. Push the function to be called onto the stack.

  3. Push any arguments to the function onto the stack.

  4. Call the function with lua_call .

The lua_call function looks something like this:

 int lua_call (lua_State *MyLua, int arguments, int results); 

The arguments and results integers are the numbers of arguments and results that passed onto the stack.

If a C function needs to keep a reference to a Lua value outside of its lifespan, it must create a reference to the value. These references are stored and manipulated and released with lua_ref, lua_getref , and lua_unref .

All arguments and the function value are then popped from the stack. Lua makes sure that the returned values fit on the stack, and that the function results are pushed in direct order so that the last result is on the top. The lua_call function propagates any errors in this process upwards, and a special function, lua_pcall , is used to track error messages that flow this way.

C functions can also be used to extend Lua, a technique that is covered in Chapter 12, along with extending Ruby and Python in the same way.

Performing Actions

Lua's C API has equivalent commands to the basic library that it uses when in C API mode. These commands are listed in Table 7.4.

Table 7.4. Lua API Actions

Basic Library Function

Equivalent C API Function

dofile ()

lua_dofile

dostring ()

lua_dostring

error ()

lua_error

newtag ()

lua_newtag

tag ()

lua_tag

type ()

lua_type


Out of all of these, lau_dostring is the one most likely to be encountered because it is used to perform most Lua actions. Lua can also be executed in chunks written in a file or in a string by using lua_dofile , lua_dostring , or the lua_dobuffer command.

When called with a NULL argument, lua_dofile executes the standard in ( stdin ) stream. Both lua_dofile and lua_dobuffer are able to execute pre-compiled Lua chunks this way. The lua_dostring command, however, can only execute source code.

The function lua_dostring calls the interpreter over a section of code contained in a string. The lua_getglobal , lua_setglobal , lua_call , and lua_register are used to interpret code files, set and manipulate global variables, call Lua functions, and make C functions accessible to Lua.

[ LiB ]


Game Programming with Pyton, Lua and Ruby
Game Programming with Pyton, Lua and Ruby
ISBN: N/A
EAN: N/A
Year: 2005
Pages: 133

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net