All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH 0/5] cppmagic: Preprocessor tricks
@ 2016-01-27 11:39 David Gibson
  2016-01-27 11:39 ` [PATCH 1/5] cppmagic: New module David Gibson
                   ` (5 more replies)
  0 siblings, 6 replies; 9+ messages in thread
From: David Gibson @ 2016-01-27 11:39 UTC (permalink / raw)
  To: ccan, rusty

ccan already has cpp macros used in some interesting ways.  This
module is far more interesting.. or possibly horrifying.

It includes some very fancy preprocessor techniques described in the
article at:
    http://jhnet.co.uk/articles/cpp_magic

This includes preprocessing time evaluated conditionals, (sort of)
recursion and iteration across the parameters of varargs macros.

David Gibson (5):
  cppmagic: New module
  cppmagic: Logical operations
  cppmagic: Conditionals
  cppmagic: Allow multiple and deferred evaluation
  cppmagic: Iteration

 ccan/cppmagic/LICENSE    |   1 +
 ccan/cppmagic/_info      |  30 ++++++++
 ccan/cppmagic/cppmagic.h | 191 +++++++++++++++++++++++++++++++++++++++++++++++
 ccan/cppmagic/test/run.c |  92 +++++++++++++++++++++++
 4 files changed, 314 insertions(+)
 create mode 120000 ccan/cppmagic/LICENSE
 create mode 100644 ccan/cppmagic/_info
 create mode 100644 ccan/cppmagic/cppmagic.h
 create mode 100644 ccan/cppmagic/test/run.c

-- 
2.5.0

_______________________________________________
ccan mailing list
ccan@lists.ozlabs.org
https://lists.ozlabs.org/listinfo/ccan

^ permalink raw reply	[flat|nested] 9+ messages in thread

* [PATCH 1/5] cppmagic: New module
  2016-01-27 11:39 [PATCH 0/5] cppmagic: Preprocessor tricks David Gibson
@ 2016-01-27 11:39 ` David Gibson
  2016-01-27 11:39 ` [PATCH 2/5] cppmagic: Logical operations David Gibson
                   ` (4 subsequent siblings)
  5 siblings, 0 replies; 9+ messages in thread
From: David Gibson @ 2016-01-27 11:39 UTC (permalink / raw)
  To: ccan, rusty

A module for some of the awesome / horrifying techniques described at:
    http://jhnet.co.uk/articles/cpp_magic
    https://github.com/pfultz2/Cloak/wiki/C-Preprocessor-tricks,-tips,-and-idioms

Start off with just some simple things.

Signed-off-by: David Gibson <david@gibson.dropbear.id.au>
---
 ccan/cppmagic/LICENSE    |  1 +
 ccan/cppmagic/_info      | 30 ++++++++++++++++++++++++++++++
 ccan/cppmagic/cppmagic.h | 42 ++++++++++++++++++++++++++++++++++++++++++
 ccan/cppmagic/test/run.c | 34 ++++++++++++++++++++++++++++++++++
 4 files changed, 107 insertions(+)
 create mode 120000 ccan/cppmagic/LICENSE
 create mode 100644 ccan/cppmagic/_info
 create mode 100644 ccan/cppmagic/cppmagic.h
 create mode 100644 ccan/cppmagic/test/run.c

diff --git a/ccan/cppmagic/LICENSE b/ccan/cppmagic/LICENSE
new file mode 120000
index 0000000..2354d12
--- /dev/null
+++ b/ccan/cppmagic/LICENSE
@@ -0,0 +1 @@
+../../licenses/BSD-MIT
\ No newline at end of file
diff --git a/ccan/cppmagic/_info b/ccan/cppmagic/_info
new file mode 100644
index 0000000..aad394b
--- /dev/null
+++ b/ccan/cppmagic/_info
@@ -0,0 +1,30 @@
+#include "config.h"
+#include <stdio.h>
+#include <string.h>
+
+/**
+ * cppmagic - Abuse of the C preprocessor
+ *
+ * This contains a bunch of fancy macro techniques such as
+ * preprocessor-time evaluated conditionals and (quasi) recursion and
+ * iteration.
+ *
+ * It's based on these articles:
+ *  - http://jhnet.co.uk/articles/cpp_magic
+ *  - https://github.com/pfultz2/Cloak/wiki/C-Preprocessor-tricks,-tips,-and-idioms
+ * and code from the Boost C++ library.
+ *
+ * License: BSD-MIT
+ */
+int main(int argc, char *argv[])
+{
+	/* Expect exactly one argument */
+	if (argc != 2)
+		return 1;
+
+	if (strcmp(argv[1], "depends") == 0) {
+		return 0;
+	}
+
+	return 1;
+}
diff --git a/ccan/cppmagic/cppmagic.h b/ccan/cppmagic/cppmagic.h
new file mode 100644
index 0000000..f29e0ec
--- /dev/null
+++ b/ccan/cppmagic/cppmagic.h
@@ -0,0 +1,42 @@
+/* MIT (BSD) license - see LICENSE file for details */
+#ifndef CCAN_CPPMAGIC_H
+#define CCAN_CPPMAGIC_H
+
+/**
+ * CPPMAGIC_NOTHING - expands to nothing
+ */
+#define CPPMAGIC_NOTHING()
+
+/**
+ * CPPMAGIC_STRINGIFY - convert arguments to a string literal
+ */
+#define _CPPMAGIC_STRINGIFY(...)	#__VA_ARGS__
+#define CPPMAGIC_STRINGIFY(...)		_CPPMAGIC_STRINGIFY(__VA_ARGS__)
+
+/**
+ * CPPMAGIC_GLUE2 - glue arguments together
+ *
+ * CPPMAGIC_GLUE2(@a_, @b_)
+ *	expands to the expansion of @a_ followed immediately
+ *	(combining tokens) by the expansion of @b_
+ */
+#define _CPPMAGIC_GLUE2(a_, b_)		a_##b_
+#define CPPMAGIC_GLUE2(a_, b_)		_CPPMAGIC_GLUE2(a_, b_)
+
+/**
+ * CPPMAGIC_1ST - return 1st argument
+ *
+ * CPPMAGIC_1ST(@a_, ...)
+ *	expands to the expansion of @a_
+ */
+#define CPPMAGIC_1ST(a_, ...)		a_
+
+/**
+ * CPPMAGIC_2ND - return 2nd argument
+ *
+ * CPPMAGIC_2ST(@a_, @b_, ...)
+ *	expands to the expansion of @b_
+ */
+#define CPPMAGIC_2ND(a_, b_, ...)	b_
+
+#endif /* CCAN_CPPMAGIC_H */
diff --git a/ccan/cppmagic/test/run.c b/ccan/cppmagic/test/run.c
new file mode 100644
index 0000000..0ed830d
--- /dev/null
+++ b/ccan/cppmagic/test/run.c
@@ -0,0 +1,34 @@
+#include "config.h"
+
+#include <string.h>
+
+#include <ccan/cppmagic/cppmagic.h>
+#include <ccan/tap/tap.h>
+
+static inline void check1(const char *orig, const char *expand,
+			  const char *match)
+{
+	ok(strcmp(expand, match) == 0,
+	   "%s => %s : %s", orig, expand, match);
+}
+
+#define CHECK1(orig, match) \
+	check1(#orig, CPPMAGIC_STRINGIFY(orig), match)
+
+int main(void)
+{
+	plan_tests(7);
+
+	CHECK1(CPPMAGIC_NOTHING(), "");
+	CHECK1(CPPMAGIC_GLUE2(a, b), "ab");
+
+	CHECK1(CPPMAGIC_1ST(a), "a");
+	CHECK1(CPPMAGIC_1ST(a, b), "a");
+	CHECK1(CPPMAGIC_1ST(a, b, c), "a");
+
+	CHECK1(CPPMAGIC_2ND(a, b), "b");
+	CHECK1(CPPMAGIC_2ND(a, b, c), "b");
+
+	/* This exits depending on whether all tests passed */
+	return exit_status();
+}
-- 
2.5.0

_______________________________________________
ccan mailing list
ccan@lists.ozlabs.org
https://lists.ozlabs.org/listinfo/ccan

^ permalink raw reply related	[flat|nested] 9+ messages in thread

* [PATCH 2/5] cppmagic: Logical operations
  2016-01-27 11:39 [PATCH 0/5] cppmagic: Preprocessor tricks David Gibson
  2016-01-27 11:39 ` [PATCH 1/5] cppmagic: New module David Gibson
@ 2016-01-27 11:39 ` David Gibson
  2016-01-27 11:39 ` [PATCH 3/5] cppmagic: Conditionals David Gibson
                   ` (3 subsequent siblings)
  5 siblings, 0 replies; 9+ messages in thread
From: David Gibson @ 2016-01-27 11:39 UTC (permalink / raw)
  To: ccan, rusty

In order to implement fancier things, we need to represent truth values in
cpp.  We use '0' and '1' strings, like in C, but we need ways to get these
values from other conditions.

CPPMAGIC_ISZERO() and CPPMAGIC_NONZERO() test if the argument is '0' or
anything else (ISZERO doubles as a logical not).

CPPMAGIC_ISEMPTY() and CPPMAGIC_NON_EMPTY() expand to 0 or 1 depending on
whether they have any arguments at all or not.

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

diff --git a/ccan/cppmagic/cppmagic.h b/ccan/cppmagic/cppmagic.h
index f29e0ec..c4454e7 100644
--- a/ccan/cppmagic/cppmagic.h
+++ b/ccan/cppmagic/cppmagic.h
@@ -39,4 +39,49 @@
  */
 #define CPPMAGIC_2ND(a_, b_, ...)	b_
 
+/**
+ * CPPMAGIC_ISZERO - is argument '0'
+ *
+ * CPPMAGIC_ISZERO(@a)
+ *	expands to '1' if @a is '0', otherwise expands to '0'.
+ */
+#define _CPPMAGIC_ISPROBE(...)		CPPMAGIC_2ND(__VA_ARGS__, 0)
+#define _CPPMAGIC_PROBE()		$, 1
+#define _CPPMAGIC_ISZERO_0		_CPPMAGIC_PROBE()
+#define CPPMAGIC_ISZERO(a_)		\
+	_CPPMAGIC_ISPROBE(CPPMAGIC_GLUE2(_CPPMAGIC_ISZERO_, a_))
+
+/**
+ * CPPMAGIC_NONZERO - is argument not '0'
+ *
+ * CPPMAGIC_NONZERO(@a)
+ *	expands to '0' if @a is '0', otherwise expands to '1'.
+ */
+#define CPPMAGIC_NONZERO(a_)		CPPMAGIC_ISZERO(CPPMAGIC_ISZERO(a_))
+
+/**
+ * CPPMAGIC_NONEMPTY - does the macro have any arguments?
+ *
+ * CPPMAGIC_NONEMPTY()
+ * 	expands to '0'
+ * CPPMAGIC_NONEMPTY(@a)
+ * CPPMAGIC_NONEMPTY(@a, ...)
+ * 	expand to '1'
+ */
+#define _CPPMAGIC_EOA()			0
+#define CPPMAGIC_NONEMPTY(...)		\
+	CPPMAGIC_NONZERO(CPPMAGIC_1ST(_CPPMAGIC_EOA __VA_ARGS__)())
+
+/**
+ * CPPMAGIC_ISEMPTY - does the macro have no arguments?
+ *
+ * CPPMAGIC_ISEMPTY()
+ * 	expands to '1'
+ * CPPMAGIC_ISEMPTY(@a)
+ * CPPMAGIC_ISEMPTY(@a, ...)
+ * 	expand to '0'
+ */
+#define CPPMAGIC_ISEMPTY(...)		\
+	CPPMAGIC_ISZERO(CPPMAGIC_NONEMPTY(__VA_ARGS__))
+
 #endif /* CCAN_CPPMAGIC_H */
diff --git a/ccan/cppmagic/test/run.c b/ccan/cppmagic/test/run.c
index 0ed830d..fb2bfb9 100644
--- a/ccan/cppmagic/test/run.c
+++ b/ccan/cppmagic/test/run.c
@@ -17,7 +17,7 @@ static inline void check1(const char *orig, const char *expand,
 
 int main(void)
 {
-	plan_tests(7);
+	plan_tests(21);
 
 	CHECK1(CPPMAGIC_NOTHING(), "");
 	CHECK1(CPPMAGIC_GLUE2(a, b), "ab");
@@ -29,6 +29,24 @@ int main(void)
 	CHECK1(CPPMAGIC_2ND(a, b), "b");
 	CHECK1(CPPMAGIC_2ND(a, b, c), "b");
 
+	CHECK1(CPPMAGIC_ISZERO(0), "1");
+	CHECK1(CPPMAGIC_ISZERO(1), "0");
+	CHECK1(CPPMAGIC_ISZERO(123), "0");
+	CHECK1(CPPMAGIC_ISZERO(abc), "0");
+
+	CHECK1(CPPMAGIC_NONZERO(0), "0");
+	CHECK1(CPPMAGIC_NONZERO(1), "1");
+	CHECK1(CPPMAGIC_NONZERO(123), "1");
+	CHECK1(CPPMAGIC_NONZERO(abc), "1");
+
+	CHECK1(CPPMAGIC_NONEMPTY(), "0");
+	CHECK1(CPPMAGIC_NONEMPTY(0), "1");
+	CHECK1(CPPMAGIC_NONEMPTY(a, b, c), "1");
+
+	CHECK1(CPPMAGIC_ISEMPTY(), "1");
+	CHECK1(CPPMAGIC_ISEMPTY(0), "0");
+	CHECK1(CPPMAGIC_ISEMPTY(a, b, c), "0");
+	
 	/* This exits depending on whether all tests passed */
 	return exit_status();
 }
-- 
2.5.0

_______________________________________________
ccan mailing list
ccan@lists.ozlabs.org
https://lists.ozlabs.org/listinfo/ccan

^ permalink raw reply related	[flat|nested] 9+ messages in thread

* [PATCH 3/5] cppmagic: Conditionals
  2016-01-27 11:39 [PATCH 0/5] cppmagic: Preprocessor tricks David Gibson
  2016-01-27 11:39 ` [PATCH 1/5] cppmagic: New module David Gibson
  2016-01-27 11:39 ` [PATCH 2/5] cppmagic: Logical operations David Gibson
@ 2016-01-27 11:39 ` David Gibson
  2016-01-27 11:40 ` [PATCH 4/5] cppmagic: Allow multiple and deferred evaluation David Gibson
                   ` (2 subsequent siblings)
  5 siblings, 0 replies; 9+ messages in thread
From: David Gibson @ 2016-01-27 11:39 UTC (permalink / raw)
  To: ccan, rusty

Implement CPPMAGIC_IFELSE which operates similar to the C ? : operator, but
is evaluated at preprocessing time.

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

diff --git a/ccan/cppmagic/cppmagic.h b/ccan/cppmagic/cppmagic.h
index c4454e7..b5c3578 100644
--- a/ccan/cppmagic/cppmagic.h
+++ b/ccan/cppmagic/cppmagic.h
@@ -84,4 +84,18 @@
 #define CPPMAGIC_ISEMPTY(...)		\
 	CPPMAGIC_ISZERO(CPPMAGIC_NONEMPTY(__VA_ARGS__))
 
+/*
+ * CPPMAGIC_IFELSE - preprocessor conditional
+ *
+ * CPPMAGIC_IFELSE(@cond)(@if)(@else)
+ *	expands to @else if @cond is '0', otherwise expands to @if
+ */
+#define _CPPMAGIC_IF_0(...)		_CPPMAGIC_IF_0_ELSE
+#define _CPPMAGIC_IF_1(...)		__VA_ARGS__ _CPPMAGIC_IF_1_ELSE
+#define _CPPMAGIC_IF_0_ELSE(...)	__VA_ARGS__
+#define _CPPMAGIC_IF_1_ELSE(...)
+#define _CPPMAGIC_IFELSE(cond_)		CPPMAGIC_GLUE2(_CPPMAGIC_IF_, cond_)
+#define CPPMAGIC_IFELSE(cond_)		\
+	_CPPMAGIC_IFELSE(CPPMAGIC_NONZERO(cond_))
+
 #endif /* CCAN_CPPMAGIC_H */
diff --git a/ccan/cppmagic/test/run.c b/ccan/cppmagic/test/run.c
index fb2bfb9..0f8917d 100644
--- a/ccan/cppmagic/test/run.c
+++ b/ccan/cppmagic/test/run.c
@@ -17,7 +17,7 @@ static inline void check1(const char *orig, const char *expand,
 
 int main(void)
 {
-	plan_tests(21);
+	plan_tests(24);
 
 	CHECK1(CPPMAGIC_NOTHING(), "");
 	CHECK1(CPPMAGIC_GLUE2(a, b), "ab");
@@ -47,6 +47,10 @@ int main(void)
 	CHECK1(CPPMAGIC_ISEMPTY(0), "0");
 	CHECK1(CPPMAGIC_ISEMPTY(a, b, c), "0");
 	
+	CHECK1(CPPMAGIC_IFELSE(0)(abc)(def), "def");
+	CHECK1(CPPMAGIC_IFELSE(1)(abc)(def), "abc");
+	CHECK1(CPPMAGIC_IFELSE(not zero)(abc)(def), "abc");
+
 	/* This exits depending on whether all tests passed */
 	return exit_status();
 }
-- 
2.5.0

_______________________________________________
ccan mailing list
ccan@lists.ozlabs.org
https://lists.ozlabs.org/listinfo/ccan

^ permalink raw reply related	[flat|nested] 9+ messages in thread

* [PATCH 4/5] cppmagic: Allow multiple and deferred evaluation
  2016-01-27 11:39 [PATCH 0/5] cppmagic: Preprocessor tricks David Gibson
                   ` (2 preceding siblings ...)
  2016-01-27 11:39 ` [PATCH 3/5] cppmagic: Conditionals David Gibson
@ 2016-01-27 11:40 ` David Gibson
  2016-01-27 11:40 ` [PATCH 5/5] cppmagic: Iteration David Gibson
  2016-01-30 12:51 ` [PATCH 0/5] cppmagic: Preprocessor tricks David Gibson
  5 siblings, 0 replies; 9+ messages in thread
From: David Gibson @ 2016-01-27 11:40 UTC (permalink / raw)
  To: ccan, rusty

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@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

_______________________________________________
ccan mailing list
ccan@lists.ozlabs.org
https://lists.ozlabs.org/listinfo/ccan

^ permalink raw reply related	[flat|nested] 9+ messages in thread

* [PATCH 5/5] cppmagic: Iteration
  2016-01-27 11:39 [PATCH 0/5] cppmagic: Preprocessor tricks David Gibson
                   ` (3 preceding siblings ...)
  2016-01-27 11:40 ` [PATCH 4/5] cppmagic: Allow multiple and deferred evaluation David Gibson
@ 2016-01-27 11:40 ` David Gibson
  2016-01-30 12:51 ` [PATCH 0/5] cppmagic: Preprocessor tricks David Gibson
  5 siblings, 0 replies; 9+ messages in thread
From: David Gibson @ 2016-01-27 11:40 UTC (permalink / raw)
  To: ccan, rusty

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 | 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

_______________________________________________
ccan mailing list
ccan@lists.ozlabs.org
https://lists.ozlabs.org/listinfo/ccan

^ permalink raw reply related	[flat|nested] 9+ messages in thread

* Re: [PATCH 0/5] cppmagic: Preprocessor tricks
  2016-01-27 11:39 [PATCH 0/5] cppmagic: Preprocessor tricks David Gibson
                   ` (4 preceding siblings ...)
  2016-01-27 11:40 ` [PATCH 5/5] cppmagic: Iteration David Gibson
@ 2016-01-30 12:51 ` David Gibson
  2016-02-01  1:51   ` Rusty Russell
  5 siblings, 1 reply; 9+ messages in thread
From: David Gibson @ 2016-01-30 12:51 UTC (permalink / raw)
  To: ccan, rusty


[-- Attachment #1.1: Type: text/plain, Size: 771 bytes --]

On Wed, Jan 27, 2016 at 10:39:56PM +1100, David Gibson wrote:
> ccan already has cpp macros used in some interesting ways.  This
> module is far more interesting.. or possibly horrifying.
> 
> It includes some very fancy preprocessor techniques described in the
> article at:
>     http://jhnet.co.uk/articles/cpp_magic
> 
> This includes preprocessing time evaluated conditionals, (sort of)
> recursion and iteration across the parameters of varargs macros.

No comments so far.  Unless there's an objection in the next couple of
days, I'll commit this myself.

-- 
David Gibson			| I'll have my music baroque, and my code
david AT gibson.dropbear.id.au	| minimalist, thank you.  NOT _the_ _other_
				| _way_ _around_!
http://www.ozlabs.org/~dgibson

[-- Attachment #1.2: signature.asc --]
[-- Type: application/pgp-signature, Size: 819 bytes --]

[-- Attachment #2: Type: text/plain, Size: 127 bytes --]

_______________________________________________
ccan mailing list
ccan@lists.ozlabs.org
https://lists.ozlabs.org/listinfo/ccan

^ permalink raw reply	[flat|nested] 9+ messages in thread

* Re: [PATCH 0/5] cppmagic: Preprocessor tricks
  2016-01-30 12:51 ` [PATCH 0/5] cppmagic: Preprocessor tricks David Gibson
@ 2016-02-01  1:51   ` Rusty Russell
  2016-02-01  3:59     ` David Gibson
  0 siblings, 1 reply; 9+ messages in thread
From: Rusty Russell @ 2016-02-01  1:51 UTC (permalink / raw)
  To: David Gibson, ccan

David Gibson <david@gibson.dropbear.id.au> writes:
> On Wed, Jan 27, 2016 at 10:39:56PM +1100, David Gibson wrote:
>> ccan already has cpp macros used in some interesting ways.  This
>> module is far more interesting.. or possibly horrifying.
>> 
>> It includes some very fancy preprocessor techniques described in the
>> article at:
>>     http://jhnet.co.uk/articles/cpp_magic
>> 
>> This includes preprocessing time evaluated conditionals, (sort of)
>> recursion and iteration across the parameters of varargs macros.
>
> No comments so far.  Unless there's an objection in the next couple of
> days, I'll commit this myself.

We're still reeling, I think!

Commit away!
Rusty.
_______________________________________________
ccan mailing list
ccan@lists.ozlabs.org
https://lists.ozlabs.org/listinfo/ccan

^ permalink raw reply	[flat|nested] 9+ messages in thread

* Re: [PATCH 0/5] cppmagic: Preprocessor tricks
  2016-02-01  1:51   ` Rusty Russell
@ 2016-02-01  3:59     ` David Gibson
  0 siblings, 0 replies; 9+ messages in thread
From: David Gibson @ 2016-02-01  3:59 UTC (permalink / raw)
  To: Rusty Russell; +Cc: ccan


[-- Attachment #1.1: Type: text/plain, Size: 1018 bytes --]

On Mon, Feb 01, 2016 at 12:21:33PM +1030, Paul 'Rusty' Russell wrote:
> David Gibson <david@gibson.dropbear.id.au> writes:
> > On Wed, Jan 27, 2016 at 10:39:56PM +1100, David Gibson wrote:
> >> ccan already has cpp macros used in some interesting ways.  This
> >> module is far more interesting.. or possibly horrifying.
> >> 
> >> It includes some very fancy preprocessor techniques described in the
> >> article at:
> >>     http://jhnet.co.uk/articles/cpp_magic
> >> 
> >> This includes preprocessing time evaluated conditionals, (sort of)
> >> recursion and iteration across the parameters of varargs macros.
> >
> > No comments so far.  Unless there's an objection in the next couple of
> > days, I'll commit this myself.
> 
> We're still reeling, I think!

Heh heh heh.

> Commit away!

Done.

-- 
David Gibson			| I'll have my music baroque, and my code
david AT gibson.dropbear.id.au	| minimalist, thank you.  NOT _the_ _other_
				| _way_ _around_!
http://www.ozlabs.org/~dgibson

[-- Attachment #1.2: signature.asc --]
[-- Type: application/pgp-signature, Size: 819 bytes --]

[-- Attachment #2: Type: text/plain, Size: 127 bytes --]

_______________________________________________
ccan mailing list
ccan@lists.ozlabs.org
https://lists.ozlabs.org/listinfo/ccan

^ permalink raw reply	[flat|nested] 9+ messages in thread

end of thread, other threads:[~2016-02-01  3:58 UTC | newest]

Thread overview: 9+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2016-01-27 11:39 [PATCH 0/5] cppmagic: Preprocessor tricks David Gibson
2016-01-27 11:39 ` [PATCH 1/5] cppmagic: New module David Gibson
2016-01-27 11:39 ` [PATCH 2/5] cppmagic: Logical operations David Gibson
2016-01-27 11:39 ` [PATCH 3/5] cppmagic: Conditionals David Gibson
2016-01-27 11:40 ` [PATCH 4/5] cppmagic: Allow multiple and deferred evaluation David Gibson
2016-01-27 11:40 ` [PATCH 5/5] cppmagic: Iteration David Gibson
2016-01-30 12:51 ` [PATCH 0/5] cppmagic: Preprocessor tricks David Gibson
2016-02-01  1:51   ` Rusty Russell
2016-02-01  3:59     ` David Gibson

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.