1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
|
---
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 `<honeysuckle.h>` 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 (aka long int)
- `HS_NUM` - lua_Number (aka double)
- `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
`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.
```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.
If none of the argument lists match the provided arguments, this
function throws an error.
```c
int overloaded_lua_binding(lua_State *L)
{
long int i;
double 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 `long int`, `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),
),
"lightUserdataVal", HS_LIGHT, (void *)ptr,
HS_END);
```
### 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;
long int 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
|