Created: 2014-05-19 06:37
Updated: 2019-02-15 17:51

Moon -- A C Binding Toolkit for Lua

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.

Using this Library

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_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

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, the rest goes into a table used by the __index metafield). In case a metatable with the same 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 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

  1. is a full userdata
  2. is a moon object
  3. has the metatable identified by metatable_name or has a cast function to metatable_name registered
  4. has the MOON_OBJECT_IS_VALID flag set
  5. all isvalid functions return a non-zero value (for objects created via moon_newfield)
  6. contains a non-NULL pointer value (for objects created via moon_newpointer or moon_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:

  1. Call moon_atexit registering your cleanup function.
  2. Do your initialization.
  3. If successful, you set the int pointer to non-zero, which is almost atomic and can't fail, and pop the userdata.
  4. 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 (think double)
  • "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 #undefed 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 the moon_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 and luaL_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)

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.
Cookies help us deliver our services. By using our services, you agree to our use of cookies Learn more