summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--thunk.h195
1 files changed, 190 insertions, 5 deletions
diff --git a/thunk.h b/thunk.h
index d2ec683..8bcc6d7 100644
--- a/thunk.h
+++ b/thunk.h
@@ -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);
}
© All Rights Reserved