honeysuckle is a C library intended to make creating Lua bindings simpler. It was originally a core part of the honey code, but was split out into its own library because it is useful in many other contexts. It provides functions for argument parsing, table creation and processing, error throwing and handling, string wrangling, and registry operations.
void hs_parse_args( lua_State *L, ...);
[-0, +0, v][?]
L
: The Lua state to parse arguments from.…
: A series of struct hs_arg
construction macros.None.
This function reads arguments provided on the stack into the variables provided to it in the argument list, performing type-checking as it proceeds. If the wrong number of arguments is provided, or if there is a type mismatch between the expected type provided to this function and the actual type on the stack, it will throw an error message detailing that mismatch.
Below is a table of all of the available macros for parsing. Each function-like macro takes a single variable (not a pointer or a constant) whose type must be exactly that listed under “variable type” or undefined behavior will result. Your compiler will probably complain if there is a mismatch.
macro | expected stack type | variable type | variable value after call |
---|---|---|---|
hs_bool(x) | boolean | bool | the boolean value |
hs_int(x) | number, interpreted as integer | lua_Integer | the integer value |
hs_num(x) | number | lua_Number | the number value |
hs_str(x) | string | char * | pointer to the string data on the stack |
hs_tbl(x) | table | int | stack index of the table |
hs_func(x) | function | int | stack index of the function |
hs_cfunc(x) | function, interpretable as C function | lua_CFunction | pointer to the function |
hs_user(x) | userdata | void * | pointer to the memory block |
hs_light(x) | light userdata | void * | the pointer value |
hs_nil(x) | nil value | int | stack index of the nil value |
hs_any(x) | any value | int | stack index of the value |
The following would be used to create a Lua function that expects arguments like (number, string, table)
.
lua_Number n; char *string; int table_index; hs_parse_args(L, hs_num(n), hs_str(string), hs_tbl(table_index));
int hs_parse_overloaded( lua_State *L, ...);
[-0, +0, v][?]
L
: The Lua state to parse arguments from.…
: A series of hs_overload()
macros.The zero-based index of the overload option selected.
This function allows you to create overloaded Lua bindings with a very similar syntax to hs_parse_args()
. It accepts a series of hs_overload()
macros that in turn accept a series of struct hs_arg
construction macros, exactly as described above. It will attempt to match each provided overload option to the given arguments, returning as soon as it finds one. This function is subject to the same cautions about types discussed above.
Please note that the process of attempting to match an overload may mutate the values in the provided variables, and so you should not rely on them having the same value after a call as before, even if they do not participate in the actually selected overload.
lua_Integer i; lua_Number n; char *string; int choice = hs_parse_overloaded( L, hs_overload(hs_int(i)), hs_overload(hs_num(n), hs_str(string)) ); if (choice == 0) { /* n and string are undefined! */ /* do something with i... */ } else { /* i is undefined! */ /* do something with n and string... */ }
int hs_create_table( lua_State *L, ...);
[-?, +1, m][?]
L
: The Lua state to operate on.…
: Any number of struct hs_tbl_entry
construction macros.The stack index of the created table.
Create Lua tables. This function takes a series of key/value macros and constructs a table containing those keys and values. Each macro takes the form hs_[key type]_[value_type](key, value)
, as illustrated in the table below, Arguments to these values are passed by value, so concerns about bit alignment are not relevant here.
Macros dealing with table, function, and userdata types each take an integer. This is the stack index of the value on the Lua stack to use. These values are popped before returning. Please note both that this means that nesting tables is quite trivial (by simply calling hs_create_table()
inside one of the macros) and that nesting tables in this way requires a subtable to have all of its stack-indexed keys and values pushed after any such keys or values used in an ancestor table, or else the indices needed to construct that ancestor will be modified and likely will no longer be valid.
Value Type | ||||||||||
---|---|---|---|---|---|---|---|---|---|---|
boolean | integer | number | string | table | function | C function | userdata | light userdata | ||
Key Type | boolean | hs_bool_bool(bool, bool) | hs_bool_int(bool, lua_Integer) | hs_bool_num(bool, lua_Number) | hs_bool_str(bool, char *) | hs_bool_tbl(bool, int) | hs_bool_func(bool, int) | hs_bool_cfunc(bool, lua_CFunction) | hs_bool_user(bool, int) | hs_bool_light(bool, void *) |
integer | hs_int_bool(lua_Integer, bool) | hs_int_int(lua_Integer, lua_Integer) | hs_int_num(lua_Integer, lua_Number) | hs_int_str(lua_Integer, char *) | hs_int_tbl(lua_Integer, int) | hs_int_func(lua_Integer, int) | hs_int_cfunc(lua_Integer, lua_CFunction) | hs_int_user(lua_Integer, int) | hs_int_light(lua_Integer, void *) | |
number | hs_num_bool(lua_Number, bool) | hs_num_int(lua_Number, lua_Integer) | hs_num_num(lua_Number, lua_Number) | hs_num_str(lua_Number, char *) | hs_num_tbl(lua_Number, int) | hs_num_func(lua_Number, int) | hs_num_cfunc(lua_Number, lua_CFunction) | hs_num_user(lua_Number, int) | hs_num_light(lua_Number, void *) | |
string | hs_str_bool(char *, bool) | hs_str_int(char *, lua_Integer) | hs_str_num(char *, lua_Number) | hs_str_str(char *, char *) | hs_str_tbl(char *, int) | hs_str_func(char *, int) | hs_str_cfunc(char *, lua_CFunction) | hs_str_user(char *, int) | hs_str_light(char *, void *) | |
table | hs_tbl_bool(int, bool) | hs_tbl_int(int, lua_Integer) | hs_tbl_num(int, lua_Number) | hs_tbl_str(int, char *) | hs_tbl_tbl(int, int) | hs_tbl_func(int, int) | hs_tbl_cfunc(int, lua_CFunction) | hs_tbl_user(int, int) | hs_tbl_light(int, void *) | |
function | hs_func_bool(int, bool) | hs_func_int(int, lua_Integer) | hs_func_num(int, lua_Number) | hs_func_str(int, char *) | hs_func_tbl(int, int) | hs_func_func(int, int) | hs_func_cfunc(int, lua_CFunction) | hs_func_user(int, int) | hs_func_light(int, void *) | |
C function | hs_cfunc_bool(lua_CFunction, bool) | hs_cfunc_int(lua_CFunction, lua_Integer) | hs_cfunc_num(lua_CFunction, lua_Number) | hs_cfunc_str(lua_CFunction, char *) | hs_cfunc_tbl(lua_CFunction, int) | hs_cfunc_func(lua_CFunction, int) | hs_cfunc_cfunc(lua_CFunction, lua_CFunction) | hs_cfunc_user(lua_CFunction, int) | hs_cfunc_light(lua_CFunction, void *) | |
userdata | hs_user_bool(int, bool) | hs_user_int(int, lua_Integer) | hs_user_num(int, lua_Number) | hs_user_str(int, char *) | hs_user_tbl(int, int) | hs_user_func(int, int) | hs_user_cfunc(int, lua_CFunction) | hs_user_user(int, int) | hs_user_light(int, void *) | |
light userdata | hs_light_bool(void *, bool) | hs_light_int(void *, lua_Integer) | hs_light_num(void *, lua_Number) | hs_light_str(void *, char *) | hs_light_tbl(void *, int) | hs_light_func(void *, int) | hs_light_cfunc(void *, lua_CFunction) | hs_light_user(void *, int) | hs_light_light(void *, void *) |
The following constructs the table { debugLevel=0, logfile='output.log', someUserdata=[userdata], myTable={ hello='world' } }.
lua_newuserdata(L, sizeof(char)); int user_index = lua_gettop(L); hs_create_table(L, hs_str_int("debugLevel", 0), hs_str_str("logfile", "output.log"), hs_str_user("someUserdata", user_index), hs_str_tbl("myTable", hs_create_table( L, hs_str_str("hello", "world"), ));
void hs_process_table( lua_State *L, int table_index, ...);
[-0, +0, e][?]
L
: The Lua state to operate on.table_index
: The stack index of the table to process.…
: List of key processor macros.None.
This function is intended to create Lua bindings that can be called like someBinding{debug=true, verbosity=6, logfile='log.txt'}
. To that end, it accepts a list of value-processing macros, which are listed below. Each accepts as arguments a string to check as a key in the table, a callback function pointer (whose signature is different for each type – see below), and a void pointer to some additional data that may be used by the callbacks.
value type | callback signature | macro |
---|---|---|
boolean | void cb(bool, void*) | hs_process_bool(key, callback, data) |
integer | void cb(int, void*) | hs_process_int(key, callback, data) |
number | void cb(lua_Number, void*) | hs_process_num(key, callback, data) |
string | void cb(const char*, void*) | hs_process_str(key, callback, data) |
For the extremely common case of simply setting a variable, there are provided the functions hs_pt_set_bool
, hs_pt_set_int
, hs_pt_set_num
, and hs_pt_set_str
, which will cast the provided void pointer to bool
, lua_Integer
, lua_Number
, or const char *
, respectively, and then set that value to the value in the table.
This function may throw an error if the provided index is not valid or is not a table. It may trigger metamethods in extracting values.
void open_logfile(const char *logfile, void *settings) { // do something to open and configure the logfile } // tbl_index comes from somewhere... struct settings { bool debug; lua_Integer verbosity; lua_Number epsilon; }; struct settings s; hs_process_table(L, tbl_index, hs_process_int("verbosity", hs_pt_set_int, &(s.verbosity)), hs_process_bool("debug", hs_pt_set_bool, &(s.debug)), hs_process_str("logfile", set_logfile, &s), hs_process_num("epsilon", hs_pt_set_num, &(s.epsilon)) );
void hs_pushstring( lua_State *L, const char *format_string, ...);
[-0, +1, m][?]
L
: The Lua state to operate on.format_string
: A printf format string to construct the string to push.…
: printf format options.None.
This function makes it simple to push printf-formatted strings to the Lua stack. Its syntax is exactly that of sprintf, except the destination is a lua_State
pointer, rather than a string buffer.
const char name[] = "Kelly"; int hours = 10; int minutes = 5; hs_pushstring(L, "hello, %s. the current time is %d:%02d.", name, hours, minutes);
void hs_vpushstring( lua_State *L, const char *format_string, va_list args);
[-0, +0, m][?]
L
: The Lua state to operate on.format_string
: A printf format string to construct the string to push.args
: List of printf options in a variadic argument list.None.
Similar to hs_pushstring()
above. Its syntax is exactly that of vsprintf, except the destination is a lua_State
pointer, rather than a string buffer.
void hs_vpushstring( lua_State *L, const char *format_string, ...);
[-0, +0, v][?]
L
: The Lua state to operate on.format_string
: A printf format string to construct the string to push.…
: printf options.None, but by its nature this function will not return and instead makes a long jump to the current error handler.
Similar to hs_pushstring()
above, but pushes the string and then immediately throws an error with it. Useful for constructing complex error messages. This function may also throw a memory-related error if string creation fails.
void hs_traceback(lua_State *L);
[-(0|1), +(0|1), -][?]
L
: the Lua state to inspectNone.
This function produces a call traceback string to go with an error message provided to it, or keeps the “error message” intact if it is not a string. It is generally not intended to be called manually, but rather provided to lua_pcall()
as an error handler.
int hs_call( lua_State *L, int nargs, int nret);
[-(nargs+1), +(nret|1), -][?]
L
: the Lua state to call within.nargs
: the number of arguments to pop from the stack.nret
: the number of return values to push to the stack.
0 on success, or else one of the Lua error codes: LUA_ERRRUN
, LUA_ERRMEM
, or LUA_ERRERR
.
This function is exactly the same as lua_pcall
, with the same return values, except that it automatically pushes hs_traceback()
to use as an error handler and does not accept a different error handler.
int hs_rstore(lua_State *L);
[-1, +0, m][?]
L
: the Lua state to operate on.The reference key.
This is a macro wrapper around luaL_ref()
that always uses the Lua registry table. It is provided for convenience.
void hs_rload( lua_State *L, int reference);
[-0, +1, -][?]
L
: the Lua state to operate on.reference
: A reference key returned by hs_rstore()
.None.
This is a macro wrapper around lua_rawgeti()
that always gets values from the Lua registry table. It is provided for convenience.
void hs_rdel( lua_State *L, int reference);
[-0, +0, -][?]
L
: the Lua state to operate on.reference
: a reference key returned by hs_rstore()
.The reference key.
This is a macro wrapper around luaL_unref()
that always uses the Lua registry table. It is provided for convenience.