diff options
-rw-r--r-- | thunk.h | 195 |
1 files changed, 190 insertions, 5 deletions
@@ -3,7 +3,7 @@ /* * Machinery for generating simple C thunks using the C preprocessor. - * Copyright (C) 2016 Vito Caputo <vcaputo@pengaru.com> + * Copyright (C) 2016,2020 Vito Caputo <vcaputo@pengaru.com> * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -370,6 +370,66 @@ struct thunk_t { _THUNK_GEN_PROTO_(_name, _C99_NARGS(__VA_ARGS__), __VA_ARGS__) +/* XXX: I'm unclear on why using the existing _THUNK_GEN_PROTO() macros refuse + * to work from the init case. The only thing I'm doing differently there is + * inserting `thunk_t *, thunk` parameters between the ##_name, and + * __VA_ARGS__. I expected the inserted pair to just combine with the + * __VA_ARGS__ pasted afterwards, but it kept spewing errors until I resorted + * to this variant. There may be some stupidity hiding in plain sight, maybe I + * needed to do another double evaluation dance, but this internal duplication + * works for now. TODO: investigate when time permits + */ +/* Macros for prototyping the initializer for the thunk being instantiated, + * we need a variant for each supported arity. */ +#define _THUNK_GEN_INIT_PROTO_0(_name, _thunk, _nil) \ + _name(_thunk) + +#define _THUNK_GEN_INIT_PROTO_2(_name, _thunk, _t_a, _n_a) \ + _name(_thunk, _t_a _n_a) + +#define _THUNK_GEN_INIT_PROTO_4(_name, _thunk, _t_a, _n_a, _t_b, _n_b) \ + _name(_thunk, _t_a _n_a, _t_b _n_b) + +#define _THUNK_GEN_INIT_PROTO_6(_name, _thunk, _t_a, _n_a, _t_b, _n_b, _t_c, _n_c) \ + _name(_thunk, _t_a _n_a, _t_b _n_b, _t_c _n_c) + +#define _THUNK_GEN_INIT_PROTO_8(_name, _thunk, _t_a, _n_a, _t_b, _n_b, _t_c, _n_c, _t_d, _n_d)\ + _name(_thunk, _t_a _n_a, _t_b _n_b, _t_c _n_c, _t_d _n_d) + +#define _THUNK_GEN_INIT_PROTO_10(_name, _thunk, _t_a, _n_a, _t_b, _n_b, _t_c, _n_c, _t_d, _n_d, _t_e, _n_e)\ + _name(_thunk, _t_a _n_a, _t_b _n_b, _t_c _n_c, _t_d _n_d, _t_e _n_e) + +#define _THUNK_GEN_INIT_PROTO_12(_name, _thunk, _t_a, _n_a, _t_b, _n_b, _t_c, _n_c, _t_d, _n_d, _t_e, _n_e, _t_f, _n_f)\ + _name(_thunk, _t_a _n_a, _t_b _n_b, _t_c _n_c, _t_d _n_d, _t_e _n_e, _t_f _n_f) + +#define _THUNK_GEN_INIT_PROTO_14(_name, _thunk, _t_a, _n_a, _t_b, _n_b, _t_c, _n_c, _t_d, _n_d, _t_e, _n_e, _t_f, _n_f, _t_g, _n_g)\ + _name(_thunk, _t_a _n_a, _t_b _n_b, _t_c _n_c, _t_d _n_d, _t_e _n_e, _t_f _n_f, _t_g _n_g) + +#define _THUNK_GEN_INIT_PROTO_16(_name, _thunk, _t_a, _n_a, _t_b, _n_b, _t_c, _n_c, _t_d, _n_d, _t_e, _n_e, _t_f, _n_f, _t_g, _n_g, _t_h, _n_h)\ + _name(_thunk, _t_a _n_a, _t_b _n_b, _t_c _n_c, _t_d _n_d, _t_e _n_e, _t_f _n_f, _t_g _n_g, _t_h _n_h) + +#define _THUNK_GEN_INIT_PROTO_18(_name, _thunk, _t_a, _n_a, _t_b, _n_b, _t_c, _n_c, _t_d, _n_d, _t_e, _n_e, _t_f, _n_f, _t_g, _n_g, _t_h, _n_h, _t_i, _n_i)\ + _name(_thunk, _t_a _n_a, _t_b _n_b, _t_c _n_c, _t_d _n_d, _t_e _n_e, _t_f _n_f, _t_g _n_g, _t_h _n_h, _t_i _n_i) + +#define _THUNK_GEN_INIT_PROTO_20(_name, _thunk, _t_a, _n_a, _t_b, _n_b, _t_c, _n_c, _t_d, _n_d, _t_e, _n_e, _t_f, _n_f, _t_g, _n_g, _t_h, _n_h, _t_i, _n_i, _t_j, _n_j)\ + _name(_thunk, _t_a _n_a, _t_b _n_b, _t_c _n_c, _t_d _n_d, _t_e _n_e, _t_f _n_f, _t_g _n_g, _t_h _n_h, _t_i _n_i, _t_j _n_j) + +#define _THUNK_GEN_INIT_PROTO_22(_name, _thunk, _t_a, _n_a, _t_b, _n_b, _t_c, _n_c, _t_d, _n_d, _t_e, _n_e, _t_f, _n_f, _t_g, _n_g, _t_h, _n_h, _t_i, _n_i, _t_j, _n_j, _t_k, _n_k)\ + _name(_thunk, _t_a _n_a, _t_b _n_b, _t_c _n_c, _t_d _n_d, _t_e _n_e, _t_f _n_f, _t_g _n_g, _t_h _n_h, _t_i _n_i, _t_j _n_j, _t_k _n_k) + +#define _THUNK_GEN_INIT_PROTO_24(_name, _thunk, _t_a, _n_a, _t_b, _n_b, _t_c, _n_c, _t_d, _n_d, _t_e, _n_e, _t_f, _n_f, _t_g, _n_g, _t_h, _n_h, _t_i, _n_i, _t_j, _n_j, _t_k, _n_k, _t_l, _n_l)\ + _name(_thunk, _t_a _n_a, _t_b _n_b, _t_c _n_c, _t_d _n_d, _t_e _n_e, _t_f _n_f, _t_g _n_g, _t_h _n_h, _t_i _n_i, _t_j _n_j, _t_k _n_k, _t_l _n_l) + +#define _THUNK_GEN_INIT_PROTO__(_name, _thunk, _count, ...) \ + _THUNK_GEN_INIT_PROTO_##_count(_name, _thunk, __VA_ARGS__) + +#define _THUNK_GEN_INIT_PROTO_(_name, _thunk, _count, ...) \ + _THUNK_GEN_INIT_PROTO__(_name, _thunk, _count, __VA_ARGS__) + +#define _THUNK_GEN_INIT_PROTO(_name, _thunk, ...) \ + _THUNK_GEN_INIT_PROTO_(_name, _thunk, _C99_NARGS(__VA_ARGS__), __VA_ARGS__) + + /* Define a function for use with a thunk. This generates the scaffolding * functions and structure for (un)packing the args with an appropriately * wrapped thunk_t. Calls to this function may then be supplied to anything @@ -385,6 +445,7 @@ struct thunk_t { /* struct for encapsulating the calling environment */ \ thunk_t __thunk; \ _THUNK_GEN_STRUCT (__VA_ARGS__); \ + char __payload[]; \ } __thunk_environment_##_name; \ \ static int __thunk_dispatch_##_name(thunk_t *thunk) { \ @@ -416,6 +477,33 @@ struct thunk_t { return &env->__thunk; \ } \ \ + static thunk_t * __thunk_alloc_##_name(void **payload_ptr, size_t payload_size) {\ + /* allocate a thunk with an optional extra payload ptr, return it */\ + __thunk_environment_##_name *env; \ + \ + env = malloc(sizeof(*env) + payload_size); \ + assert(env); \ + \ + if (payload_ptr) \ + *payload_ptr = env->__payload; \ + \ + env->__thunk.dispatch = __thunk_dispatch_##_name; \ + \ + return &env->__thunk; \ + } \ + \ + static thunk_t * _THUNK_GEN_INIT_PROTO(__thunk_init_##_name, thunk_t *_thunk, __VA_ARGS__) {\ + /* initialize environment provided as a pre-allocd thunk, return it */\ + __thunk_environment_##_name *env = (__thunk_environment_##_name *)_thunk;\ + \ + assert(env); \ + assert(env->__thunk.dispatch == __thunk_dispatch_##_name); \ + \ + _THUNK_GEN_INITIALIZE(env, __VA_ARGS__); \ + \ + return &env->__thunk; \ + } \ + \ static int _THUNK_GEN_PROTO(_name, __VA_ARGS__) @@ -429,9 +517,12 @@ struct thunk_t { /* struct for encapsulating the calling environment */ \ thunk_t __thunk; \ _THUNK_GEN_STRUCT (__VA_ARGS__); \ + char __payload[]; \ } __thunk_environment_##_name; \ \ int __thunk_dispatch_##_name(thunk_t *thunk); \ + thunk_t * _THUNK_GEN_INIT_PROTO(__thunk_init_##_name, thunk_t *_thunk, __VA_ARGS__);\ + thunk_t * __thunk_alloc_##_name(void **payload_ptr, size_t payload_size);\ thunk_t * _THUNK_GEN_PROTO(__thunk_instantiate_##_name, __VA_ARGS__); @@ -465,19 +556,113 @@ struct thunk_t { return &env->__thunk; \ } \ \ + thunk_t * __thunk_alloc_##_name(void **payload_ptr, size_t payload_size) {\ + /* allocate a thunk with an optional extra payload ptr, return it */\ + __thunk_environment_##_name *env; \ + \ + env = malloc(sizeof(*env) + payload_size); \ + assert(env); \ + \ + if (payload_ptr) \ + *payload_ptr = env->__payload; \ + \ + env->__thunk.dispatch = __thunk_dispatch_##_name; \ + \ + return &env->__thunk; \ + } \ + \ + thunk_t * _THUNK_GEN_INIT_PROTO(__thunk_init_##_name, thunk_t *_thunk, __VA_ARGS__) {\ + /* initialize environment provided as a pre-allocd thunk, return it */\ + __thunk_environment_##_name *env = (__thunk_environment_##_name *)_thunk;\ + \ + assert(env); \ + assert(env->__thunk.dispatch == __thunk_dispatch_##_name); \ + \ + _THUNK_GEN_INITIALIZE(env, __VA_ARGS__); \ + \ + return &env->__thunk; \ + } \ + \ int _THUNK_GEN_PROTO(_name, __VA_ARGS__) -/* Call the appropriate instantiate function which returns an embedded thunk_t, - * this is how you prepare the thunks established via THUNK_DEFINE(). */ +/* The following macros wrap the appropriate thunk-specific functions for + * instantiating a pre-defined thunk. + */ + +/* This is the simplest form where you just write a call to the + * THUNK_DEFINE()'d function as usual, but wrapped in THUNK(). Most of the + * time this is sufficient, but in more complicated scenarios you may need the + * split THUNK_ALLOC() and THUNK_INIT() pair further below. + */ #define _THUNK(_call) \ __thunk_instantiate_##_call #define THUNK(_call) \ _THUNK(_call) -/* Call the function associated with a prepared thunk, supplying the bound environment - * as arguments. This is how code receiving generic thunks executes them. */ + +/* This is for separately allocating the thunk instance for the named + * THUNK_DEFINE()'d function, with an optional payload of the specified size + * allocated as part of the instance, with the payload's pointer stored @ + * _payload_ptr if non-NULL. There is no way to retrieve this payload pointer + * after this point, it's completely opaque to everything, provided purely as a + * place to externally stow some additional instance-bound state. + * + * The instance is not ready to dispatch yet at this point, it still needs + * initializing to bind any variables to the thunk's calling environment. + * Those variables may reference indirectly to state stowed in the payload + * pointer returned here, which is the main reason this became a two-step + * process. + */ +#define _THUNK_ALLOC(_func, _payload_ptr, _payload_size) \ + __thunk_alloc_##_func(_payload_ptr, _payload_size) + +#define THUNK_ALLOC(_func, _payload_ptr, _payload_size) \ + _THUNK_ALLOC(_func, _payload_ptr, _payload_size) + + +/* This is for initializing a distinctly allocated instance from THUNK_ALLOC(). + * You only should use the split ALLOC+INIT method when using the embedded + * payload feature, otherwise the simpler THUNK() should suffice. + * + * Use is basically identical to THUNK() except the parameters for the function + * as established by THUNK_DEFINE() must be prefixed by the (thunk_t *) + * returned by THUNK_ALLOC() as the first parameter, so the initializer can + * find the pre-allocated instance to initialize. + * + * i.e. for the function foo_func: THUNK_DEFINE(foo_func, char *, bar): + * + * Allocating the instance foo_thunk w/4096 byte payload, accessed via buf: + * + * char *buf; + * thunk_t foo_thunk = THUNK_ALLOC(foo_func, &buf, 4096); + * + * Initializing foo_thunk with "bar-str" as the (char *bar) parameter: + * + * THUNK_INIT(foo_func(foo_thunk, "bar-str")); + * ^^^^ note the thunk is supplied, vs. w/THUNK(): + * THUNK(foo_func("bar-str")); + * + * XXX: note that you cannot mix functions across THUNK_ALLOC and THUNK_INIT + * on a given instance. You must initialize an instance allocated for a given + * function with the same function. There is an assert in place to help + * prevent this type of mistake. + */ +#define _THUNK_INIT(_call) \ + __thunk_init_##_call + +#define THUNK_INIT(_call) \ + _THUNK_INIT(_call) + + +/* Call the function associated with a thunk instance, supplying the bound + * environment as arguments. This is how code receiving generic thunks + * executes them. + * + * After the thunked function returns, the instance is automatically freed, + * including any payload if THUNK_ALLOC() was used. + */ static inline int thunk_dispatch(thunk_t *thunk) { return thunk->dispatch(thunk); } |