[ccan] [PATCH 5/5] cppmagic: Iteration

David Gibson david at gibson.dropbear.id.au
Wed Jan 27 22:40:01 AEDT 2016


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 at gibson.dropbear.id.au>
---
 ccan/cppmagic/cppmagic.h | 55 ++++++++++++++++++++++++++++++++++++++++++++++++
 ccan/cppmagic/test/run.c | 31 ++++++++++++++++++++++++++-
 2 files changed, 85 insertions(+), 1 deletion(-)

diff --git a/ccan/cppmagic/cppmagic.h b/ccan/cppmagic/cppmagic.h
index 8acbba7..f1f6868 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 2aa9530..ee02c6c 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)	<<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), "<<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();
 }
-- 
2.5.0



More information about the ccan mailing list