#ifndef _THUNK_H #define _THUNK_H /* * Machinery for generating simple C thunks using the C preprocessor. * Copyright (C) 2016,2020 Vito Caputo * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License version 2.1 as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #include #include #include typedef struct thunk_t thunk_t; struct thunk_t { int (*dispatch)(thunk_t *); }; /* Clever macro args counting from https://gcc.gnu.org/ml/gcc/2000-09/msg00604.html */ #define _C99_NARGS(...) \ _C99_NARGS1( , ##__VA_ARGS__) #define _C99_NARGS1(...) \ _C99_NARGS2(__VA_ARGS__, 24, 23, 22, 21, 20, 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0) #define _C99_NARGS2(_, x0, x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11, x12, x13, x14, x15, x16, x17, x18, x19, x20, x21, x22, x23, _n, ...) \ _n /* Macros for declaring the thunk environment struct members, we need a variant * for each supported arity. */ #define _THUNK_GEN_STRUCT_0(_nil) #define _THUNK_GEN_STRUCT_2(_t_a, _n_a) \ _t_a _n_a; #define _THUNK_GEN_STRUCT_4(_t_a, _n_a, _t_b, _n_b) \ _t_a _n_a; \ _t_b _n_b; #define _THUNK_GEN_STRUCT_6(_t_a, _n_a, _t_b, _n_b, _t_c, _n_c) \ _t_a _n_a; \ _t_b _n_b; \ _t_c _n_c; #define _THUNK_GEN_STRUCT_8(_t_a, _n_a, _t_b, _n_b, _t_c, _n_c, _t_d, _n_d) \ _t_a _n_a; \ _t_b _n_b; \ _t_c _n_c; \ _t_d _n_d; #define _THUNK_GEN_STRUCT_10(_t_a, _n_a, _t_b, _n_b, _t_c, _n_c, _t_d, _n_d, _t_e, _n_e)\ _t_a _n_a; \ _t_b _n_b; \ _t_c _n_c; \ _t_d _n_d; \ _t_e _n_e; #define _THUNK_GEN_STRUCT_12(_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_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_STRUCT_14(_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_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_STRUCT_16(_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_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_STRUCT_18(_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_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_STRUCT_20(_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_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_STRUCT_22(_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_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_STRUCT_24(_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)\ _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_STRUCT__(_count, ...) \ _THUNK_GEN_STRUCT_##_count(__VA_ARGS__) #define _THUNK_GEN_STRUCT_(_count, ...) \ _THUNK_GEN_STRUCT__(_count, __VA_ARGS__) #define _THUNK_GEN_STRUCT(...) \ _THUNK_GEN_STRUCT_(_C99_NARGS(__VA_ARGS__), __VA_ARGS__) /* Macros for populating the thunk environment struct members when instantiating the * thunk, we need a variant for each supported arity. */ #define _THUNK_GEN_INITIALIZE_0(_env, _nil) #define _THUNK_GEN_INITIALIZE_2(_env, _t_a, _n_a) \ _env->_n_a = _n_a; #define _THUNK_GEN_INITIALIZE_4(_env, _t_a, _n_a, _t_b, _n_b) \ _env->_n_a = _n_a; \ _env->_n_b = _n_b; #define _THUNK_GEN_INITIALIZE_6(_env, _t_a, _n_a, _t_b, _n_b, _t_c, _n_c) \ _env->_n_a = _n_a; \ _env->_n_b = _n_b; \ _env->_n_c = _n_c; #define _THUNK_GEN_INITIALIZE_8(_env, _t_a, _n_a, _t_b, _n_b, _t_c, _n_c, _t_d, _n_d)\ _env->_n_a = _n_a; \ _env->_n_b = _n_b; \ _env->_n_c = _n_c; \ _env->_n_d = _n_d; #define _THUNK_GEN_INITIALIZE_10(_env, _t_a, _n_a, _t_b, _n_b, _t_c, _n_c, _t_d, _n_d, _t_e, _n_e)\ _env->_n_a = _n_a; \ _env->_n_b = _n_b; \ _env->_n_c = _n_c; \ _env->_n_d = _n_d; \ _env->_n_e = _n_e; #define _THUNK_GEN_INITIALIZE_12(_env, _t_a, _n_a, _t_b, _n_b, _t_c, _n_c, _t_d, _n_d, _t_e, _n_e, _t_f, _n_f)\ _env->_n_a = _n_a; \ _env->_n_b = _n_b; \ _env->_n_c = _n_c; \ _env->_n_d = _n_d; \ _env->_n_e = _n_e; \ _env->_n_f = _n_f; #define _THUNK_GEN_INITIALIZE_14(_env, _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)\ _env->_n_a = _n_a; \ _env->_n_b = _n_b; \ _env->_n_c = _n_c; \ _env->_n_d = _n_d; \ _env->_n_e = _n_e; \ _env->_n_f = _n_f; \ _env->_n_g = _n_g; #define _THUNK_GEN_INITIALIZE_16(_env, _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)\ _env->_n_a = _n_a; \ _env->_n_b = _n_b; \ _env->_n_c = _n_c; \ _env->_n_d = _n_d; \ _env->_n_e = _n_e; \ _env->_n_f = _n_f; \ _env->_n_g = _n_g; \ _env->_n_h = _n_h; #define _THUNK_GEN_INITIALIZE_18(_env, _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)\ _env->_n_a = _n_a; \ _env->_n_b = _n_b; \ _env->_n_c = _n_c; \ _env->_n_d = _n_d; \ _env->_n_e = _n_e; \ _env->_n_f = _n_f; \ _env->_n_g = _n_g; \ _env->_n_h = _n_h; \ _env->_n_i = _n_i; #define _THUNK_GEN_INITIALIZE_20(_env, _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)\ _env->_n_a = _n_a; \ _env->_n_b = _n_b; \ _env->_n_c = _n_c; \ _env->_n_d = _n_d; \ _env->_n_e = _n_e; \ _env->_n_f = _n_f; \ _env->_n_g = _n_g; \ _env->_n_h = _n_h; \ _env->_n_i = _n_i; \ _env->_n_j = _n_j; #define _THUNK_GEN_INITIALIZE_22(_env, _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)\ _env->_n_a = _n_a; \ _env->_n_b = _n_b; \ _env->_n_c = _n_c; \ _env->_n_d = _n_d; \ _env->_n_e = _n_e; \ _env->_n_f = _n_f; \ _env->_n_g = _n_g; \ _env->_n_h = _n_h; \ _env->_n_i = _n_i; \ _env->_n_j = _n_j; \ _env->_n_k = _n_k; #define _THUNK_GEN_INITIALIZE_24(_env, _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)\ _env->_n_a = _n_a; \ _env->_n_b = _n_b; \ _env->_n_c = _n_c; \ _env->_n_d = _n_d; \ _env->_n_e = _n_e; \ _env->_n_f = _n_f; \ _env->_n_g = _n_g; \ _env->_n_h = _n_h; \ _env->_n_i = _n_i; \ _env->_n_j = _n_j; \ _env->_n_k = _n_k; \ _env->_n_l = _n_l; #define _THUNK_GEN_INITIALIZE__(_env, _count, ...) \ _THUNK_GEN_INITIALIZE_##_count(_env, __VA_ARGS__) #define _THUNK_GEN_INITIALIZE_(_env, _count, ...) \ _THUNK_GEN_INITIALIZE__(_env, _count, __VA_ARGS__) #define _THUNK_GEN_INITIALIZE(_env, ...) \ _THUNK_GEN_INITIALIZE_(_env, _C99_NARGS(__VA_ARGS__), __VA_ARGS__) /* Macros for dispatching the embedded thunk from its associated environment, * we need a variant for each supported arity. */ #define _THUNK_GEN_DISPATCH_0(_name, _env, _nil) \ _name() #define _THUNK_GEN_DISPATCH_2(_name, _env, _t_a, _n_a) \ _name(_env->_n_a) #define _THUNK_GEN_DISPATCH_4(_name, _env, _t_a, _n_a, _t_b, _n_b) \ _name(_env->_n_a, _env->_n_b) #define _THUNK_GEN_DISPATCH_6(_name, _env, _t_a, _n_a, _t_b, _n_b, _t_c, _n_c) \ _name(_env->_n_a, _env->_n_b, _env->_n_c) #define _THUNK_GEN_DISPATCH_8(_name, _env, _t_a, _n_a, _t_b, _n_b, _t_c, _n_c, _t_d, _n_d)\ _name(_env->_n_a, _env->_n_b, _env->_n_c, _env->_n_d) #define _THUNK_GEN_DISPATCH_10(_name, _env, _t_a, _n_a, _t_b, _n_b, _t_c, _n_c, _t_d, _n_d, _t_e, _n_e)\ _name(_env->_n_a, _env->_n_b, _env->_n_c, _env->_n_d, _env->_n_e) #define _THUNK_GEN_DISPATCH_12(_name, _env, _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(_env->_n_a, _env->_n_b, _env->_n_c, _env->_n_d, _env->_n_e, _env->_n_f) #define _THUNK_GEN_DISPATCH_14(_name, _env, _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(_env->_n_a, _env->_n_b, _env->_n_c, _env->_n_d, _env->_n_e, _env->_n_f, _env->_n_g) #define _THUNK_GEN_DISPATCH_16(_name, _env, _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(_env->_n_a, _env->_n_b, _env->_n_c, _env->_n_d, _env->_n_e, _env->_n_f, _env->_n_g, _env->_n_h) #define _THUNK_GEN_DISPATCH_18(_name, _env, _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(_env->_n_a, _env->_n_b, _env->_n_c, _env->_n_d, _env->_n_e, _env->_n_f, _env->_n_g, _env->_n_h, _env->_n_i) #define _THUNK_GEN_DISPATCH_20(_name, _env, _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(_env->_n_a, _env->_n_b, _env->_n_c, _env->_n_d, _env->_n_e, _env->_n_f, _env->_n_g, _env->_n_h, _env->_n_i, _env->_n_j) #define _THUNK_GEN_DISPATCH_22(_name, _env, _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(_env->_n_a, _env->_n_b, _env->_n_c, _env->_n_d, _env->_n_e, _env->_n_f, _env->_n_g, _env->_n_h, _env->_n_i, _env->_n_j, _env->n_k) #define _THUNK_GEN_DISPATCH_24(_name, _env, _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(_env->_n_a, _env->_n_b, _env->_n_c, _env->_n_d, _env->_n_e, _env->_n_f, _env->_n_g, _env->_n_h, _env->_n_i, _env->_n_j, _env->n_k, _env->n_l) #define _THUNK_GEN_DISPATCH__(_name, _env, _count, ...) \ _THUNK_GEN_DISPATCH_##_count(_name, _env, __VA_ARGS__) #define _THUNK_GEN_DISPATCH_(_name, _env, _count, ...) \ _THUNK_GEN_DISPATCH__(_name, _env, _count, __VA_ARGS__) #define _THUNK_GEN_DISPATCH(_name, _env, ...) \ _THUNK_GEN_DISPATCH_(_name, _env, _C99_NARGS(__VA_ARGS__), __VA_ARGS__) /* Macros for prototyping the function being thunked, * we need a variant for each supported arity. */ #define _THUNK_GEN_PROTO_0(_name, _nil) \ _name(void) #define _THUNK_GEN_PROTO_2(_name, _t_a, _n_a) \ _name(_t_a _n_a) #define _THUNK_GEN_PROTO_4(_name, _t_a, _n_a, _t_b, _n_b) \ _name(_t_a _n_a, _t_b _n_b) #define _THUNK_GEN_PROTO_6(_name, _t_a, _n_a, _t_b, _n_b, _t_c, _n_c) \ _name(_t_a _n_a, _t_b _n_b, _t_c _n_c) #define _THUNK_GEN_PROTO_8(_name, _t_a, _n_a, _t_b, _n_b, _t_c, _n_c, _t_d, _n_d)\ _name(_t_a _n_a, _t_b _n_b, _t_c _n_c, _t_d _n_d) #define _THUNK_GEN_PROTO_10(_name, _t_a, _n_a, _t_b, _n_b, _t_c, _n_c, _t_d, _n_d, _t_e, _n_e)\ _name(_t_a _n_a, _t_b _n_b, _t_c _n_c, _t_d _n_d, _t_e _n_e) #define _THUNK_GEN_PROTO_12(_name, _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(_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_PROTO_14(_name, _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(_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_PROTO_16(_name, _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(_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_PROTO_18(_name, _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(_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_PROTO_20(_name, _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(_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_PROTO_22(_name, _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(_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_PROTO_24(_name, _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(_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_PROTO__(_name, _count, ...) \ _THUNK_GEN_PROTO_##_count(_name, __VA_ARGS__) #define _THUNK_GEN_PROTO_(_name, _count, ...) \ _THUNK_GEN_PROTO__(_name, _count, __VA_ARGS__) #define _THUNK_GEN_PROTO(_name, ...) \ _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 * accepting a (thunk_t *) by simply wrapping the call with THUNK(). */ /* This is the static variant for private everything static placed local to the * function definition, all in one place in one fell swoop. */ #define THUNK_DEFINE_STATIC(_name, ...) \ static int _THUNK_GEN_PROTO(_name, __VA_ARGS__) __attribute__ ((unused));\ static int __thunk_dispatch_##_name(thunk_t *thunk) __attribute__ ((unused));\ static thunk_t * __thunk_alloc_##_name(void **payload_ptr, size_t payload_size) __attribute__ ((unused));\ static thunk_t * _THUNK_GEN_PROTO(__thunk_instantiate_##_name, __VA_ARGS__) __attribute__ ((unused));\ static thunk_t * _THUNK_GEN_INIT_PROTO(__thunk_init_##_name, thunk_t *_thunk, __VA_ARGS__) __attribute__ ((unused));\ \ typedef struct __thunk_environment_##_name { \ /* 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) { \ /* dispatch thunk from associated environment */ \ __thunk_environment_##_name *env; \ int r; \ \ /* XXX: the embedded __thunk is always at the start, otherwise \ * container_of() could be used... */ \ env = (__thunk_environment_##_name *)thunk; \ assert(env); \ \ r = _THUNK_GEN_DISPATCH(_name, env, __VA_ARGS__); \ \ return r; \ } \ \ static thunk_t * _THUNK_GEN_PROTO(__thunk_instantiate_##_name, __VA_ARGS__) {\ /* allocate and initialize environment, return it */ \ __thunk_environment_##_name *env; \ \ env = malloc(sizeof(*env)); \ assert(env); \ \ _THUNK_GEN_INITIALIZE(env, __VA_ARGS__); \ env->__thunk.dispatch = __thunk_dispatch_##_name; \ \ 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) { \ memset(env->__payload, 0, payload_size); \ *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__) /* this is the public variant which is split across declare & define */ /* put the declaration in the .h, the define in the .c, be sure to include the * .h from the implementing .c - not just the consumers. */ #define THUNK_DECLARE(_name, ...) \ int _THUNK_GEN_PROTO(_name, __VA_ARGS__) __attribute__ ((unused)); \ typedef struct __thunk_environment_##_name { \ /* 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) __attribute__ ((unused)); \ thunk_t * _THUNK_GEN_INIT_PROTO(__thunk_init_##_name, thunk_t *_thunk, __VA_ARGS__) __attribute__ ((unused));\ thunk_t * __thunk_alloc_##_name(void **payload_ptr, size_t payload_size) __attribute__ ((unused));\ thunk_t * _THUNK_GEN_PROTO(__thunk_instantiate_##_name, __VA_ARGS__) __attribute__ ((unused)); #define THUNK_DEFINE(_name, ...) \ int __thunk_dispatch_##_name(thunk_t *thunk) { \ /* dispatch thunk from associated environment */ \ __thunk_environment_##_name *env; \ int r; \ \ /* XXX: the embedded __thunk is always at the start, otherwise \ * container_of() could be used... */ \ env = (__thunk_environment_##_name *)thunk; \ assert(env); \ \ r = _THUNK_GEN_DISPATCH(_name, env, __VA_ARGS__); \ \ return r; \ } \ \ thunk_t * _THUNK_GEN_PROTO(__thunk_instantiate_##_name, __VA_ARGS__) { \ /* allocate and populate environment, return it */ \ __thunk_environment_##_name *env; \ \ env = malloc(sizeof(*env)); \ assert(env); \ \ _THUNK_GEN_INITIALIZE(env, __VA_ARGS__); \ env->__thunk.dispatch = __thunk_dispatch_##_name; \ \ 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) { \ memset(env->__payload, 0, payload_size); \ *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__) /* 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) /* 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) { int r; r = thunk->dispatch(thunk); free(thunk); return r; } /* Same as thunk_dispatch() but without the free. * Callers can trivially free thunk instances w/free(), there's * no fancy destructor stuff or anything. So when a thunk needs * to be used repeatedly in a loop for instance, use this, then * just free the thunk yourself. */ static inline int thunk_dispatch_keep(thunk_t *thunk) { return thunk->dispatch(thunk); } #endif