summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--libmisc/include/libmisc/macro.h61
-rw-r--r--libmisc/include/libmisc/obj.h27
-rw-r--r--libmisc/tests/test_macro.c80
3 files changed, 109 insertions, 59 deletions
diff --git a/libmisc/include/libmisc/macro.h b/libmisc/include/libmisc/macro.h
index 9ac29b9..d7f6243 100644
--- a/libmisc/include/libmisc/macro.h
+++ b/libmisc/include/libmisc/macro.h
@@ -63,6 +63,7 @@
#define LM_FIRST(a, ...) a
#define LM_SECOND(a, b, ...) b
+
#define LM_EAT(...)
#define LM_EXPAND(...) __VA_ARGS__
@@ -80,57 +81,45 @@
/* tuples */
-#define LM_IS_TUPLE(x) LM_IS_SENTINEL(_LM_IS_TUPLE x)
-#define _LM_IS_TUPLE(...) LM_SENTINEL()
+#define LM_IS_TUPLE(x) LM_IS_SENTINEL(_LM_IS_TUPLE x)
+#define _LM_IS_TUPLE(...) LM_SENTINEL()
/* `tuples` is a sequence of `(tuple1)(tuple2)(tuple3)` */
-#define _LM_TUPLES_COMMA(tuple...) (tuple),
-#define LM_TUPLES_NONEMPTY(tuples) LM_IS_TUPLE(_LM_TUPLES_COMMA tuples)
+#define _LM_TUPLES_COMMA(...) (__VA_ARGS__),
+#define LM_TUPLES_IS_NONEMPTY(tuples) LM_IS_TUPLE(_LM_TUPLES_COMMA tuples)
#define LM_TUPLES_HEAD(tuples) LM_EXPAND(LM_FIRST LM_EAT() (_LM_TUPLES_COMMA tuples))
#define LM_TUPLES_TAIL(tuples) LM_EAT tuples
/* iteration */
-/* BUG: LM_FOREACH_TUPLE maxes out at 1024 tuples. */
-#define LM_FOREACH_TUPLE(tuples, func, ...) \
- _LM_EVAL(_LM_FOREACH_TUPLE(tuples, func, __VA_ARGS__))
-#define _LM_FOREACH_TUPLE(tuples, func, ...) \
- LM_IF(LM_TUPLES_NONEMPTY(tuples))( \
- _LM_DEFER2(func)(__VA_ARGS__ __VA_OPT__(,) LM_EXPAND LM_TUPLES_HEAD(tuples)) \
- _LM_DEFER2(_LM_FOREACH_TUPLE_indirect)()(LM_TUPLES_TAIL(tuples), func, __VA_ARGS__) \
- )()
-#define _LM_FOREACH_TUPLE_indirect() _LM_FOREACH_TUPLE
-
-#define _LM_DEFER2(macro) macro LM_EAT LM_EAT()()
-
-#define _LM_EVAL(...) _LM_EVAL__1024(__VA_ARGS__) /* 1024 iterations aught to be enough for anybody */
-#define _LM_EVAL__1024(...) _LM_EVAL__512(_LM_EVAL__512(__VA_ARGS__))
-#define _LM_EVAL__512(...) _LM_EVAL__256(_LM_EVAL__256(__VA_ARGS__))
-#define _LM_EVAL__256(...) _LM_EVAL__128(_LM_EVAL__128(__VA_ARGS__))
-#define _LM_EVAL__128(...) _LM_EVAL__64(_LM_EVAL__64(__VA_ARGS__))
-#define _LM_EVAL__64(...) _LM_EVAL__32(_LM_EVAL__32(__VA_ARGS__))
-#define _LM_EVAL__32(...) _LM_EVAL__16(_LM_EVAL__16(__VA_ARGS__))
+/* The desire to support a high number of iterations is in competition
+ * with the desire for short compile times. 16 is the lowest
+ * power-of-2 for which the current code compiles. */
+#define _LM_EVAL _LM_EVAL__16
#define _LM_EVAL__16(...) _LM_EVAL__8(_LM_EVAL__8(__VA_ARGS__))
#define _LM_EVAL__8(...) _LM_EVAL__4(_LM_EVAL__4(__VA_ARGS__))
#define _LM_EVAL__4(...) _LM_EVAL__2(_LM_EVAL__2(__VA_ARGS__))
#define _LM_EVAL__2(...) _LM_EVAL__1(_LM_EVAL__1(__VA_ARGS__))
#define _LM_EVAL__1(...) __VA_ARGS__
-/** The same as LM_FOREACH_TUPLE(), but callable from inside of LM_FOREACH_TUPLE(). */
-#define LM_FOREACH_TUPLE2(tuples, func, ...) \
- LM_IF(LM_TUPLES_NONEMPTY(tuples))( \
- _LM_DEFER2(func)(__VA_ARGS__ __VA_OPT__(,) LM_EXPAND LM_TUPLES_HEAD(tuples)) \
- _LM_DEFER2(_LM_FOREACH_TUPLE_indirect)()(LM_TUPLES_TAIL(tuples), func, __VA_ARGS__) \
- )()
-#define LM_FOREACH_TUPLE3(tuples, func, ...) \
- LM_IF(LM_TUPLES_NONEMPTY(tuples))( \
- _LM_DEFER2(func)(__VA_ARGS__ __VA_OPT__(,) LM_EXPAND LM_TUPLES_HEAD(tuples)) \
- _LM_DEFER2(_LM_FOREACH_TUPLE_indirect)()(LM_TUPLES_TAIL(tuples), func, __VA_ARGS__) \
- )()
-#define LM_FOREACH_TUPLE4(tuples, func, ...) \
- LM_IF(LM_TUPLES_NONEMPTY(tuples))( \
+#define _LM_DEFER2(macro) macro LM_EAT LM_EAT()()
+
+/**
+ * LM_FOREACH_TUPLE( (tup1) (tup2) (tup3), func, args...) calls
+ * func(args..., tup...) for each tuple.
+ *
+ * BUG: LM_FOREACH_TUPLE is limited to (16*2)-1=31 tuples.
+ */
+#define LM_FOREACH_TUPLE(tuples, func, ...) \
+ _LM_EVAL(_LM_FOREACH_TUPLE(tuples, func, __VA_ARGS__))
+#define _LM_FOREACH_TUPLE(tuples, func, ...) \
+ LM_IF(LM_TUPLES_IS_NONEMPTY(tuples))( \
_LM_DEFER2(func)(__VA_ARGS__ __VA_OPT__(,) LM_EXPAND LM_TUPLES_HEAD(tuples)) \
_LM_DEFER2(_LM_FOREACH_TUPLE_indirect)()(LM_TUPLES_TAIL(tuples), func, __VA_ARGS__) \
)()
+#define _LM_FOREACH_TUPLE_indirect() _LM_FOREACH_TUPLE
+
+/** The same as LM_FOREACH_TUPLE(), but callable from inside of LM_FOREACH_TUPLE(). */
+#define LM_FOREACH_TUPLE2(...) _LM_DEFER2(_LM_FOREACH_TUPLE_indirect)()(__VA_ARGS__)
#endif /* _LIBMISC_MACRO_H_ */
diff --git a/libmisc/include/libmisc/obj.h b/libmisc/include/libmisc/obj.h
index d30a6f2..6645db7 100644
--- a/libmisc/include/libmisc/obj.h
+++ b/libmisc/include/libmisc/obj.h
@@ -56,17 +56,8 @@
#define _LO_IFACE_VTABLE_lo_nest(_ARG_child_iface_name) const struct _lo_##_ARG_child_iface_name##_vtable *_lo_##_ARG_child_iface_name##_vtable; LM_FOREACH_TUPLE2(_ARG_child_iface_name##_LO_IFACE, _LO_IFACE_VTABLE2)
#define _LO_IFACE_VTABLE_lo_func(_ARG_ret_type, _ARG_func_name, ...) _ARG_ret_type (*_ARG_func_name)(void * __VA_OPT__(,) __VA_ARGS__);
-#define _LO_IFACE_VTABLE2(_tuple_typ, ...) _LO_IFACE_VTABLE2_##_tuple_typ(__VA_ARGS__)
-#define _LO_IFACE_VTABLE2_lo_nest(_ARG_child_iface_name) const struct _lo_##_ARG_child_iface_name##_vtable *_lo_##_ARG_child_iface_name##_vtable; LM_FOREACH_TUPLE3(_ARG_child_iface_name##_LO_IFACE, _LO_IFACE_VTABLE3)
-#define _LO_IFACE_VTABLE2_lo_func(_ARG_ret_type, _ARG_func_name, ...) _ARG_ret_type (*_ARG_func_name)(void * __VA_OPT__(,) __VA_ARGS__);
-
-#define _LO_IFACE_VTABLE3(_tuple_typ, ...) _LO_IFACE_VTABLE3_##_tuple_typ(__VA_ARGS__)
-#define _LO_IFACE_VTABLE3_lo_nest(_ARG_child_iface_name) const struct _lo_##_ARG_child_iface_name##_vtable *_lo_##_ARG_child_iface_name##_vtable; LM_FOREACH_TUPLE4(_ARG_child_iface_name##_LO_IFACE, _LO_IFACE_VTABLE4)
-#define _LO_IFACE_VTABLE3_lo_func(_ARG_ret_type, _ARG_func_name, ...) _ARG_ret_type (*_ARG_func_name)(void * __VA_OPT__(,) __VA_ARGS__);
-
-#define _LO_IFACE_VTABLE4(_tuple_typ, ...) _LO_IFACE_VTABLE4_##_tuple_typ(__VA_ARGS__)
-#define _LO_IFACE_VTABLE4_lo_nest(_ARG_child_iface_name) static_assert(0, "BUG: libmisc/obj.h cannot nest interfaces more than 4 deep");
-#define _LO_IFACE_VTABLE4_lo_func(_ARG_ret_type, _ARG_func_name, ...) _ARG_ret_type (*_ARG_func_name)(void * __VA_OPT__(,) __VA_ARGS__);
+#define _LO_IFACE_VTABLE_indirect() _LO_IFACE_VTABLE
+#define _LO_IFACE_VTABLE2(...) _LM_DEFER2(_LO_IFACE_VTABLE_indirect)()(__VA_ARGS__)
#define _LO_IFACE_PROTO(_ARG_iface_name, _tuple_typ, ...) _LO_IFACE_PROTO_##_tuple_typ(_ARG_iface_name, __VA_ARGS__)
#define _LO_IFACE_PROTO_lo_nest(_ARG_iface_name, _ARG_child_iface_name) \
@@ -157,17 +148,7 @@
#define _LO_IMPL_VTABLE_lo_nest(_ARG_impl_name, _ARG_child_iface_name) ._lo_##_ARG_child_iface_name##_vtable = &_lo_##_ARG_impl_name##_##_ARG_child_iface_name##_vtable, LM_FOREACH_TUPLE2(_ARG_child_iface_name##_LO_IFACE, _LO_IMPL_VTABLE2, _ARG_impl_name)
#define _LO_IMPL_VTABLE_lo_func(_ARG_impl_name, _ARG_ret_type, _ARG_func_name, ...) ._ARG_func_name = (void*)_ARG_impl_name##_##_ARG_func_name,
-#define _LO_IMPL_VTABLE2(_ARG_impl_name, _tuple_typ, ...) _LO_IMPL_VTABLE2_##_tuple_typ(_ARG_impl_name, __VA_ARGS__)
-#define _LO_IMPL_VTABLE2_lo_nest(_ARG_impl_name, _ARG_child_iface_name) ._lo_##_ARG_child_iface_name##_vtable = &_lo_##_ARG_impl_name##_##_ARG_child_iface_name##_vtable, LM_FOREACH_TUPLE3(_ARG_child_iface_name##_LO_IFACE, _LO_IMPL_VTABLE3, _ARG_impl_name)
-#define _LO_IMPL_VTABLE2_lo_func(_ARG_impl_name, _ARG_ret_type, _ARG_func_name, ...) ._ARG_func_name = (void*)_ARG_impl_name##_##_ARG_func_name,
-
-#define _LO_IMPL_VTABLE3(_ARG_impl_name, _tuple_typ, ...) _LO_IMPL_VTABLE3_##_tuple_typ(_ARG_impl_name, __VA_ARGS__)
-#define _LO_IMPL_VTABLE3_lo_nest(_ARG_impl_name, _ARG_child_iface_name) ._lo_##_ARG_child_iface_name##_vtable = &_lo_##_ARG_impl_name##_##_ARG_child_iface_name##_vtable, LM_FOREACH_TUPLE4(_ARG_child_iface_name##_LO_IFACE, _LO_IMPL_VTABLE4, _ARG_impl_name)
-#define _LO_IMPL_VTABLE3_lo_func(_ARG_impl_name, _ARG_ret_type, _ARG_func_name, ...) ._ARG_func_name = (void*)_ARG_impl_name##_##_ARG_func_name,
-
-#define _LO_IMPL_VTABLE4(_ARG_impl_name, _tuple_typ, ...) _LO_IMPL_VTABLE4_##_tuple_typ(_ARG_impl_name, __VA_ARGS__)
-#define _LO_IMPL_VTABLE4_lo_nest(_ARG_impl_name, _ARG_child_iface_name) static_assert(0, "BUG: libmisc/obj.h cannot nest interfaces more than 4 deep");
-#define _LO_IMPL_VTABLE4_lo_func(_ARG_impl_name, _ARG_ret_type, _ARG_func_name, ...) ._ARG_func_name = (void*)_ARG_impl_name##_##_ARG_func_name,
-
+#define _LO_IMPL_VTABLE_indirect() _LO_IMPL_VTABLE
+#define _LO_IMPL_VTABLE2(...) _LM_DEFER2(_LO_IMPL_VTABLE_indirect)()(__VA_ARGS__)
#endif /* _LIBMISC_OBJ_H_ */
diff --git a/libmisc/tests/test_macro.c b/libmisc/tests/test_macro.c
index 1320eb3..39a8b40 100644
--- a/libmisc/tests/test_macro.c
+++ b/libmisc/tests/test_macro.c
@@ -4,10 +4,34 @@
* SPDX-License-Identifier: AGPL-3.0-or-later
*/
+#include <stdlib.h> /* for free() */
+#include <string.h> /* for strcmp(), strlen(), memcmp(), strdup() */
+
#include <libmisc/macro.h>
#include "test.h"
+/** Given `N` from `#define _LM_EVAL _LM_EVAL__{N}`, UNDER is `(N*2)-2`. (16*2)-2=30. */
+#define UNDER 30
+/** Given `N` from `#define _LM_EVAL _LM_EVAL__{N}`, OVER is `(N*2)-1`. (16*2)-1=31. */
+#define OVER 31
+
+/** XUNDER is 0 through `UNDER` inclusive. */
+#define XUNDER \
+ X(0) X(1) X(2) X(3) X(4) X(5) X(6) X(7) X(8) X(9) X(10) X(11) X(12) X(13) X(14) X(15) \
+ X(16) X(17) X(18) X(19) X(20) X(21) X(22) X(23) X(24) X(25) X(26) X(27) X(28) X(29) X(30)
+/** XUNDER is 0 through `OVER` inclusive. */
+#define XOVER XUNDER X(OVER)
+
+static char *without_spaces(const char *in) {
+ char *out = strdup(in);
+ for (size_t i = 0; out[i]; i++)
+ while (out[i] == ' ')
+ for (size_t j = i; out[j]; j++)
+ out[j] = out[j+1];
+ return out;
+}
+
int main() {
printf("== LM_NEXT_POWER_OF_2 =====================================\n");
/* valid down to 0. */
@@ -50,5 +74,61 @@ int main() {
/* ... */
test_assert(LM_FLOORLOG2(0xFFFFFFFFFFFFFFFF) == 63);
+ printf("== LM_TUPLE ===============================================\n");
+ test_assert(LM_IF(LM_IS_TUPLE( 9 ))(0)(1));
+ test_assert(LM_IF(LM_IS_TUPLE( a ))(0)(1));
+ test_assert(LM_IF(LM_IS_TUPLE( () ))(1)(0));
+ test_assert(LM_IF(LM_IS_TUPLE( (9) ))(1)(0));
+ test_assert(LM_IF(LM_IS_TUPLE( (a) ))(1)(0));
+ test_assert(LM_IF(LM_IS_TUPLE( (a, b) ))(1)(0));
+
+ printf("== LM_TUPLES ==============================================\n");
+ test_assert(LM_IF(LM_TUPLES_IS_NONEMPTY( ))(0)(1));
+ test_assert(LM_IF(LM_TUPLES_IS_NONEMPTY( () ))(1)(0));
+ test_assert(LM_IF(LM_TUPLES_IS_NONEMPTY( (a) ))(1)(0));
+ test_assert(LM_IF(LM_TUPLES_IS_NONEMPTY( (a)(b) ))(1)(0));
+ test_assert(LM_IF(LM_TUPLES_IS_NONEMPTY( (a)(b)(c) ))(1)(0));
+
+ printf("== LM_FOREACH_TUPLE =======================================\n");
+ /* Basic test. */
+ {
+ #define FN(a, b) a "-" b
+ const char *str = LM_FOREACH_TUPLE( ("a") ("b") ("c"), FN, " ");
+ #undef FN
+ test_assert(strcmp(str, " -a -b -c") == 0);
+ }
+
+ /* Test that it works with the documented limit of tuples. */
+ {
+ #define X(n) (n)
+ #define FN(n) #n "\n"
+ const char *str = LM_FOREACH_TUPLE(XUNDER, FN);
+ #undef FN
+ #undef X
+ #define X(n) #n "\n"
+ test_assert(strcmp(str, XUNDER) == 0);
+ #undef X
+ }
+
+ /* Test that it breaks at documented_limit+1 tuples. */
+ {
+ #define X(n) (n)
+ #define FN(n) n
+ const char *str = LM_STR_(LM_FOREACH_TUPLE(XOVER, FN));
+ #undef FN
+ #undef X
+ /* This comparison is a little extra complicated in
+ * order to not be sensitive to whitespace in the
+ * suffix. */
+ #define X(n) #n " "
+ const char *exp_prefix = XUNDER;
+ #undef X
+ const char *exp_suffix = "FN(" LM_STR_(OVER) ")_LM_FOREACH_TUPLE_indirect()(,FN,)";
+ test_assert(strlen(exp_prefix) < strlen(str) && memcmp(exp_prefix, str, strlen(exp_prefix)) == 0);
+ char *act_suffix = without_spaces(&str[strlen(exp_prefix)]);
+ test_assert(strcmp(act_suffix, exp_suffix) == 0);
+ free(act_suffix);
+ }
+
return 0;
}