cppmagic: Iteration
authorDavid Gibson <david@gibson.dropbear.id.au>
Tue, 26 Jan 2016 10:54:47 +0000 (21:54 +1100)
committerDavid Gibson <david@gibson.dropbear.id.au>
Wed, 3 Feb 2016 22:44:13 +0000 (09:44 +1100)
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 <david@gibson.dropbear.id.au>
ccan/cppmagic/cppmagic.h
ccan/cppmagic/test/run.c

index 8acbba71a68d4e97c849831f7b5abd55bcc4f1fe..f1f6868e550dd2377d8829fec8a62d88bc842d1d 100644 (file)
 #define CPPMAGIC_DEFER1(a_)    a_ CPPMAGIC_NOTHING()
 #define CPPMAGIC_DEFER2(a_)    a_ CPPMAGIC_NOTHING CPPMAGIC_NOTHING()()
 
 #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 */
 #endif /* CCAN_CPPMAGIC_H */
index 2aa95303ed52d21a14f3a536cc580e7d7050bf07..ee02c6c8d01f2ba1715f2d501a68335698cbb4a4 100644 (file)
@@ -18,9 +18,16 @@ static inline void check1(const char *orig, const char *expand,
 #define TESTRECURSE()  R CPPMAGIC_DEFER1(_TESTRECURSE)()()
 #define _TESTRECURSE() TESTRECURSE
 
 #define TESTRECURSE()  R CPPMAGIC_DEFER1(_TESTRECURSE)()()
 #define _TESTRECURSE() TESTRECURSE
 
+#define TESTMAP1(x)    <<x>>
+
+#define TESTMAP2(x)            [[ x
+#define TESTMAP3(x)            x ]]
+
+#define TEST2MAP(x, y) x ** y
+
 int main(void)
 {
 int main(void)
 {
-       plan_tests(27);
+       plan_tests(42);
 
        CHECK1(CPPMAGIC_NOTHING(), "");
        CHECK1(CPPMAGIC_GLUE2(a, b), "ab");
 
        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_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), "<<a>>");
+       CHECK1(CPPMAGIC_MAP(TESTMAP1, a, b), "<<a>> , <<b>>");
+       CHECK1(CPPMAGIC_MAP(TESTMAP1, a, b, c), "<<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();
 }
        /* This exits depending on whether all tests passed */
        return exit_status();
 }