Lua is an embedded language. This means that Lua is not a stand-alone application, but a library that we can link with other applications to incorporate Lua facilities into them.
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 until now?
The solution to this puzzle is the Lua interpreter
—the executable lua
.
This executable is a small application,
around six hundred lines of code,
that uses the Lua library to implement
the stand-alone interpreter.
The 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).
This ability to be used as a library to extend an application is what makes Lua an embeddable 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), so that they can add facilities that cannot be written directly in Lua. This is what makes Lua an extensible language.
These two views of Lua (as an embeddable 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.
The C API is the set of functions, constants, and types that allow C code to interact with Lua.[23] 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. Virtually anything that Lua code can do can also be done by C code through the C API.
The C API follows the modus operandi of C, which is quite different from that of Lua. When programming in C, we must care about type checking, 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 our responsibility to make sure that the arguments are valid before calling a function.[24] If we make mistakes, we can get a crash 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 us full control over all details.
As its title says,
the goal of this chapter is to give an overview of what is
involved when we use Lua from C.
Do not try to understand all the details of what is going on now;
we will fill them in later.
Nevertheless,
do not forget that you always can find more
details about specific functions
in the Lua reference manual.
Moreover, you can find several examples of API uses
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.
From now on, we are wearing a C programmer’s hat.
We will start this overview with a simple example of an application program: a stand-alone Lua interpreter. We can write a bare-bones stand-alone interpreter as in Figure 27.1, “A bare-bones stand-alone Lua interpreter”.
Figure 27.1. A bare-bones stand-alone Lua interpreter
#include <stdio.h> #include <string.h> #include "lua.h" #include "lauxlib.h" #include "lualib.h" int main (void) { char buff[256]; int error; lua_State *L = luaL_newstate(); /* opens Lua */ luaL_openlibs(L); /* opens the standard libraries */ while (fgets(buff, sizeof(buff), stdin) != NULL) { error = luaL_loadstring(L, buff) || lua_pcall(L, 0, 0, 0); if (error) { fprintf(stderr, "%s\n", lua_tostring(L, -1)); lua_pop(L, 1); /* pop error message from the stack */ } } lua_close(L); return 0; }
The header file lua.h
declares the basic functions provided by Lua.
It includes functions to create a new Lua environment,
to invoke Lua functions,
to read and write global variables in the environment,
to register new functions to be called by Lua, and so on.
Everything declared in lua.h
has a lua_
prefix
(e.g., lua_pcall
).
The header file lauxlib.h
declares the functions provided
by the auxiliary library (auxlib).
All its declarations start with luaL_
(e.g., luaL_loadstring
).
The auxiliary library uses the basic API provided by lua.h
to provide a higher abstraction level,
in particular with abstractions
used by the standard libraries.
The basic API strives for economy and orthogonality,
whereas the auxiliary library strives for
practicality for a few common tasks.
Of course, it is very easy for your program to create other
abstractions that it needs, too.
Keep in mind that the auxiliary library
has no access to the internals of Lua.
It does its entire job through the official basic API
declared in lua.h
.
Whatever it does,
your program can do too.
The Lua library defines no C global variables at all.
It keeps all its state in the dynamic structure lua_State
;
all functions inside Lua receive
a pointer to this structure as an argument.
This design makes Lua reentrant
and ready to be used in multithreaded code.
As its name implies,
the function luaL_newstate
creates a new Lua state.
When luaL_newstate
creates a fresh state,
its environment contains no predefined functions,
not even print
.
To keep Lua small, all standard libraries
come as separate packages,
so that we do not have to use them if we do not need to.
The header file lualib.h
declares functions to open the libraries.
The function luaL_openlibs
opens all
standard libraries.
After creating a state and populating it
with the standard libraries,
it is time to handle user input.
For each line the user enters,
the program first compiles it
with luaL_loadstring
.
If there are no errors, the call returns zero
and pushes the resulting function on the stack.
(We will discuss this mysterious stack in the next section.)
Then the program calls lua_pcall
,
which pops the function from the stack
and runs it in protected mode.
Like luaL_loadstring
,
lua_pcall
returns zero if there are no errors.
In case of error,
both functions push an error message on the stack;
we then get this message with lua_tostring
and,
after printing it, remove it from the stack
with lua_pop
.
Real error handling can be quite complex in C, and how to do it depends on the nature of our application. The Lua core never writes anything directly to any output stream; it signals errors by returning error messages. Each application can handle these messages in a way most appropriate to its needs. To simplify our discussions, we will assume for our next examples a simple error handler like the following one, which prints an error message, closes the Lua state, and finishes the whole application:
#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, fmt, argp); va_end(argp); lua_close(L); exit(EXIT_FAILURE); }
Later we will discuss more about error handling in the application code.
Because we can compile Lua as either C or C++ code,
lua.h
does not include the following boilerplate
commonly used in C libraries:
#ifdef __cplusplus extern "C" { #endif ... #ifdef __cplusplus } #endif
If we have compiled Lua as C code
and are using it in C++,
we can include lua.hpp
instead of lua.h
.
It is defined as follows:
extern "C" { #include "lua.h" }
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, we can use the stack to keep intermediate results, too.
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.
In Lua, when we write t[k] = v
,
both k
and v
can have several different types;
even t
can have different types,
due to metatables.
If we want to offer this operation in C, however,
any given 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).
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
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; we designed Lua to interface easily not only with C/C++, but also with Java, Fortran, C#, and the like. Second, Lua does garbage collection: if we keep a Lua table in a C variable, the Lua engine has no way to know about this use; it may (wrongly) assume that this table is garbage and collect it.
Therefore, the Lua API does not define
anything like a lua_Value
type.
Instead,
it uses the stack to exchange values between Lua and C.
Each slot in this stack can hold any Lua value.
Whenever we want to ask for a value from Lua
(such as the value of a global variable),
we call Lua, which pushes the required value onto the stack.
Whenever we want to pass a value to Lua,
we first push the value onto the stack,
and then we call Lua (which will pop the value).
We still need a different function to push each C type onto the stack
and a different function to get each C type from the stack,
but we avoid combinatorial explosion.
Moreover, because this stack is part of the Lua state,
the garbage collector knows which values C is using.
Nearly all functions in the API use the stack.
As we saw in our first example,
luaL_loadstring
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 error message there too.
Lua manipulates this stack in a strict LIFO discipline (Last In, First Out). When we call Lua, it changes only the top part of the stack. Our C code has more freedom; specifically, it can inspect any element in the stack and even insert and delete elements at any position.
The API has a push function for each Lua type
with a direct representation in C:
lua_pushnil
for the constant nil,
lua_pushboolean
for Booleans (integers, in C),
lua_pushnumber
for doubles,[25]
lua_pushinteger
for integers,
lua_pushlstring
for arbitrary strings
(a pointer to char
plus a length),
and lua_pushstring
for zero-terminated strings:
void lua_pushnil (lua_State *L); void lua_pushboolean (lua_State *L, int bool); void lua_pushnumber (lua_State *L, lua_Number n); void lua_pushinteger (lua_State *L, lua_Integer n); void lua_pushlstring (lua_State *L, const char *s, size_t len); void lua_pushstring (lua_State *L, const char *s);
There are also functions to push C functions and userdata values onto the stack; we will discuss them later.
The type lua_Number
is the numeric float type in Lua.
It is double
by default,
but we can configure Lua at compile time to use float
or even long double
.
The type lua_Integer
is the numeric integer type in Lua.
Usually, it is defined as long long
,
which is a signed 64-bit integer.
Again, it is trivial to configure Lua to use
int
or long
for this type.
The combination float
–int
,
with 32-bit floats and integers,
creates what we call Small Lua,
which is particularly interesting for small machines
and restricted hardware.[26]
Strings in Lua are not zero-terminated;
they can contain arbitrary binary data.
In consequence,
the basic function to push a string onto
the stack is lua_pushlstring
,
which requires an explicit length as an argument.
For zero-terminated strings, we can use also lua_pushstring
,
which uses strlen
to supply the string length.
Lua never keeps pointers to external strings
(or to any other external object except C functions,
which are always static).
For any string that it has to keep,
Lua either makes an internal copy or reuses one.
Therefore, we can free or modify our buffers
as soon as these functions return.
Whenever we push an element onto the stack,
it is our 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.
(The header file lua.h
defines
this constant as LUA_MINSTACK
.)
This space is more than enough for most common uses,
so usually we do not even think about it.
However, some tasks need more stack space,
in particular if we have a loop pushing elements onto the stack.
In those cases, we need to call lua_checkstack
,
which checks whether the stack has enough space for our needs:
int lua_checkstack (lua_State *L, int sz);
Here, sz
is the number of extra slots we need.
If possible,
lua_checkstack
grows the stack
to accommodate the required extra size.
Otherwise, it returns zero.
The auxiliary library offers a higher-level function to check for stack space:
void luaL_checkstack (lua_State *L, int sz, const char *msg);
This function is similar to lua_checkstack
but,
if it cannot fulfill the request,
it raises an error with the given message,
instead of returning an error code.
To refer to elements on the stack,
the API uses indices.
The first element pushed on the stack 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,
with negative indices.
In this case, -1 refers to the element on 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 on 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.
To check whether a stack element has a specific type,
the API offers a family of functions called lua_is*
,
where the *
can be any Lua type.
So, there are
lua_isnil
,
lua_isnumber
, lua_isstring
,
lua_istable
, and the like.
All these functions have the same prototype:
int lua_is* (lua_State *L, int index);
Actually,
lua_isnumber
does not check whether
the value has that specific type,
but whether the value can be converted to that type;
lua_isstring
is similar:
in particular, any number satisfies lua_isstring
.
There is also a function lua_type
,
which returns the type of an element on the stack.
Each type is represented by a respective constant:
LUA_TNIL
,
LUA_TBOOLEAN
,
LUA_TNUMBER
,
LUA_TSTRING
, etc.
We use this function mainly in conjunction with a switch statement.
It is also useful when we need to check for strings and
numbers without potential coercions.
To get a value from the stack,
there are the lua_to*
functions:
int lua_toboolean (lua_State *L, int index); const char *lua_tolstring (lua_State *L, int index, size_t *len); lua_State *lua_tothread (lua_State *L, int index); lua_Number lua_tonumber (lua_State *L, int index); lua_Integer lua_tointeger (lua_State *L, int index);
We can call any of these functions
even when the given element does not have
an appropriate type.
The function lua_toboolean
works for any type,
converting any Lua value
to a C Boolean according to the Lua rules for conditions:
zero for the values nil and false,
and one for any other Lua value.
The functions lua_tolstring
and lua_tothread
return NULL
for values with incorrect types.
The numeric functions, however,
have no way to signal a wrong type,
so they simply return zero.
Formerly we would need to call
lua_isnumber
to check the type,
but Lua 5.2 introduced the following new functions:
lua_Number lua_tonumberx (lua_State *L, int idx, int *isnum); lua_Integer lua_tointegerx (lua_State *L, int idx, int *isnum);
The out parameter isnum
returns a Boolean that indicates
whether the Lua value was successfully coerced to the desired type.
The function lua_tolstring
returns a pointer to an internal
copy of the string and stores the string’s length in the
position given by len
.
We must not change this internal copy
(there is a const
there to remind us).
Lua ensures that this pointer is valid as long as the
corresponding string value is on the stack.
When a C function called by Lua returns,
Lua clears its stack;
therefore, as a rule,
we should never store pointers to Lua strings
outside the function that got them.
Any string that lua_tolstring
returns always has an
extra zero at its end,
but it can have other zeros inside it.
The size returned through the third argument, len
,
is the real string’s length.
In particular,
assuming that the value on the top of the stack is a string,
the following assertions are always valid:
size_t len; const char *s = lua_tolstring(L, -1, &len); /* any Lua string */ assert(s[len] == '\0'); assert(strlen(s) <= len);
We can call lua_tolstring
with NULL
as its third argument if we do not need the length.
Better yet, we can use the macro lua_tostring
,
which simply calls lua_tolstring
with a NULL
third argument.
To illustrate the use of these functions, Figure 27.2, “Dumping the stack” presents a useful helper function that dumps the entire content of the stack.
Figure 27.2. Dumping the stack
static void stackDump (lua_State *L) { int i; int top = lua_gettop(L); /* depth of the stack */ 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 values with no C equivalents (tables, functions, etc.),
it prints only their types.
(lua_typename
converts a type code to a type name.)
In Lua 5.3,
we can still print all numbers with lua_tonumber
and the "%g"
format,
as integers are always coercible to floats.
However, we may prefer to print integers as integers,
to avoid losing precision.
In that case,
we can use the new function lua_isinteger
to distinguish integers from floats:
case LUA_TNUMBER: { /* numbers */ if (lua_isinteger(L, i)) /* integer? */ printf("%lld", lua_tointeger(L, i)); else /* float */ printf("%g", lua_tonumber(L, i)); break; }
Besides the previous functions, which exchange values between C and the stack, the API offers also the following operations for generic stack manipulation:
int lua_gettop (lua_State *L); void lua_settop (lua_State *L, int index); void lua_pushvalue (lua_State *L, int index); void lua_rotate (lua_State *L, int index, int n); void lua_remove (lua_State *L, int index); void lua_insert (lua_State *L, int index); void lua_replace (lua_State *L, int index); void lua_copy (lua_State *L, int fromidx, int toidx);
The function lua_gettop
returns the number of elements on the stack,
which is also the index of the top element.
The function lua_settop
sets the top
(that is, the number of elements on the stack) to a specific value.
If the previous top was higher than the new one,
the function discards the extra top values.
Otherwise, it pushes nils on the stack to get the
given size.
In particular, lua_settop(L, 0)
empties the stack.
We can also use negative indices with lua_settop
.
Using this facility, the API offers 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
on the stack a copy of the element at the given index.
The function lua_rotate
is new in Lua 5.3.
As the name implies,
it rotates the stack elements
from the given index to the top of the stack
by n
positions.
A positive n
rotates the elements in the direction of the top;
a negative n
rotates in the other direction.
This is a quite versatile function,
and two other API operations are defined as macros using it.
One is lua_remove
,
which removes the element at the given index,
shifting down the elements above this position
to fill in the gap.
Its definition is as follows:
#define lua_remove(L,idx) \ (lua_rotate(L, (idx), -1), lua_pop(L, 1))
That is, it rotates the stack by one position,
moving the desired element to the top,
and then pops that element.
The other macro is lua_insert
,
which moves the top element into the given position,
shifting up the elements above this position to open space:
#define lua_insert(L,idx) lua_rotate(L, (idx), 1)
The function lua_replace
pops a value and
sets it as the value of the given index,
without moving anything;
finally, lua_copy
copies the value at one index to another,
leaving the original untouched.[27]
Note that the following operations have no effect
on a non-empty stack:
lua_settop(L, -1); /* set top to its current value */ lua_insert(L, -1); /* move top element to the top */ lua_copy(L, x, x); /* copy an element to its own position */ lua_rotate(L, x, 0); /* rotates by zero positions */
The program in Figure 27.3, “Example of stack manipulation” uses stackDump
(defined in Figure 27.2, “Dumping the stack”)
to illustrate these stack operations.
Figure 27.3. Example of stack manipulation
#include <stdio.h>
#include "lua.h"
#include "lauxlib.h"
static void stackDump (lua_State *L) {
as in Figure 27.2, “Dumping the stack”
}
int main (void) {
lua_State *L = luaL_newstate();
lua_pushboolean(L, 1);
lua_pushnumber(L, 10);
lua_pushnil(L);
lua_pushstring(L, "hello");
stackDump(L);
/* will print: true 10 nil 'hello' */
lua_pushvalue(L, -4); stackDump(L);
/* will print: true 10 nil 'hello' true */
lua_replace(L, 3); stackDump(L);
/* will print: true 10 true 'hello' */
lua_settop(L, 6); stackDump(L);
/* will print: true 10 true 'hello' nil nil */
lua_rotate(L, 3, 1); stackDump(L);
/* will print: true 10 nil true 'hello' nil */
lua_remove(L, -3); stackDump(L);
/* will print: true 10 nil 'hello' nil */
lua_settop(L, -5); stackDump(L);
/* will print: true */
lua_close(L);
return 0;
}
All structures in Lua are dynamic:
they grow as needed, and eventually shrink again when possible.
This means that the possibility of a memory-allocation failure
is pervasive in Lua.
Almost any operation can face this eventuality.
Moreover, many operations can raise other errors;
for instance, an access to a global variable can
trigger an __index
metamethod and
that metamethod may raise an error.
Finally, operations that allocate memory
eventually trigger the garbage collector,
which may invoke finalizers, which can raise errors too.
In short,
the vast majority of functions in the Lua API can result in errors.
Instead of using error codes for each operation in its API,
Lua uses exceptions to signal errors.
Unlike C++ or Java,
the C language does not offer an exception handling mechanism.
To circumvent this difficulty,
Lua uses the setjmp
facility from C,
which results in a mechanism somewhat similar to exception handling.
Therefore, most API functions can raise an error
(that is, call longjmp
) instead of returning.
When we write library code (C functions to be called from Lua), the use of long jumps requires no extra work from our part, because Lua catches any error. When we write application code (C code that calls Lua), however, we must provide a way to catch those errors.
When our application calls functions in the Lua API,
it is exposed to errors.
As we just discussed,
Lua usually signals these errors through long jumps.
However, if there is no corresponding setjmp
,
the interpreter cannot make a long jump.
In that case, any error in the API causes Lua
to call a panic function and,
if that function returns,
exit the application.
We can set our own panic function with lua_atpanic
,
but there is not much that it can do.
To properly handle errors in our application code,
we must call our code through Lua,
so that it sets an appropriate context to catch errors
—that is, it runs the code in the context of a setjmp
.
In the same way that we can run Lua code in protected mode
using pcall
,
we can run C code using lua_pcall
.
More specifically,
we pack the code in a function and call that function
through Lua, using lua_pcall
.
With this setting,
our C code will run in protected mode.
Even in case of memory-allocation failure,
lua_pcall
returns a proper error code,
leaving the interpreter in a consistent state.
The following fragment shows the idea:
static int foo (lua_State *L) {
code to run in protected mode
return 0;
}
int secure_foo (lua_State *L) {
lua_pushcfunction(L, foo); /* push 'foo' as a Lua function */
return (lua_pcall(L, 0, 0, 0) == 0);
}
In this example,
no matter what happens,
a call to secure_foo
will return a Boolean
signaling the success of foo
.
In particular,
note that the stack already has some preallocated slots
and that lua_pushcfunction
does not allocate memory,
so it cannot raise any error.
(The prototype of the function foo
is a requirement
of lua_pushcfunction
,
which creates a function in Lua representing a C function.
We will cover the details about C functions
in Lua in the section called “C Functions”.)
Lua is a safe language. This means that no matter what we write in Lua, no matter how wrong it is, we 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 be explained only in terms of the underlying hardware (e.g., error positions are given as instruction addresses).
Whenever we add new C functions to Lua,
we can break its safety.
For instance, a function equivalent to
the BASIC command poke
,
which stores an arbitrary byte at an arbitrary memory address,
could cause all sorts of memory corruption.
We must strive to ensure that our add-ons are safe to Lua
and provide good error handling.
As we discussed earlier,
C programs have to set their error handling
through lua_pcall
.
When we write library functions for Lua, however,
usually they do not need to handle errors.
Errors raised by a library function will be caught
either by a pcall
in Lua or by a lua_pcall
in the application code.
So, whenever a function in a C library detects an error,
it can simply call lua_error
(or better yet luaL_error
,
which formats the error message and then
calls lua_error
).
The function lua_error
tidies
any loose ends in the Lua system
and jumps back to the protected call
that originated that execution,
passing along the error message.
The Lua core does not assume anything about how
to allocate memory.
It calls neither malloc
nor realloc
to allocate memory.
Instead,
it does all its memory allocation and deallocation through
one single allocation function,
which the user must provide when she creates a Lua state.
The function luaL_newstate
,
which we have been using to create states,
is an auxiliary function that creates a Lua state
with a default allocation function.
This default allocation function uses
the standard functions malloc
–realloc
–free
from the C standard library,
which are (or should be) good enough for most applications.
However,
it is quite easy to get full control over Lua allocation,
by creating our state with the primitive lua_newstate
:
lua_State *lua_newstate (lua_Alloc f, void *ud);
This function takes two arguments:
an allocation function and a user data.
A state created in this way does all its
allocation and deallocation by calling f
;
even the structure lua_State
is
allocated by f
.
An allocation function must match
the type lua_Alloc
:
typedef void * (*lua_Alloc) (void *ud, void *ptr, size_t osize, size_t nsize);
The first parameter is always the user data
provided to lua_newstate
;
the second parameter is the address of the block being
(re)allocated or released;
the third parameter is the original block size;
and the last parameter is the requested block size.
If ptr
is not NULL
,
Lua ensures that it was previously allocated with size osize
.
(When ptr
is NULL
,
the previous size of the block was clearly zero,
so Lua uses osize
for some debug information.)
Lua uses NULL
to represent a block of size zero.
When nsize
is zero,
the allocation function must free the block pointed to by ptr
and return NULL
,
which corresponds to a block of the required size (zero).
When ptr
is NULL
,
the function must allocate and return a block with the given size;
if it cannot allocate the given block,
it must return NULL
.
If ptr
is NULL
and nsize
is zero,
both rules apply:
the net result is that the allocation function does
nothing and returns NULL
.
Finally,
when ptr
is non-NULL
and nsize
is non-zero,
the allocation function should reallocate the block,
like realloc
,
and return the new address
(which may or may not be the same as the original).
Again, in case of errors, it must return NULL
.
Lua assumes that the allocation function never fails
when the new size is smaller than or equal to the old one.
(Lua shrinks some structures during garbage collection,
and it is unable to recover from errors there.)
The standard allocation function
used by luaL_newstate
has the following definition
(extracted directly from the file lauxlib.c
):
void *l_alloc (void *ud, void *ptr, size_t osize, size_t nsize) { (void)ud; (void)osize; /* not used */ if (nsize == 0) { free(ptr); return NULL; } else return realloc(ptr, nsize); }
It assumes that free(NULL)
does nothing
and that realloc(NULL, size)
is equivalent to malloc(size)
.
The ISO C standard mandates both behaviors.
We can recover the memory allocator of a Lua state by calling
lua_getallocf
:
lua_Alloc lua_getallocf (lua_State *L, void **ud);
If ud
is not NULL
,
the function sets *ud
with the value of the user data
for this allocator.
We can change the memory allocator of a Lua state by calling
lua_setallocf
:
void lua_setallocf (lua_State *L, lua_Alloc f, void *ud);
Keep in mind that any new allocator will be responsible for freeing blocks that were allocated by the previous one. More often than not, the new function is a wrapper around the old one, for instance to trace allocations or to synchronize accesses to the heap.
Internally, Lua does not cache free memory blocks for reuse. It assumes that the allocation function does this caching; good allocators do. Lua also does not attempt to minimize fragmentation. Studies show that fragmentation is more the result of poor allocation strategies than of program behavior; good allocators do not create much fragmentation.
It is difficult to beat a well-implemented allocator, but sometimes you may try. For instance, Lua gives you the old size of any block that it frees or reallocates. Therefore, a specialized allocator does not need to keep information about the block size, reducing the memory overhead for each block.
Another situation where you can improve memory allocation
is in multithreading systems.
Such systems typically demand synchronization
for their memory-allocation functions,
as they use a global resource (the heap).
However, the access to a Lua state must be synchronized too
—or, better yet, restricted to one thread,
as in our implementation of lproc
in Chapter 33, Threads and States.
So, if each Lua state allocates memory from a private pool,
the allocator can avoid the costs of extra synchronization.
Exercise 27.1: Compile and run the simple stand-alone interpreter (Figure 27.1, “A bare-bones stand-alone Lua interpreter”).
Exercise 27.2: Assume the stack is empty. What will be its contents after the following sequence of calls?
lua_pushnumber(L, 3.5); lua_pushstring(L, "hello"); lua_pushnil(L); lua_rotate(L, 1, -1); lua_pushvalue(L, -2); lua_remove(L, 1); lua_insert(L, -2);
Exercise 27.3:
Use the function stackDump
(Figure 27.2, “Dumping the stack”)
to check your answer to the previous exercise.
Exercise 27.4:
Write a library that allows a script to limit the total amount
of memory used by its Lua state.
It may offer a single function, setlimit
,
to set that limit.
The library should set its own allocation function.
This function,
before calling the original allocator,
checks the total memory in use and returns NULL
if the requested memory exceeds the limit.
(Hint: the library can use the user data of the allocation function to keep its state: the byte count, the current memory limit, etc.; remember to use the original user data when calling the original allocation function.)
[23] Throughout this text, the term “function” actually means “function or macro”. The API implements several facilities as macros.
[24] You can compile Lua with the macro
LUA_USE_APICHECK
defined to enable some checks;
this option is particularly useful when debugging your C code.
Nevertheless,
several errors simply cannot be detected in C,
such as invalid pointers.
[25] For historical reasons, the term “number” in the API refers to doubles.
[26] For these configurations, have a look
in the file luaconf.h
.
[27] The function lua_copy
was introduced in Lua 5.2.
Personal copy of Eric Taylor <jdslkgjf.iapgjflksfg@yandex.com>