#ifndef _THUNK_H #define _THUNK_H /* * Machinery for generating simple C thunks using the C preprocessor. * Copyright (C) 2016 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 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__, 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, _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_INSTANTIATE_0(_env, _nil) #define _THUNK_GEN_INSTANTIATE_2(_env, _t_a, _n_a) \ _env->_n_a = _n_a; #define _THUNK_GEN_INSTANTIATE_4(_env, _t_a, _n_a, _t_b, _n_b) \ _env->_n_a = _n_a; \ _env->_n_b = _n_b; #define _THUNK_GEN_INSTANTIATE_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_INSTANTIATE_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_INSTANTIATE_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_INSTANTIATE_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_INSTANTIATE_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_INSTANTIATE_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_INSTANTIATE_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_INSTANTIATE_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_INSTANTIATE_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_INSTANTIATE_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_INSTANTIATE__(_env, _count, ...) \ _THUNK_GEN_INSTANTIATE_##_count(_env, __VA_ARGS__) #define _THUNK_GEN_INSTANTIATE_(_env, _count, ...) \ _THUNK_GEN_INSTANTIATE__(_env, _count, __VA_ARGS__) #define _THUNK_GEN_INSTANTIATE(_env, ...) \ _THUNK_GEN_INSTANTIATE_(_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__) /* 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(). */ #define THUNK_DEFINE(_name, ...) \ static int _THUNK_GEN_PROTO(_name, __VA_ARGS__); \ typedef struct __thunk_environment_##_name { \ /* struct for encapsulating the calling environment */ \ thunk_t __thunk; \ _THUNK_GEN_STRUCT (__VA_ARGS__); \ } __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__); \ free(env); \ \ return r; \ } \ \ static 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_INSTANTIATE(env, __VA_ARGS__); \ env->__thunk.dispatch = __thunk_dispatch_##_name; \ \ return &env->__thunk; \ } \ \ static 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(). */ #define THUNK(_call) \ __thunk_instantiate_##_call /* Call the function associated with a prepared thunk, supplying the bound environment * as arguments. This is how code receiving generic thunks executes them. */ static inline int thunk_dispatch(thunk_t *thunk) { return thunk->dispatch(thunk); } #endif