diff options
Diffstat (limited to 'lib')
-rw-r--r-- | lib/macro.h | 98 |
1 files changed, 98 insertions, 0 deletions
diff --git a/lib/macro.h b/lib/macro.h new file mode 100644 index 00000000..24fc3393 --- /dev/null +++ b/lib/macro.h @@ -0,0 +1,98 @@ +/* + * BIRD Macro Tricks + * + * (c) 2018 Jan Maria Matejka <mq@jmq.cz> + * + * Can be freely distributed and used under the terms of the GNU GPL. + * + * Contains useful but dirty macro tricks: + * MACRO_CONCAT(a, b) -> concatenates a##b + * MACRO_BOOL(x) -> convert 0 to 0, anything else to 1 + * MACRO_IFELSE(b)(true-branch)(false-branch) + * -> b shall be 0 or 1; expands to the appropriate branch + * MACRO_ISEMPTY(...) -> 1 for empty argument list, 0 otherwise + * MACRO_FOREACH(func, ...) + * -> calling FOREACH(func, a, b, c, d) expands to + * func(a) func(b) func(c) func(d) + * MACRO_RPACK(func, terminator, ...) + * -> packs the list into recursive calls: + * func(func(func(func(terminator, a), b), c), d) + */ + +#ifndef _BIRD_MACRO_H_ +#define _BIRD_MACRO_H_ + +/* What to do with args */ +#define MACRO_DROP(...) +#define MACRO_UNPAREN(...) __VA_ARGS__ +#define MACRO_SEP(a, b, sep) a sep b + +/* Aliases for some special chars */ +#define MACRO_COMMA , +#define MACRO_LPAREN ( +#define MACRO_RPAREN ) +#define MACRO_LPAREN_() ( +#define MACRO_RPAREN_() ) + +/* Multiple expansion trick */ +#define MACRO_EXPAND0(...) __VA_ARGS__ +#define MACRO_EXPAND1(...) MACRO_EXPAND0(MACRO_EXPAND0(__VA_ARGS__)) +#define MACRO_EXPAND2(...) MACRO_EXPAND1(MACRO_EXPAND1(__VA_ARGS__)) +#define MACRO_EXPAND3(...) MACRO_EXPAND2(MACRO_EXPAND2(__VA_ARGS__)) +#define MACRO_EXPAND(...) MACRO_EXPAND3(MACRO_EXPAND3(__VA_ARGS__)) + +/* Deferring expansion in the expansion trick */ +#define MACRO_EMPTY() +#define MACRO_DEFER(t) t MACRO_EMPTY() +#define MACRO_DEFER2(t) t MACRO_EMPTY MACRO_EMPTY()() +#define MACRO_DEFER3(t) t MACRO_EMPTY MACRO_EMPTY MACRO_EMPTY()()() + +/* Token concatenation */ +#define MACRO_CONCAT(prefix, ...) prefix##__VA_ARGS__ +#define MACRO_CONCAT_AFTER(...) MACRO_CONCAT(__VA_ARGS__) + +/* Get first or second argument only */ +#define MACRO_FIRST(a, ...) a +#define MACRO_SECOND(a, b, ...) b +#define MACRO_SECOND_OR_ZERO(...) MACRO_SECOND(__VA_ARGS__, 0,) + +/* Macro Boolean auxiliary macros */ +#define MACRO_BOOL_CHECK_0 ~, 1 +#define MACRO_BOOL_NEG(x) MACRO_SECOND_OR_ZERO(MACRO_CONCAT(MACRO_BOOL_CHECK_, x)) + +#define MACRO_BOOL_NOT_0 1 +#define MACRO_BOOL_NOT_1 0 + +/* Macro Boolean negation */ +#define MACRO_NOT(x) MACRO_CONCAT(MACRO_BOOL_NOT_, x) + +/* Convert anything to bool (anything -> 1, 0 -> 0) */ +#define MACRO_BOOL(x) MACRO_NOT(MACRO_BOOL_NEG(x)) + +/* + * Macro If/Else condition + * Usage: MACRO_IFELSE(condition)(true-branch)(false-branch) + * Expands to true-branch if condition is true, otherwise to false-branch. + */ +#define MACRO_IFELSE(b) MACRO_CONCAT(MACRO_IFELSE_, b) +#define MACRO_IFELSE_0(...) MACRO_UNPAREN +#define MACRO_IFELSE_1(...) __VA_ARGS__ MACRO_DROP + +/* Auxiliary macros for MACRO_FOREACH */ +#define MACRO_ISLAST(...) MACRO_BOOL_NEG(MACRO_FIRST(MACRO_ISLAST_CHECK __VA_ARGS__)()) +#define MACRO_ISLAST_CHECK() 0 + +#define MACRO_FOREACH_EXPAND(call, a, ...) MACRO_IFELSE(MACRO_ISLAST(__VA_ARGS__))(call(a))(call(a) MACRO_DEFER2(MACRO_FOREACH_PAREN)()(call, __VA_ARGS__)) +#define MACRO_FOREACH_PAREN() MACRO_FOREACH_EXPAND + +#define MACRO_RPACK_EXPAND(call, terminator, a, ...) MACRO_IFELSE(MACRO_ISLAST(__VA_ARGS__))(call(terminator, a))(call(MACRO_DEFER2(MACRO_RPACK_PAREN)()(call, terminator, __VA_ARGS__), a)) +#define MACRO_RPACK_PAREN() MACRO_RPACK_EXPAND +/* + * Call the first argument for each following: + * MACRO_FOREACH(func, a, b, c, d) expands to func(a) func(b) func(c) func(d). + * It supports also macros as func. + */ +#define MACRO_FOREACH(call, ...) MACRO_EXPAND(MACRO_FOREACH_EXPAND(call, __VA_ARGS__)) +#define MACRO_RPACK(call, terminator, ...) MACRO_EXPAND(MACRO_RPACK_EXPAND(call, terminator, __VA_ARGS__)) + +#endif |