summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorTavian Barnes <tavianator@tavianator.com>2025-07-07 13:19:13 -0400
committerTavian Barnes <tavianator@tavianator.com>2025-07-26 14:19:52 -0400
commitb4e3696101b049815dac8459b2b9cb18213b489e (patch)
treed4f36e7efb7a169f9c52fea47a4286ee121828b0
parentb01a0cc5d6f1557440f421e4cf86fea97a3442e2 (diff)
downloadbfs-b4e3696101b049815dac8459b2b9cb18213b489e.tar.xz
list: Simplify macros with C23 features
We can avoid the MACRO_((list), __VA_ARGS__, ) dance since the comma is no longer required. typeof() also comes in handy.
-rw-r--r--src/list.h236
-rw-r--r--tests/list.c3
2 files changed, 68 insertions, 171 deletions
diff --git a/src/list.h b/src/list.h
index 276c610..92000d5 100644
--- a/src/list.h
+++ b/src/list.h
@@ -82,6 +82,7 @@
#ifndef BFS_LIST_H
#define BFS_LIST_H
+#include "bfs.h"
#include "diag.h"
#include <stddef.h>
@@ -121,76 +122,21 @@
* The item to initialize.
* @node (optional)
* If specified, use item->node.next rather than item->next.
- *
- * ---
- *
- * We play some tricks with variadic macros to handle the optional parameter:
- *
- * SLIST_ITEM_INIT(item) => item->next = NULL
- * SLIST_ITEM_INIT(item, node) => item->node.next = NULL
- *
- * The first trick is that
- *
- * #define SLIST_ITEM_INIT(item, ...)
- *
- * won't work because both commas are required (until C23; see N3033). As a
- * workaround, we dispatch to another macro and add a trailing comma.
- *
- * SLIST_ITEM_INIT(item) => SLIST_ITEM_INIT_(item, )
- * SLIST_ITEM_INIT(item, node) => SLIST_ITEM_INIT_(item, node, )
*/
-#define SLIST_ITEM_INIT(...) \
- SLIST_ITEM_INIT_(__VA_ARGS__, )
+#define SLIST_ITEM_INIT(item, ...) \
+ SLIST_ITEM_INIT_((item), LIST_NEXT_(__VA_ARGS__))
-/**
- * Now we need a way to generate either ->next or ->node.next depending on
- * whether the node parameter was passed. The approach is based on
- *
- * #define FOO(...) BAR(__VA_ARGS__, 1, 2, )
- * #define BAR(x, y, z, ...) z
- *
- * FOO(a) => 2
- * FOO(a, b) => 1
- *
- * The LIST_NEXT_() macro uses this technique:
- *
- * LIST_NEXT_() => LIST_NODE_(next, )
- * LIST_NEXT_(node, ) => LIST_NODE_(next, node, )
- */
-#define LIST_NEXT_(...) \
- LIST_NODE_(next, __VA_ARGS__)
+#define SLIST_ITEM_INIT_(item, next) \
+ LIST_VOID_(item->next = NULL)
/**
- * LIST_NODE_() dispatches to yet another macro:
+ * Get the projection for an item's next pointer.
*
- * LIST_NODE_(next, ) => LIST_NODE__(next, , . , , )
- * LIST_NODE_(next, node, ) => LIST_NODE__(next, node, , . , , )
- */
-#define LIST_NODE_(dir, ...) \
- LIST_NODE__(dir, __VA_ARGS__, . , , )
-
-/**
- * And finally, LIST_NODE__() adds the node and the dot if necessary.
- *
- * dir node ignored dot
- * v v v v
- * LIST_NODE__(next, , . , , ) => next
- * LIST_NODE__(next, node, , . , , ) => node . next
- * ^ ^ ^ ^
- * dir node ignored dot
+ * LIST_NEXT_() => next
+ * LIST_NEXT_(node) => node.next
*/
-#define LIST_NODE__(dir, node, ignored, dot, ...) \
- node dot dir
-
-/**
- * SLIST_ITEM_INIT_() uses LIST_NEXT_() to generate the right name for the list
- * node, and finally delegates to the actual implementation.
- */
-#define SLIST_ITEM_INIT_(item, ...) \
- SLIST_ITEM_INIT__((item), LIST_NEXT_(__VA_ARGS__))
-
-#define SLIST_ITEM_INIT__(item, next) \
- LIST_VOID_(item->next = NULL)
+#define LIST_NEXT_(node) \
+ BFS_VA_IF(node)(node.next)(next)
/**
* Type-checking macro for singly-linked lists.
@@ -219,13 +165,6 @@
(!SLIST_HEAD(list))
/**
- * Like container_of(), but using the head pointer instead of offsetof() since
- * we don't have the type around.
- */
-#define SLIST_CONTAINER_(tail, head, next) \
- (void *)((char *)tail - ((char *)&head->next - (char *)head))
-
-/**
* Get the tail of a singly-linked list.
*
* @list
@@ -235,14 +174,11 @@
* @return
* The last item in the list.
*/
-#define SLIST_TAIL(...) \
- SLIST_TAIL_(__VA_ARGS__, )
+#define SLIST_TAIL(list, ...) \
+ SLIST_TAIL_((list), LIST_NEXT_(__VA_ARGS__))
-#define SLIST_TAIL_(list, ...) \
- SLIST_TAIL__((list), LIST_NEXT_(__VA_ARGS__))
-
-#define SLIST_TAIL__(list, next) \
- (list->head ? SLIST_CONTAINER_(list->tail, list->head, next) : NULL)
+#define SLIST_TAIL_(list, next) \
+ (list->head ? container_of(list->tail, typeof(*list->head), next) : NULL)
/**
* Check if an item is attached to a singly-linked list.
@@ -256,13 +192,10 @@
* @return
* Whether the item is attached to the list.
*/
-#define SLIST_ATTACHED(list, ...) \
- SLIST_ATTACHED_(list, __VA_ARGS__, )
-
-#define SLIST_ATTACHED_(list, item, ...) \
- SLIST_ATTACHED__((list), (item), LIST_NEXT_(__VA_ARGS__))
+#define SLIST_ATTACHED(list, item, ...) \
+ SLIST_ATTACHED_((list), (item), LIST_NEXT_(__VA_ARGS__))
-#define SLIST_ATTACHED__(list, item, next) \
+#define SLIST_ATTACHED_(list, item, next) \
(item->next || list->tail == &item->next)
/**
@@ -279,14 +212,11 @@
* @return
* A cursor for the next item.
*/
-#define SLIST_INSERT(list, cursor, ...) \
- SLIST_INSERT_(list, cursor, __VA_ARGS__, )
-
-#define SLIST_INSERT_(list, cursor, item, ...) \
- SLIST_INSERT__((list), (cursor), (item), LIST_NEXT_(__VA_ARGS__))
+#define SLIST_INSERT(list, cursor, item, ...) \
+ SLIST_INSERT_((list), (cursor), (item), LIST_NEXT_(__VA_ARGS__))
-#define SLIST_INSERT__(list, cursor, item, next) \
- (bfs_assert(!SLIST_ATTACHED__(list, item, next)), \
+#define SLIST_INSERT_(list, cursor, item, next) \
+ (bfs_assert(!SLIST_ATTACHED_(list, item, next)), \
item->next = *cursor, \
*cursor = item, \
list->tail = item->next ? list->tail : &item->next, \
@@ -302,11 +232,8 @@
* @node (optional)
* If specified, use item->node.next rather than item->next.
*/
-#define SLIST_APPEND(list, ...) \
- SLIST_APPEND_(list, __VA_ARGS__, )
-
-#define SLIST_APPEND_(list, item, ...) \
- LIST_VOID_(SLIST_INSERT_(list, (list)->tail, item, __VA_ARGS__))
+#define SLIST_APPEND(list, item, ...) \
+ LIST_VOID_(SLIST_INSERT(list, (list)->tail, item, __VA_ARGS__))
/**
* Add an item to the head of a singly-linked list.
@@ -318,11 +245,8 @@
* @node (optional)
* If specified, use item->node.next rather than item->next.
*/
-#define SLIST_PREPEND(list, ...) \
- SLIST_PREPEND_(list, __VA_ARGS__, )
-
-#define SLIST_PREPEND_(list, item, ...) \
- LIST_VOID_(SLIST_INSERT_(list, &(list)->head, item, __VA_ARGS__))
+#define SLIST_PREPEND(list, item, ...) \
+ LIST_VOID_(SLIST_INSERT(list, &(list)->head, item, __VA_ARGS__))
/**
* Splice a singly-linked list into another.
@@ -366,15 +290,12 @@
* @return
* The removed item.
*/
-#define SLIST_REMOVE(list, ...) \
- SLIST_REMOVE_(list, __VA_ARGS__, )
+#define SLIST_REMOVE(list, cursor, ...) \
+ SLIST_REMOVE_((list), (cursor), LIST_NEXT_(__VA_ARGS__))
-#define SLIST_REMOVE_(list, cursor, ...) \
- SLIST_REMOVE__((list), (cursor), LIST_NEXT_(__VA_ARGS__))
-
-#define SLIST_REMOVE__(list, cursor, next) \
+#define SLIST_REMOVE_(list, cursor, next) \
(list->tail = (*cursor)->next ? list->tail : cursor, \
- slist_remove_(*cursor, cursor, &(*cursor)->next, sizeof(*cursor)))
+ (typeof(*cursor))slist_remove_(*cursor, cursor, &(*cursor)->next, sizeof(*cursor)))
// Helper for SLIST_REMOVE()
static inline void *slist_remove_(void *ret, void *cursor, void *next, size_t size) {
@@ -396,14 +317,8 @@ static inline void *slist_remove_(void *ret, void *cursor, void *next, size_t si
* @return
* The popped item, or NULL if the list was empty.
*/
-#define SLIST_POP(...) \
- SLIST_POP_(__VA_ARGS__, )
-
-#define SLIST_POP_(list, ...) \
- SLIST_POP__((list), __VA_ARGS__)
-
-#define SLIST_POP__(list, ...) \
- (list->head ? SLIST_REMOVE_(list, &list->head, __VA_ARGS__) : NULL)
+#define SLIST_POP(list, ...) \
+ ((list)->head ? SLIST_REMOVE(list, &(list)->head, __VA_ARGS__) : NULL)
/**
* Loop over the items in a singly-linked list.
@@ -417,15 +332,12 @@ static inline void *slist_remove_(void *ret, void *cursor, void *next, size_t si
* @node (optional)
* If specified, use head->node.next rather than head->next.
*/
-#define for_slist(type, item, ...) \
- for_slist_(type, item, __VA_ARGS__, )
+#define for_slist(type, item, list, ...) \
+ for_slist_(type, item, (list), LIST_NEXT_(__VA_ARGS__))
-#define for_slist_(type, item, list, ...) \
- for_slist__(type, item, (list), LIST_NEXT_(__VA_ARGS__))
-
-#define for_slist__(type, item, list, next) \
- for (type *item = list->head, *_next; \
- item && (SLIST_CHECK_(list), _next = item->next, true); \
+#define for_slist_(type, item, list, next) \
+ for (type *item = SLIST_HEAD(list), *_next; \
+ item && (_next = item->next, true); \
item = _next)
/**
@@ -440,8 +352,8 @@ static inline void *slist_remove_(void *ret, void *cursor, void *next, size_t si
* @node (optional)
* If specified, use head->node.next rather than head->next.
*/
-#define drain_slist(type, item, ...) \
- for (type *item; (item = SLIST_POP(__VA_ARGS__));)
+#define drain_slist(type, item, list, ...) \
+ for (type *item; (item = SLIST_POP(list, __VA_ARGS__));)
/**
* Initialize a doubly-linked list.
@@ -456,11 +368,11 @@ static inline void *slist_remove_(void *ret, void *cursor, void *next, size_t si
LIST_VOID_(list->head = list->tail = NULL)
/**
- * LIST_PREV_() => prev
- * LIST_PREV_(node, ) => node.prev
+ * LIST_PREV_() => prev
+ * LIST_PREV_(node) => node.prev
*/
-#define LIST_PREV_(...) \
- LIST_NODE_(prev, __VA_ARGS__)
+#define LIST_PREV_(node) \
+ BFS_VA_IF(node)(node.prev)(prev)
/**
* Initialize a doubly-linked list item.
@@ -470,13 +382,10 @@ static inline void *slist_remove_(void *ret, void *cursor, void *next, size_t si
* @node (optional)
* If specified, use item->node.next rather than item->next.
*/
-#define LIST_ITEM_INIT(...) \
- LIST_ITEM_INIT_(__VA_ARGS__, )
-
-#define LIST_ITEM_INIT_(item, ...) \
- LIST_ITEM_INIT__((item), LIST_PREV_(__VA_ARGS__), LIST_NEXT_(__VA_ARGS__))
+#define LIST_ITEM_INIT(item, ...) \
+ LIST_ITEM_INIT_((item), LIST_PREV_(__VA_ARGS__), LIST_NEXT_(__VA_ARGS__))
-#define LIST_ITEM_INIT__(item, prev, next) \
+#define LIST_ITEM_INIT_(item, prev, next) \
LIST_VOID_(item->prev = item->next = NULL)
/**
@@ -489,10 +398,7 @@ static inline void *slist_remove_(void *ret, void *cursor, void *next, size_t si
* Check if a doubly-linked list is empty.
*/
#define LIST_EMPTY(list) \
- LIST_EMPTY_((list))
-
-#define LIST_EMPTY_(list) \
- (LIST_CHECK_(list), !list->head)
+ (LIST_CHECK_(list), !(list)->head)
/**
* Add an item to the tail of a doubly-linked list.
@@ -504,8 +410,8 @@ static inline void *slist_remove_(void *ret, void *cursor, void *next, size_t si
* @node (optional)
* If specified, use item->node.{prev,next} rather than item->{prev,next}.
*/
-#define LIST_APPEND(list, ...) \
- LIST_INSERT(list, (list)->tail, __VA_ARGS__)
+#define LIST_APPEND(list, item, ...) \
+ LIST_INSERT(list, (list)->tail, item, __VA_ARGS__)
/**
* Add an item to the head of a doubly-linked list.
@@ -517,8 +423,8 @@ static inline void *slist_remove_(void *ret, void *cursor, void *next, size_t si
* @node (optional)
* If specified, use item->node.{prev,next} rather than item->{prev,next}.
*/
-#define LIST_PREPEND(list, ...) \
- LIST_INSERT(list, NULL, __VA_ARGS__)
+#define LIST_PREPEND(list, item, ...) \
+ LIST_INSERT(list, NULL, item, __VA_ARGS__)
/**
* Check if an item is attached to a doubly-linked list.
@@ -532,13 +438,10 @@ static inline void *slist_remove_(void *ret, void *cursor, void *next, size_t si
* @return
* Whether the item is attached to the list.
*/
-#define LIST_ATTACHED(list, ...) \
- LIST_ATTACHED_(list, __VA_ARGS__, )
-
-#define LIST_ATTACHED_(list, item, ...) \
- LIST_ATTACHED__((list), (item), LIST_PREV_(__VA_ARGS__), LIST_NEXT_(__VA_ARGS__))
+#define LIST_ATTACHED(list, item, ...) \
+ LIST_ATTACHED_((list), (item), LIST_PREV_(__VA_ARGS__), LIST_NEXT_(__VA_ARGS__))
-#define LIST_ATTACHED__(list, item, prev, next) \
+#define LIST_ATTACHED_(list, item, prev, next) \
(item->prev || item->next || list->head == item || list->tail == item)
/**
@@ -553,14 +456,11 @@ static inline void *slist_remove_(void *ret, void *cursor, void *next, size_t si
* @node (optional)
* If specified, use item->node.{prev,next} rather than item->{prev,next}.
*/
-#define LIST_INSERT(list, cursor, ...) \
- LIST_INSERT_(list, cursor, __VA_ARGS__, )
-
-#define LIST_INSERT_(list, cursor, item, ...) \
- LIST_INSERT__((list), (cursor), (item), LIST_PREV_(__VA_ARGS__), LIST_NEXT_(__VA_ARGS__))
+#define LIST_INSERT(list, cursor, item, ...) \
+ LIST_INSERT_((list), (cursor), (item), LIST_PREV_(__VA_ARGS__), LIST_NEXT_(__VA_ARGS__))
-#define LIST_INSERT__(list, cursor, item, prev, next) LIST_VOID_( \
- bfs_assert(!LIST_ATTACHED__(list, item, prev, next)), \
+#define LIST_INSERT_(list, cursor, item, prev, next) LIST_VOID_( \
+ bfs_assert(!LIST_ATTACHED_(list, item, prev, next)), \
item->prev = cursor, \
item->next = cursor ? cursor->next : list->head, \
*(item->prev ? &item->prev->next : &list->head) = item, \
@@ -576,13 +476,10 @@ static inline void *slist_remove_(void *ret, void *cursor, void *next, size_t si
* @node (optional)
* If specified, use item->node.{prev,next} rather than item->{prev,next}.
*/
-#define LIST_REMOVE(list, ...) \
- LIST_REMOVE_(list, __VA_ARGS__, )
+#define LIST_REMOVE(list, item, ...) \
+ LIST_REMOVE_((list), (item), LIST_PREV_(__VA_ARGS__), LIST_NEXT_(__VA_ARGS__))
-#define LIST_REMOVE_(list, item, ...) \
- LIST_REMOVE__((list), (item), LIST_PREV_(__VA_ARGS__), LIST_NEXT_(__VA_ARGS__))
-
-#define LIST_REMOVE__(list, item, prev, next) LIST_VOID_( \
+#define LIST_REMOVE_(list, item, prev, next) LIST_VOID_( \
*(item->prev ? &item->prev->next : &list->head) = item->next, \
*(item->next ? &item->next->prev : &list->tail) = item->prev, \
item->prev = item->next = NULL)
@@ -599,15 +496,12 @@ static inline void *slist_remove_(void *ret, void *cursor, void *next, size_t si
* @node (optional)
* If specified, use head->node.next rather than head->next.
*/
-#define for_list(type, item, ...) \
- for_list_(type, item, __VA_ARGS__, )
-
-#define for_list_(type, item, list, ...) \
- for_list__(type, item, (list), LIST_NEXT_(__VA_ARGS__))
+#define for_list(type, item, list, ...) \
+ for_list_(type, item, (list), LIST_NEXT_(__VA_ARGS__))
-#define for_list__(type, item, list, next) \
- for (type *item = list->head, *_next; \
- item && (LIST_CHECK_(list), _next = item->next, true); \
+#define for_list_(type, item, list, next) \
+ for (type *item = (LIST_CHECK_(list), list->head), *_next; \
+ item && (_next = item->next, true); \
item = _next)
#endif // BFS_LIST_H
diff --git a/tests/list.c b/tests/list.c
index 5d0403f..1d70f33 100644
--- a/tests/list.c
+++ b/tests/list.c
@@ -96,4 +96,7 @@ void check_list(void) {
SLIST_APPEND(&l2, &i12);
SLIST_SPLICE(&l1, &l1.head->next, &l2);
bfs_verify(check_list_items(&l1, ARRAY(10, 11, 12, 15, 20)));
+
+ // Check the return type of SLIST_POP()
+ bfs_check(SLIST_POP(&l1)->n == 10);
}