This library provides new convenience functions for binding C types to Lua as userdata for Lua 5.1, Lua 5.2, and Lua 5.3. It supports objects with different lifetimes, polymorphic type checking, type-safe binding of tagged unions or embedded structs, properties and methods at the same time, and uniform handling of pointers to objects using a simple and small set of API functions.
This package includes a header file moon.h
and the corresponding
source file moon.c
. To use this package you need to include the
header file wherever you want to call one of the macros/functions
defined within. If you just have a single source file where you want
to use those functions, you are done: moon.h
includes moon.c
and
makes every function static
. If you have multiple source files that
need functions/macros from this library, this approach is flawed,
because you will end up with multiple versions of the same functions.
Instead include the header wherever you need it, but when you compile
those source files, define the macro MOON_PREFIX
to a unique name
to use for all functions in the moon
library. You also have to
compile and link moon.c
using the same define. This approach will
change the common moon_
prefix to your custom prefix behind the
scenes to avoid linker errors in case another library also links to
moon.c
.
The header file moon_flag.h
can be included whenever needed, but it
depends on the functions defined in moon.c
. The moon_dlfix.h
header is completely independent, but relies on some platform specific
functions.
This section lists all provided macros/functions.
The main part of the moon toolkit.
#define MOON_EXPORT
#define MOON_IMPORT
#define MOON_LOCAL
Macros for specifying symbol visibility.
#define MOON_CONCAT( _a, _b )
A macro that evaluates both arguments and joins them together. You can use that to build valid C identifiers with custom prefixes or suffixes.
#define MOON_STRINGIFY( _v )
A macro that has the same effect as #_v
, but works outside of a
macro substitution.
typedef struct {
unsigned char flags;
unsigned char cleanup_offset;
unsigned char vcheck_offset;
unsigned char object_offset;
} moon_object_header;
Common data structure shared by all userdata objects created via the
moon toolkit. The object may have optional fields following the memory
of this header structure, stored at the given offsets. The flags
field is a bit mask describing the details of the object. A pointer to
this header can be obtained by using plain lua_touserdata
on a moon
object.
typedef void* (*moon_object_cast)( void* );
Function pointer type for conversion functions used by moon_defcast
.
typedef void (*moon_object_destructor)( void* );
Function pointer type for cleanup functions of moon objects.
#define MOON_OBJECT_IS_VALID 0x01
#define MOON_OBJECT_IS_POINTER 0x02
Values stored in the flags
field of the moon_object_header
structure. The only value interesting for users of the library is the
MOON_OBJECT_IS_VALID
flag which is reset automatically by the
moon_killobject
function to signal that the destructor has already
run.
/* [ -nup, +0, e ] */
void moon_defobject( lua_State* L,
char const* metatable_name,
size_t userdata_size,
luaL_Reg const* methods,
int nup );
This function creates a new metatable and registers the functions in
the luaL_Reg
array (functions starting with double underscores __
go into the metatable, functions starting with a fullstop .
are
setup as properties, and the rest goes into a table used by the
__index
metafield). Property functions act as __index
and
__newindex
functions at the same time, i.e. they should return a
value when called with two arguments, and assign the third value when
called with three. If the luaL_Reg
array also contains an __index
and/or __newindex
function, those functions are called as fallbacks
when method/property lookup has failed. In case a metatable with the
given name already exists, an error is raised. The userdata_size
is
stored in the metatable for the moon_newobject
function -- use a
size of 0 to prohibit use of moon_newobject
(e.g. for incomplete
types). If nup
is non-zero, it pops those upvalues from the current
Lua stack top and makes them available to all registered functions
(metamethods, property functions, and methods). A __gc
metamethod
and a default __tostring
metamethod are provided by moon_defobject
as well.
/* [ -0, +1, e ] */
void* moon_newobject( lua_State* L,
char const* metatable_name,
moon_object_destructor destructor );
This function allocates a userdata, sets a metatable, and stores the
cleanup function for later use by the __gc
metamethod or the
moon_killobject
function. It throws an error if the metatable_name
has not been registered via the moon_defobject
function. The new
userdata object is pushed to the top of the Lua stack, and a pointer
to the payload (not the moon_object_header
structure) is returned.
/* [ -0, +1, e ] */
void** moon_newpointer( lua_State* L,
char const* metatable_name,
moon_object_destructor destructor );
This function allocates a userdata suitable for storing a pointer,
sets a metatable, and stores the cleanup function for later use by the
__gc
metamethod or the moon_killobject
function. It is equivalent
to moon_newobject
with the difference that the payload is stored as
a pointer, not inside the userdata memory. The pointer is initialized
to NULL
when this function returns, and may be set by assigning to
the memory location pointed to by the return value.
/* [ -0, +1, e ] */
void** moon_newfield( lua_State* L,
char const* metatable_name,
int idx,
int (*isvalid)( void* p ),
void* p );
This function allocates a userdata suitable for storing a pointer, and
sets a metatable. It is similar to moon_newpointer
, but it is
intended for exposing a data structure embedded within another
userdata (referenced by stack position idx
). The resulting moon
object keeps the parent userdata alive by storing a reference in its
uservalue table. If idx
is 0
, no uservalue table is set. Setting a
cleanup function is not possible, because the parent userdata is
responsible for cleaning up memory and other resources.
If an isvalid
function pointer is provided, it is called by the
moon_checkobject
/moon_testobject
functions to check whether the
object is still valid. This can be used to make sure that a tagged
union used as parent userdata still has the correct type, or that the
parent userdata hasn't released any necessary resources prior to
garbage collection. If the value referenced by idx
is a moon object
that also has an isvalid
check registered, all checks are performed
in the order from parent object(s) to child object.
/* [ -0, +(0|1), e ] */
int moon_getmethods( lua_State* L,
char const* tname );
If the metatable identified by tname
has methods registered, pushes
the methods table and returns LUA_TTABLE
. Otherwise nothing is
pushed and LUA_TNIL
is returned. This function only works for moon
objects and raises an error if the metatable tname
wasn't registered
via moon_defobject
.
/* [ -0, +0, e ] */
void moon_killobject( lua_State* L,
int idx );
If the moon object at the given stack index is valid, its cleanup function is run, and the object is marked as invalid (to prevent the cleanup function from running again). This function can be used to reclaim resources before the object becomes unreachable.
/* [ -0, +0, e ] */
void moon_defcast( lua_State* L,
char const* tname1,
char const* tname2,
moon_object_cast cast );
Registers the conversion function cast
for converting userdata
objects of type tname1
to type tname2
. The cast
function is
called automatically by moon_checkobject( L, idx, tname2 )
when
applied to an object of type tname1
, so function implementations can
be reused without extra work. The metatable tname1
must already
exist and belong to a moon object type (created via moon_defobject
).
/* [ -0, +0, v ] */
void* moon_checkobject( lua_State* L,
int idx,
char const* metatable_name );
This function ensures that the value stored at stack index idx
- is a full userdata
- is a moon object
- has the metatable identified by
metatable_name
or has a cast function tometatable_name
registered - has the
MOON_OBJECT_IS_VALID
flag set - all
isvalid
functions return a non-zero value (for objects created viamoon_newfield
) - contains a non-
NULL
pointer value (for objects created viamoon_newpointer
ormoon_newfield
).
If any of those conditions are false, an error is raised. Otherwise
this function returns a pointer to the object's memory (meaning the
objects created via moon_newpointer
and moon_newfield
are
dereferenced once, and if necessary the registered cast function is
called).
/* [ -0, +0, e ] */
void* moon_testobject( lua_State* L,
int idx,
char const* metatable_name );
Performs the same checks as moon_checkobject
, but returns NULL
if
any of those conditions are false instead of raising an error.
/* [ -0, +0, v ] */
lua_Integer moon_checkint( lua_State* L,
int idx,
lua_Integer min,
lua_Integer max );
This function works like luaL_checkinteger
but additionally ensures
that the given value is in the range [min
, max
], or else an error
is thrown.
/* [ -0, +0, v ] */
lua_Integer moon_optint( lua_State* L,
int idx,
lua_Integer min,
lua_Integer max,
lua_Integer def );
Similar to moon_checkint
but uses the default value def
if the
value at the given stack position is nil
or none
.
/* [ -0, +1, e ] */
int* moon_atexit( lua_State* L,
lua_CFunction cleanup );
This function puts an integer-sized userdata (initialized to 0) in the
registry and sets the given cleanup
function as __gc
metamethod.
The userdata is also pushed to the top of the Lua stack, and returned
as an int
pointer.
Use this function if you want to call a cleanup function when the Lua
state is closed, but only if some initialization succeeded. The usual
approach is as follows:
- Call
moon_atexit
registering your cleanup function. - Do your initialization.
- If successful, you set the
int
pointer to non-zero, which is almost atomic and can't fail, and pop the userdata. - When the cleanup function is called, check for a non-zero value before actually cleaning up.
/* [ -1, +0, e ] */
void moon_setuvfield( lua_State* L,
int idx,
char const* key );
This function pops the value at the top of the stack and stores it
under key
in the environment/uservalue table of the object at stack
position idx
.
/* [ -0, +(0|1), e ] */
int moon_getuvfield( lua_State* L,
int idx,
char const* key );
This function works similar to luaL_getmetafield
, but it looks for
key
in the environment/uservalue table of the object at index idx
,
and pushes the corresponding value to the top of the stack. If there
is no uservalue table or no such field, nothing is pushed and
LUA_TNIL
is returned. Otherwise, the return value is the type of the
pushed value.
/* [ -0, +1, e ] */
void moon_getcache( lua_State* L,
int idx );
This function looks up and pushes a weak-valued table under a private
key in the table given by index idx
(often LUA_REGISTRYINDEX
). If
the weak-valued table doesn't exist yet, it is created automatically.
This function is useful to map lightuserdata to full userdata, but it
can also be used to cache full userdata for enum values (using
separate caches per enum type), etc.
/* [ -0, +0, v ] */
void moon_stack_assert( lua_State* L,
... );
This "function" is implemented as a macro that evaluates to void
if
NDEBUG
is defined. If it is not, it tries to match the type
specifications (strings) given as arguments to the values at the top
of the Lua stack. Every mismatch is reported on stderr
, and finally
the whole Lua stack is dumped to stderr
using the moon_stack
function below, and an error is raised. Currently the following type
specifications are supported:
"n"
: nil"b"
: boolean"l"
: lightuserdata"i"
: integer (equivalent to"d"
before Lua 5.3)"d"
: number (thinkdouble
)"s"
: string"t"
: table"f"
: function"u"
: userdata"c"
: coroutine"a"
: any (non-nil) value
You can combine the single letter options to express alternatives, so
e.g. "tf"
means: table or function.
/* [ -0, +0, - ] */
void moon_stack( lua_State* L );
This "function" is also implemented as a macro that evaluates to
void
in case NDEBUG
is defined. Otherwise it prints the current
contents of the Lua stack in human-readable format to stderr
.
Compatiblity macro for lua_absindex
, but also available on Lua 5.1
as a function.
int moon_derive( lua_State* L );
A lua_CFunction
that may be registered as part of your module and
allows Lua code to subclass a moon object. When called it expects two
strings as arguments: a new (non-existing) type name and an old
(existing) type name of a moon object type. It sets up the new type
name as an alias to the old type but with a different methods table.
The new methods table (initially a copy of the methods of the old
type) is returned.
int moon_downcast( lua_State* L );
A lua_CFunction
that may be registered as part of your module and
allows Lua code to change the type of a moon object to a type created
via moon_derive
. It is typically used in constructors of derived
types, and expects a moon object and the new type name as arguments.
If successful, the object (with its metatable replaced) is returned.
moon_flag.h
is a macro file, that can be included multiple times and
each time defines a new userdata type as a type-safe representation of
a given enum/flag type in Lua. The resulting userdata values support
+
(bitwise or, in Lua 5.3 also |
), -
(create a new value with
certain bits cleared), and calling (test if all bits are set). For Lua
5.3 also bitwise "and" and bitwise "not" are defined.
Parameters are passed as macros before including the macro file. Any
arguments and all internal macros are #undef
ed before leaving the
macro file. The following parameters are recognized:
MOON_FLAG_NAME
(required): A metatable name used for defining a userdata type.MOON_FLAG_TYPE
(required): The underlying enum/flag type that is handled by the custom userdata.MOON_FLAG_SUFFIX
(required): All defined functions have this suffix (and themoon_flag_
prefix) to make them unique.MOON_FLAG_NOBITOPS
(optional): If this macro is defined, no metamethods for bitwise operations are created. This includes__add
,__sub
, and__call
metamethods. If you do this, you should think about using strings andluaL_checkoption
instead of userdata for representing your flags in Lua.MOON_FLAG_NORELOPS
(optional): If this macro is defined, the__eq
metamethod is not created.MOON_FLAG_USECACHE
(optional): The constructor function for this flag looks in a local cache before creating a new full userdata, and returns the cached value if possible. This way each enum/flag value has at most one userdata associated with it.MOON_FLAG_EQMETHOD( _a, _b )
(optional): If you need a custom comparison operation instead of the usual==
, define this macro.
The following (static) functions will be defined, unless they are disabled via one of the parameter macros above:
/* [ -0, +0, e ] */
void moon_flag_def_SUFFIX( lua_State* L );
/* [ -0, +1, e ] */
void moon_flag_new_SUFFIX( lua_State* L, TYPE value );
/* [ -0, +0, v ] */
TYPE moon_flag_get_SUFFIX( lua_State* L, int idx );
int moon_flag_add_SUFFIX( lua_State* L );
int moon_flag_sub_SUFFIX( lua_State* L );
int moon_flag_call_SUFFIX( lua_State* L );
int moon_flag_and_SUFFIX( lua_State* L ); /* Lua 5.3+ */
int moon_flag_not_SUFFIX( lua_State* L ); /* Lua 5.3+ */
int moon_flag_eq_SUFFIX( lua_State* L );
The last six are metamethods and not supposed to be called from C.
moon_flag_def_SUFFIX
defines the new type, creates the metatable and
registers all metamethods. moon_flag_new_SUFFIX
pushes a userdata
representing the given value to the top of the Lua stack, while
moon_flag_get_SUFFIX
returns the corresponding enum value from a
userdata on the Lua stack (or raises an error).
On Linux and BSDs (and possibly other Unix machines) binary extension modules aren't linked to the Lua library directly, but instead expect the main executable to reexport the Lua API. If you don't have control over the main executable (e.g. you are writing a plugin for a 3rd party program), you are out of luck. This header file tries to reexport the Lua API from the shared library linked to your plugin to make it available for extension modules. It relies on some platform specific tricks, so it probably won't work everywhere.
#define MOON_DLFIX()
This macro uses the dynamic linker to search for the Lua API in an
already loaded shared library and reexport it for extension modules.
If the necessary linker tricks don't work on the given platform, this
macro evaluates to a void
expression, and you will continue to get
the usual unresolved symbol errors when loading a binary extension
module.
If the builtin heuristics fail to find the shared Lua library you can specify the library name/path by defining this macro.
For some OSes all loaded shared libraries is searched for an ELF
object that contains the Lua symbols. Since this procedure also finds
shared objects that have the Lua library as a dependency, the library
name is checked for a known prefix to make sure that only the actual
Lua library is exported. The default is "liblua"
, but you can change
it by defining this macro.
Philipp Janda, siffiejoe(a)gmx.net
Comments and feedback are always welcome.
moon
is copyrighted free software distributed under the Tiny
license. The full license text follows:
moon (c) 2013-2016 Philipp Janda
You may do anything with this work that copyright law would normally
restrict, so long as you retain the above notice(s) and this license
in all redistributed copies and derived works. There is no warranty.