From: David Gibson Date: Tue, 26 Jan 2016 10:54:47 +0000 (+1100) Subject: cppmagic: Iteration X-Git-Url: http://git.ozlabs.org/?p=ccan;a=commitdiff_plain;h=0ca50da908dcc8f97af4145a9afce714cd6b225d cppmagic: Iteration This implements macros which iterate across their arguments. This is implemented in terms of (kinda sorta) recursion. In fact, they will stop working with enough arguments, but the limit is large and can be easily increased by changing the depth of the CPPMAGIC_EVAL() macro. There are 3 iterators (for now): CPPMAGIC_MAP applies another macro to each of its remaining arguments - the results are comma separated, so they can be passed into another CPPMAGIC_MAP invocation. CPPMAGIC_2MAP does the same thing, but takes the arguments a pair at a time, using a supplied two-argument macro. CPPMAGIC_JOIN combines the arguments with a chosen delimiter (effectively replacing the commas between the arguments with the delimiter) same thing, but takes the arguments a pair at a time. Signed-off-by: David Gibson --- diff --git a/ccan/cppmagic/cppmagic.h b/ccan/cppmagic/cppmagic.h index 8acbba71..f1f6868e 100644 --- a/ccan/cppmagic/cppmagic.h +++ b/ccan/cppmagic/cppmagic.h @@ -133,4 +133,59 @@ #define CPPMAGIC_DEFER1(a_) a_ CPPMAGIC_NOTHING() #define CPPMAGIC_DEFER2(a_) a_ CPPMAGIC_NOTHING CPPMAGIC_NOTHING()() +/** + * CPPMAGIC_MAP - iterate another macro across arguments + * @m: name of a one argument macro + * + * CPPMAGIC_MAP(@m, @a1, @a2, ... @an) + * expands to the expansion of @m(@a1) , @m(@a2) , ... , @m(@an) + */ +#define _CPPMAGIC_MAP_() _CPPMAGIC_MAP +#define _CPPMAGIC_MAP(m_, a_, ...) \ + m_(a_) \ + CPPMAGIC_IFELSE(CPPMAGIC_NONEMPTY(__VA_ARGS__)) \ + (, CPPMAGIC_DEFER2(_CPPMAGIC_MAP_)()(m_, __VA_ARGS__)) \ + () +#define CPPMAGIC_MAP(m_, ...) \ + CPPMAGIC_IFELSE(CPPMAGIC_NONEMPTY(__VA_ARGS__)) \ + (CPPMAGIC_EVAL(_CPPMAGIC_MAP(m_, __VA_ARGS__))) \ + () + +/** + * CPPMAGIC_2MAP - iterate another macro across pairs of arguments + * @m: name of a two argument macro + * + * CPPMAGIC_2MAP(@m, @a1, @b1, @a2, @b2, ..., @an, @bn) + * expands to the expansion of + * @m(@a1, @b1) , @m(@a2, @b2) , ... , @m(@an, @bn) + */ +#define _CPPMAGIC_2MAP_() _CPPMAGIC_2MAP +#define _CPPMAGIC_2MAP(m_, a_, b_, ...) \ + m_(a_, b_) \ + CPPMAGIC_IFELSE(CPPMAGIC_NONEMPTY(__VA_ARGS__)) \ + (, CPPMAGIC_DEFER2(_CPPMAGIC_2MAP_)()(m_, __VA_ARGS__)) \ + () +#define CPPMAGIC_2MAP(m_, ...) \ + CPPMAGIC_IFELSE(CPPMAGIC_NONEMPTY(__VA_ARGS__)) \ + (CPPMAGIC_EVAL(_CPPMAGIC_2MAP(m_, __VA_ARGS__))) \ + () + +/** + * CPPMAGIC_JOIN - separate arguments with given delimiter + * @d: delimiter + * + * CPPMAGIC_JOIN(@d, @a1, @a2, ..., @an) + * expands to the expansion of @a1 @d @a2 @d ... @d @an + */ +#define _CPPMAGIC_JOIN_() _CPPMAGIC_JOIN +#define _CPPMAGIC_JOIN(d_, a_, ...) \ + a_ \ + CPPMAGIC_IFELSE(CPPMAGIC_NONEMPTY(__VA_ARGS__)) \ + (d_ CPPMAGIC_DEFER2(_CPPMAGIC_JOIN_)()(d_, __VA_ARGS__)) \ + () +#define CPPMAGIC_JOIN(d_, ...) \ + CPPMAGIC_IFELSE(CPPMAGIC_NONEMPTY(__VA_ARGS__)) \ + (CPPMAGIC_EVAL(_CPPMAGIC_JOIN(d_, __VA_ARGS__))) \ + () + #endif /* CCAN_CPPMAGIC_H */ diff --git a/ccan/cppmagic/test/run.c b/ccan/cppmagic/test/run.c index 2aa95303..ee02c6c8 100644 --- a/ccan/cppmagic/test/run.c +++ b/ccan/cppmagic/test/run.c @@ -18,9 +18,16 @@ static inline void check1(const char *orig, const char *expand, #define TESTRECURSE() R CPPMAGIC_DEFER1(_TESTRECURSE)()() #define _TESTRECURSE() TESTRECURSE +#define TESTMAP1(x) <> + +#define TESTMAP2(x) [[ x +#define TESTMAP3(x) x ]] + +#define TEST2MAP(x, y) x ** y + int main(void) { - plan_tests(27); + plan_tests(42); CHECK1(CPPMAGIC_NOTHING(), ""); CHECK1(CPPMAGIC_GLUE2(a, b), "ab"); @@ -58,6 +65,28 @@ int main(void) CHECK1(CPPMAGIC_EVAL1(TESTRECURSE()), "R R R _TESTRECURSE ()()"); CHECK1(CPPMAGIC_EVAL2(TESTRECURSE()), "R R R R R _TESTRECURSE ()()"); + CHECK1(CPPMAGIC_MAP(TESTMAP1), ""); + CHECK1(CPPMAGIC_MAP(TESTMAP1, a), "<>"); + CHECK1(CPPMAGIC_MAP(TESTMAP1, a, b), "<> , <>"); + CHECK1(CPPMAGIC_MAP(TESTMAP1, a, b, c), "<> , <> , <>"); + + CHECK1(CPPMAGIC_2MAP(TEST2MAP), ""); + CHECK1(CPPMAGIC_2MAP(TEST2MAP, a, 1), "a ** 1"); + CHECK1(CPPMAGIC_2MAP(TEST2MAP, a, 1, b, 2), "a ** 1 , b ** 2"); + + CHECK1(CPPMAGIC_JOIN(;), ""); + CHECK1(CPPMAGIC_JOIN(;, a), "a"); + CHECK1(CPPMAGIC_JOIN(;, a, b), "a ; b"); + CHECK1(CPPMAGIC_JOIN(;, a, b, c), "a ; b ; c"); + + /* Check chaining of MAPs */ + CHECK1(CPPMAGIC_MAP(TESTMAP2, CPPMAGIC_MAP(TESTMAP3)), ""); + CHECK1(CPPMAGIC_MAP(TESTMAP2, CPPMAGIC_MAP(TESTMAP3, a)), "[[ a ]]"); + CHECK1(CPPMAGIC_MAP(TESTMAP2, CPPMAGIC_MAP(TESTMAP3, a, b)), + "[[ a ]] , [[ b ]]"); + CHECK1(CPPMAGIC_MAP(TESTMAP2, CPPMAGIC_MAP(TESTMAP3, a, b, c)), + "[[ a ]] , [[ b ]] , [[ c ]]"); + /* This exits depending on whether all tests passed */ return exit_status(); }