]> git.ozlabs.org Git - ccan/blobdiff - ccan/foreach/foreach.h
foreach: new module
[ccan] / ccan / foreach / foreach.h
diff --git a/ccan/foreach/foreach.h b/ccan/foreach/foreach.h
new file mode 100644 (file)
index 0000000..1647189
--- /dev/null
@@ -0,0 +1,107 @@
+#ifndef CCAN_FOREACH_H
+#define CCAN_FOREACH_H
+#include "config.h"
+#include <stddef.h>
+#include <assert.h>
+#include <stdbool.h>
+
+#if HAVE_COMPOUND_LITERALS
+#if HAVE_FOR_LOOP_DECLARATION
+/**
+ * foreach_int - iterate over a fixed series of integers
+ * @i: the int-compatible iteration variable
+ * @val: one or more integer-compatible values
+ *
+ * This is a convenient wrapper function for setting a variable to one or
+ * more explicit values in turn.  continue and break work as expected.
+ *
+ * Example:
+ *     int i;
+ *     foreach_int(i, 0, -1, 100, 0, -99) {
+ *             printf("i is %i\n", i);
+ *     }
+ */
+#define foreach_int(i, val, ...)                                       \
+       for (unsigned _foreach_i = ((i) = val, 0);                      \
+            _foreach_i < sizeof((int[]) { val, __VA_ARGS__ })/sizeof(val); \
+            (i) = (int[]) { val, __VA_ARGS__, 0 }[++_foreach_i])
+
+/**
+ * foreach_ptr - iterate over a non-NULL series of pointers
+ * @i: the pointer iteration variable
+ * @val: one or more compatible pointer values
+ *
+ * This is a convenient wrapper function for setting a variable to one
+ * or more explicit values in turn.  None of the values can be NULL;
+ * that is the termination condition (ie. @i will be NULL on
+ * completion).  continue and break work as expected.
+ *
+ * Example:
+ *     const char *p;
+ *     foreach_ptr(p, "Hello", "world") {
+ *             printf("p is %s\n", p);
+ *     }
+ */
+#define foreach_ptr(i, val, ...)                                       \
+       for (unsigned _foreach_i = (unsigned long)((i) = (val), 0);     \
+            (i);                                                       \
+            (i) = ((FOREACH_TYPEOF(val)[]){(val), __VA_ARGS__, NULL})  \
+                    [++_foreach_i])                                    \
+               _foreach_no_nullval(_foreach_i, i,                      \
+                                   ((void *[]){ val, __VA_ARGS__})))
+#else /* !HAVE_FOR_LOOP_DECLARATION */
+/* GCC in C89 mode still has compound literals, but no for-declarations */
+#define foreach_int(i, val, ...)                                       \
+       for ((i) = (val), _foreach_iter_init(&(i));                     \
+            _foreach_iter(&(i)) < sizeof((int[]) { (val), __VA_ARGS__ }) \
+                          / sizeof(int);                               \
+            (i) = (int[]) { (val), __VA_ARGS__, 0 }[_foreach_iter_inc(&(i))])
+
+#define foreach_ptr(i, val, ...)                                       \
+       for ((i) = (val), _foreach_iter_init(&(i));                     \
+            (i);                                                       \
+            (i) = ((FOREACH_TYPEOF(val)[]){ (val), __VA_ARGS__, 0 })   \
+                    [_foreach_iter_inc(&(i))],                         \
+                _foreach_no_nullval(_foreach_iter(&(i)), i,            \
+                                    ((void *[]){ val, __VA_ARGS__})))
+
+void _foreach_iter_init(const void *i);
+unsigned int _foreach_iter(const void *i);
+unsigned int _foreach_iter_inc(const void *i);
+
+#endif /* !HAVE_FOR_LOOP_DECLARATION */
+
+/* Make sure they don't put NULL values into array! */
+#define _foreach_no_nullval(i, p, arr)                         \
+       assert((i) >= sizeof(arr)/sizeof(arr[0]) || (p))
+
+#if HAVE_TYPEOF
+#define FOREACH_TYPEOF(val) __typeof__(&*(val))
+#else
+#define FOREACH_TYPEOF(val) void *
+#endif
+
+#else /* !HAVE_COMPOUND_LITERALS */
+
+/* No compound literals, but it's still (just) possible. */
+#define foreach_int(i, val, ...)                                       \
+       for (i = _foreach_intval_init(&(i), val, __VA_ARGS__,   \
+                                         _foreach_term);               \
+            !_foreach_intval_done(&i);                         \
+            i = _foreach_intval_next(&(i), val, __VA_ARGS__,   \
+                                         _foreach_term))
+
+#define foreach_ptr(i, val, ...)                                       \
+       for (i = _foreach_ptrval_init(&(i), val, __VA_ARGS__, NULL);    \
+            (i);                                                       \
+            i = _foreach_ptrval_next(&(i), val, __VA_ARGS__, NULL))
+
+extern int _foreach_term;
+int _foreach_intval_init(const void *i, int val, ...);
+bool _foreach_intval_done(const void *i);
+int _foreach_intval_next(const void *i, int val, ...);
+void *_foreach_ptrval_init(const void *i, const void *val, ...);
+void *_foreach_ptrval_next(const void *i, const void *val, ...);
+#endif /* !HAVE_COMPOUND_LITERALS */
+
+#endif /* CCAN_FOREACH_H */