--- 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` - integers - `HS_NUM` - numbers (double by default) - `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. ### 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 alternating type constant and pointer pairs, and finally `HS_END` to close the arguments. 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. ```C int some_lua_binding(lua_State *L) { bool b; int i; int tbl_index; lua_Number n; char *str; void* user; hs_parse_args(L, HS_BOOL, &b, HS_INT, &i, HS_TBL, &tbl_index, HS_NUM, &n, HS_STR, &str, HS_USER, &user, HS_END); // 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 after each `HS_END` you may provide another argument list, and after the final argument list you must provide another `HS_END`. This function throws an error only if none of the argument lists match the provided arguments. ```C int overloaded_lua_binding(lua_State *L) { int i; char *str; int choice = hs_parse_overloaded(L, HS_INT, &i, HS_END, HS_STR, &str, HS_END, HS_END); if (choice == 0) { // do something with i // str remains unassigned!! } else { // choice == 1, do something with str // i remains unassigned!! } return 0; } ``` ### Table construction [(Back to top)](#table-of-contents) `hs_create_table()` takes a lua_State pointer, some number of string key/type constant/value triplets, and finally `HS_END`. It returns the stack index of the newly created table. Most of the type constants expect their associated type (e.g. `HS_INT` expects an `int`, `HS_CFUNC` expects `lua_CFunction`, etc.) but `HS_FUNC` and `HS_TBL` expect an integer stack index. You cannot use `HS_NIL` or `HS_ANY` with this function. `hs_create_table()` automatically pops any stack indices provided to it. ```C int *ptr; // some pointer hs_create_table(L, "intVal", HS_INT, 4, "numVal", HS_NUM, 6.0f, "tableVal", HS_TBL, hs_create_table(L, "subTableCFunc", HS_CFUNC, some_lua_binding, "subTableString", HS_STR, "i'm a string!", HS_END), "lightUserdataVal", HS_LIGHT, (void *)ptr, HS_END); ``` #### Enums `hs_create_enum()` takes a lua_State pointer, some number of string/integer pairs, and finally `HS_END`. It creates a table in which, for each string/int pair `tbl[string] = int` and `tbl[int] = string` and returns the stack index of said table. `hs_string_to_enum()` uses such a table to convert a string literal to enum value and `hs_enum_to_string()` converts enums to strings. In case the value does not exist, converting to a string results in NULL and converting to an enum results in a negative value. ```C enum { VALUE_1, VALUE_2, VALUE_3 } example_t; int index = hs_create_enum(L, "one", VALUE_1, "two", VALUE_2, "three", VALUE_3, HS_END); example_t e = hs_string_to_enum(L, index, "one"); // VALUE_1 char *str = hs_enum_to_string(L, index, VALUE_3); // "three" ``` ### 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 can only operate on stringly-keyed values. ```C void set_verbosity(int value, void *data) { // set verbosity level } void set_debug(bool value, void *data) { // set debug } void set_logfile(char *value, void *data) { // set log filename } void process_any_key(int stack_index, void *data) { // do something } // tbl_index comes from somewhere... hs_process_table(L, tbl_index, NULL, "verbosity", HS_INT, set_verbosity, "debug", HS_BOOL, set_debug, "logfile", HS_STR, set_logfile, "extraData", HS_ANY, process_any_key, HS_END); ``` ### 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. Alternatively, you can use `hs_call_args()` which provides the same helpful, contained error messages as `hs_call()` but takes arguments explicitly in the call for both function arguments and returns: ```C // some_function accepts int, str and returns number, boolean lua_pushcfunction(L, some_function); lua_Number n; bool b; hs_call_args(L, HS_INT, 4, HS_STR, "hello", HS_END, HS_NUM, &n, HS_BOOL, &b, HS_END); ``` ### 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: 1. Finish writing the basic tests 2. Write implementations for the API described above 3. 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