[ccan] [PATCH 4/5] cppmagic: Allow multiple and deferred evaluation

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


Recursion (and therefore iteration) in cpp is difficult, since the
preprocessor explicitly looks for and inhibits recursion.

But, it's possible to trick it, up to a point.  CPPMAGIC_DEFER1() and
CPPMAGIC_DEFER2() can "hide" a macro, preventing it from being expanded
and being noticed as recursion.

Along with that we need to cause extra expansion passes to be executed.
There has to be a finite limit here - true recursion is impossible - but
that number can be made very large pretty easily.  CPPMAGIC_EVAL() multiply
expands its argument(s) - up to 1024 times.

Signed-off-by: David Gibson <david at gibson.dropbear.id.au>
---
 ccan/cppmagic/cppmagic.h | 35 +++++++++++++++++++++++++++++++++++
 ccan/cppmagic/test/run.c |  9 ++++++++-
 2 files changed, 43 insertions(+), 1 deletion(-)

diff --git a/ccan/cppmagic/cppmagic.h b/ccan/cppmagic/cppmagic.h
index b5c3578..8acbba7 100644
--- a/ccan/cppmagic/cppmagic.h
+++ b/ccan/cppmagic/cppmagic.h
@@ -98,4 +98,39 @@
 #define CPPMAGIC_IFELSE(cond_)		\
 	_CPPMAGIC_IFELSE(CPPMAGIC_NONZERO(cond_))
 
+/**
+ * CPPMAGIC_EVAL - force multiple expansion passes
+ *
+ * Forces macros in the arguments to be expanded repeatedly (up to
+ * 1024 times) even when CPP would usually stop expanding.
+ */
+#define CPPMAGIC_EVAL1(...)		__VA_ARGS__
+#define CPPMAGIC_EVAL2(...)		\
+	CPPMAGIC_EVAL1(CPPMAGIC_EVAL1(__VA_ARGS__))
+#define CPPMAGIC_EVAL4(...)		\
+	CPPMAGIC_EVAL2(CPPMAGIC_EVAL2(__VA_ARGS__))
+#define CPPMAGIC_EVAL8(...)		\
+	CPPMAGIC_EVAL4(CPPMAGIC_EVAL4(__VA_ARGS__))
+#define CPPMAGIC_EVAL16(...)		\
+	CPPMAGIC_EVAL8(CPPMAGIC_EVAL8(__VA_ARGS__))
+#define CPPMAGIC_EVAL32(...)		\
+	CPPMAGIC_EVAL16(CPPMAGIC_EVAL16(__VA_ARGS__))
+#define CPPMAGIC_EVAL64(...)		\
+	CPPMAGIC_EVAL32(CPPMAGIC_EVAL32(__VA_ARGS__))
+#define CPPMAGIC_EVAL128(...)		\
+	CPPMAGIC_EVAL64(CPPMAGIC_EVAL64(__VA_ARGS__))
+#define CPPMAGIC_EVAL256(...)		\
+	CPPMAGIC_EVAL128(CPPMAGIC_EVAL128(__VA_ARGS__))
+#define CPPMAGIC_EVAL512(...)		\
+	CPPMAGIC_EVAL256(CPPMAGIC_EVAL256(__VA_ARGS__))
+#define CPPMAGIC_EVAL1024(...)		\
+	CPPMAGIC_EVAL512(CPPMAGIC_EVAL512(__VA_ARGS__))
+#define CPPMAGIC_EVAL(...)		CPPMAGIC_EVAL1024(__VA_ARGS__)
+
+/**
+ * CPPMAGIC_DEFER1, CPPMAGIC_DEFER2 - defer expansion
+ */
+#define CPPMAGIC_DEFER1(a_)	a_ CPPMAGIC_NOTHING()
+#define CPPMAGIC_DEFER2(a_)	a_ CPPMAGIC_NOTHING CPPMAGIC_NOTHING()()
+
 #endif /* CCAN_CPPMAGIC_H */
diff --git a/ccan/cppmagic/test/run.c b/ccan/cppmagic/test/run.c
index 0f8917d..2aa9530 100644
--- a/ccan/cppmagic/test/run.c
+++ b/ccan/cppmagic/test/run.c
@@ -15,9 +15,12 @@ static inline void check1(const char *orig, const char *expand,
 #define CHECK1(orig, match) \
 	check1(#orig, CPPMAGIC_STRINGIFY(orig), match)
 
+#define TESTRECURSE()	R CPPMAGIC_DEFER1(_TESTRECURSE)()()
+#define _TESTRECURSE()	TESTRECURSE
+
 int main(void)
 {
-	plan_tests(24);
+	plan_tests(27);
 
 	CHECK1(CPPMAGIC_NOTHING(), "");
 	CHECK1(CPPMAGIC_GLUE2(a, b), "ab");
@@ -51,6 +54,10 @@ int main(void)
 	CHECK1(CPPMAGIC_IFELSE(1)(abc)(def), "abc");
 	CHECK1(CPPMAGIC_IFELSE(not zero)(abc)(def), "abc");
 
+	CHECK1(TESTRECURSE(), "R R _TESTRECURSE ()()");
+	CHECK1(CPPMAGIC_EVAL1(TESTRECURSE()), "R R R _TESTRECURSE ()()");
+	CHECK1(CPPMAGIC_EVAL2(TESTRECURSE()), "R R R R R _TESTRECURSE ()()");
+
 	/* This exits depending on whether all tests passed */
 	return exit_status();
 }
-- 
2.5.0



More information about the ccan mailing list