--- layout: base title: honeysuckle README --- honeysuckle =========== A pure C library to make writing Lua bindings simple. honeysuckle provides functions to make argument parsing, table operations, error creation and handling, string wrangling, and registry operations easy and straightforward from within your C code. Table of contents ----------------- - [Installation](#installation) - [Usage](#usage) - [Development](#development) - [License](#license) - [Footer](#footer) Installation ------------ [(Back to top)](#table-of-contents) ```bash mkdir build cd build cmake .. make sudo make install ``` To make the tests as well, do the above and then do `make test`. You should now be able to include `` into your C files for use. Usage ----- [(Back to top)](#table-of-contents) - [Type constants](#type-constants) - [Argument parsing](#argument-parsing) - [Overloading](#overloading) - [Table construction](#table-construction) - [Enums](#enums) - [Table processing](#table-processing) - [Errors and error handling](#errors-and-error-handling) - [String wrangling](#string-wrangling) - [Registry values](#registry-values) ### Type constants [(Back to top)](#table-of-contents) These constants are used when honeysuckle needs to know a type to expect. - `HS_BOOL` - booleans - `HS_INT` - lua_Integer (DO NOT use int, long int, or other types!) - `HS_NUM` - lua_Number (DO NOT use float, double, or other types!) - `HS_STR` - strings - `HS_TBL` - tables - `HS_FUNC` - any function - `HS_CFUNC` - C functions specifically - `HS_USER` - lua full userdata - `HS_LIGHT` - lua light userdata - `HS_NIL` - lua's nil - `HS_ANY` - any type `hs_type_to_string()` takes as argument a single type constant and returns a const string representing the type. For the numeric types (`HS_INT` and `HS_NUM`, using variables other than `lua_Integer` and `lua_Number` may result in undefined behavior. ### Argument parsing [(Back to top)](#table-of-contents) `hs_parse_args()` is the primary argument-parsing function. It accepts as arguments a lua_State pointer, any number of `struct hs_arg`s. These can either be constructed by hand or with function-like macros identified by the lowercase version of the type names from the section above. Use of the macros is recommended as they will likely reduce type mismatch errors (e.g. giving HS_BOOL as type but providing a pointer to a string). This function performs typechecking and in case of an error throws with helpful error messages. `HS_TBL`, `HS_FUNC`, `HS_NIL`, and `HS_ANY` take a pointer to an integer that will be filled with the stack index of the argument. Please note that, due to technical limitations, this function can only parse up to 255 arguments. This function may mutate any preexisting contents of the variables passed to contain the arguments. ```c int some_lua_binding(lua_State *L) { bool b; lua_Integer i; int tbl_index; lua_Number n; char *str; void* user; hs_parse_args(L, // explicit initializer for struct { HS_BOOL, .ptr.bool = &b }, // macro initialization (recommended) hs_int(i), hs_tbl(tbl_index), hs_num(n), hs_str(str), hs_user(user) ); // do something... return 1; } ``` #### Overloading There is another function, `hs_parse_overloaded()`, that permits overloaded argument parsing. Its syntax is exactly as `hs_parse_args()` except that each overload option must be wrapped in the macro `hs_overload()`. This function returns an integer containing the zero-indexed index of the overload option that was actually used. Please note that because of Lua's type system, overloads that differ only in an integer/number pair, function/c function pair, or userdata/light userdata pair in the same position may not work as expected. If none of the argument lists match the provided arguments, this function throws an error. This function may mutate preexisting data in the variables passed to store arguments. ```c int overloaded_lua_binding(lua_State *L) { lua_Integer i; lua_Number n; char *str; int choice = hs_parse_overloaded(L, hs_overload( hs_int(i) ), hs_overload( hs_str(str), hs_num(n) ) ); if (choice == 0) { // do something with i // str and n remain unassigned!! } else { // choice == 1, do something with str and n // i remains unassigned!! } return 0; } ``` ### Table construction [(Back to top)](#table-of-contents) `hs_create_table()` takes a lua_State pointer and then up to 255 ` struct hs_tbl_entry`s. These can either be constructed by hand, or via function-like macros identified by lowercase versions of the typenames shown above (e.g. `hs_str_int` would create a string key with integer value). Most of the type constants expect their associated type (e.g. `HS_INT` expects a `lua_Integer`, `HS_CFUNC` expects ` lua_CFunction`, etc.) but `HS_FUNC`, `HS_USER`, and `HS_TBL` expect an integer stack index. You cannot use `HS_NIL` or `HS_ANY` with this function. This function returns the stack index of the newly created table, and automatically pops any stack indices provided to it. ```c int *ptr; // some pointer hs_create_table(L, // manual struct initialization { HS_STR, .key.string="intVal", HS_INT, .value.integer=4 }, // macro initialization (recommended) hs_str_num("numVal", 6.0f), hs_int_tbl(12, hs_create_table(L, hs_str_cfunc("subTableCFunc", some_lua_binding), hs_int_bool(15, true), ) ); ``` ### Table processing [(Back to top)](#table-of-contents) `hs_process_table()` is intended for creating lua bindings that can be called like `set_config{ debug=true, verbosity=2, logfile='run.log' }`. It operates only on values with string keys and boolean, number, or string types. `hs_process_table()` accepts a lua_State pointer, a stack index for the table to operate on, and then a series of `struct hs_table_processor`s. These can either be constructed manually, or via function-like macros. This function does not pop the table from the stack. honeysuckle provides the functions `hs_pt_set_bool`, `hs_pt_set_int`, `hs_pt_set_num`, and `hs_pt_set_string` for the very common operations of "set a variable to the provided value". These can also be set via the function-like macros `hs_set_bool()`, `hs_set_int()`, `hs_set_num()`, and `hs_set_str()`. User-defined functions should have signature `void (function)([type], void *)`, where [type] is either `lua_Boolean`, `lua_Integer`, `lua_Number`, or `const char *`, depending on the expected value type. ```c 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; float epsilon; }; struct settings s; hs_process_table(L, tbl_index, // manual struct initialization { "verbosity", HS_INT, .func.integer=hs_pt_set_int, &(s.verbosity) }, // macro initialization (recommended) hs_process_bool("debug", hs_pt_set_bool, s.debug), hs_process_str("logfile", set_logfile, s), // alternative to explicitly specifying hs_pt_set_bool hs_set_num("epsilon", s.epsilon) ); ``` ### Errors and error handling [(Back to top)](#table-of-contents) `hs_throw_error()` constructs and throws error messages like printf. ```c hs_throw_error(L, "ERROR: %d is not within range", 10); ``` `hs_traceback()` can be supplied to `lua_pcall()` to get a helpful stack trace error message: ```c lua_pushcfunction(L, hs_traceback); int tr_pos = lua_gettop(); // push function // push n arguments int result = lua_pcall(L, nargs, nret, tr_pos); ``` `hs_call()` performs the above, accepting simply a lua_State pointer, int nargs, and int nret as arguments. ### String wrangling [(Back to top)](#table-of-contents) `hs_pushstring()` allows you to push string values using printf format strings. ```c hs_pushstring(L, "hello there, %s. I hear the current time is %d:%02d", "dylan", 10, 5); ``` ### Registry values [(Back to top)](#table-of-contents) honeysuckle provides three macros, `hs_rstore()`, `hs_rload()`, and `hs_ rdel()` to make registry access easier. These are mostly just thin wrappers around `luaL_ref()`, `luaL_unref()` and `lua_rawgeti()` but they come up frequently enough that I think these are helpful. ```c lua_pushstring(L, "some string"); int my_string_ref = hs_rstore(L); // ... hs_rload(L, my_string_ref); // load the value referenced by my_string_ref hs_rdel(L, my_string_ref); // done with this reference, remove from the registry table ``` Development ----------- [(Back to top)](#table-of-contents) honeysuckle is still very early in development. As the specifics of the build process and testing become clearer I'll update this section. If you're interested in helping out, send me an email! The to-dos at the time of writing are: * Write initial tests for errors, tracebacks, and function calling * Implement everything * Expand the test cases. License ------- [(Back to top)](#table-of-contents) honeysuckle is licensed under the [Anti-Capitalist Software License]. Basically: if you're a single person, a co-op, or a nonprofit, feel free to use this. Otherwise, send me an email. c: [Anti-Capitalist Software License]: https://anticapitalist.software