[ccan] [PATCH 5/6] lstack: Streamline interface with TCON_CONTAINER

David Gibson david at gibson.dropbear.id.au
Tue Oct 20 15:45:59 AEDT 2015


The interfaces the lstack module currently has implement (partial) type
safety in a somewhat clunk way - types and member names need to be passed
to a number of entry points.

This patch uses the new TCON_CONTAINER magic to stash the typing
information into the declaration of the stack object, so it no longer needs
to be explicitly passed to later calls.

This does alter the lstack interface incompatibly.  I think the module
is young enough that we can reasonably do that.  One other module,
'aga', was using lstack, and this also includes fixes so that it still
works.

Signed-off-by: David Gibson <david at gibson.dropbear.id.au>
---
 ccan/aga/dfs.c         |  18 ++++---
 ccan/lstack/_info      |   8 +--
 ccan/lstack/lstack.h   | 141 ++++++++++++++++++++++++++++---------------------
 ccan/lstack/test/run.c |  31 +++++------
 4 files changed, 111 insertions(+), 87 deletions(-)

diff --git a/ccan/aga/dfs.c b/ccan/aga/dfs.c
index 638481c..5f836ec 100644
--- a/ccan/aga/dfs.c
+++ b/ccan/aga/dfs.c
@@ -12,25 +12,27 @@
  * Depth first search
  */
 
-static bool dfs_push(struct aga_graph *g, struct lstack *stack,
+typedef LSTACK(struct aga_node, u.dfs.parent) dfs_stack;
+
+static bool dfs_push(struct aga_graph *g, dfs_stack *stack,
 		     struct aga_node *n)
 {
 	if (!aga_update_node(g, n))
 		return false;
 
-	lstack_push(stack, n, u.dfs.parent);
+	lstack_push(stack, n);
 	n->u.dfs.edge = aga_first_edge(g, n);
 	return true;
 }
 
-static void dfs_pop(struct lstack *stack)
+static void dfs_pop(dfs_stack *stack)
 {
-	lstack_pop(stack, struct aga_node, u.dfs.parent);
+	lstack_pop(stack);
 }
 
-static struct aga_node *dfs_top(struct lstack *stack)
+static struct aga_node *dfs_top(dfs_stack *stack)
 {
-	return lstack_top(stack, struct aga_node, u.dfs.parent);
+	return lstack_top(stack);
 }
 
 int aga_dfs_start(struct aga_graph *g)
@@ -46,7 +48,7 @@ int aga_dfs_start(struct aga_graph *g)
 
 struct aga_node *aga_dfs_explore(struct aga_graph *g, struct aga_node *n)
 {
-	LSTACK(stack);
+	dfs_stack stack = LSTACK_INIT;
 
 	if (!aga_check_state(g))
 		return NULL;
@@ -57,7 +59,7 @@ struct aga_node *aga_dfs_explore(struct aga_graph *g, struct aga_node *n)
 	if (dfs_push(g, &stack, n))
 		return n;
 
-	lstack_init_from_top(&stack, n, u.dfs.parent);
+	lstack_init_from_top(&stack, n);
 
 	while ((n = dfs_top(&stack))) {
 		const void *e = n->u.dfs.edge;
diff --git a/ccan/lstack/_info b/ccan/lstack/_info
index 5b270bc..21fe3c7 100644
--- a/ccan/lstack/_info
+++ b/ccan/lstack/_info
@@ -23,18 +23,18 @@
  *	{
  *		int i;
  *		struct arg *a;
- *		LSTACK(argstack);
+ *		LSTACK(struct arg, sl) argstack;
  *
  *		for (i = 0; i < argc; i++) {
  *			a = malloc(sizeof(*a));
  *			a->arg = argv[i];
- *			lstack_push(&argstack, a, sl);
+ *			lstack_push(&argstack, a);
  *		}
  *
  *		printf("Command line arguments in reverse:\n");
  *
  *		while (!lstack_empty(&argstack)) {
- *			a = lstack_pop(&argstack, struct arg, sl);
+ *			a = lstack_pop(&argstack);
  *			printf("Argument: %s\n", a->arg);
  *			free(a);
  *		}
@@ -49,7 +49,7 @@ int main(int argc, char *argv[])
 		return 1;
 
 	if (strcmp(argv[1], "depends") == 0) {
-		printf("ccan/container_of\n");
+		printf("ccan/tcon\n");
 		return 0;
 	}
 
diff --git a/ccan/lstack/lstack.h b/ccan/lstack/lstack.h
index 7eb880a..ef910bd 100644
--- a/ccan/lstack/lstack.h
+++ b/ccan/lstack/lstack.h
@@ -6,7 +6,7 @@
 #include <stdio.h>
 #include <assert.h>
 
-#include <ccan/container_of/container_of.h>
+#include <ccan/tcon/tcon.h>
 
 /**
  * struct lstack_link - a stack link
@@ -25,36 +25,75 @@ struct lstack_link {
 };
 
 /**
- * struct lstack - a stack
+ * struct lstack_ - a stack (internal type)
  * @b: the top of the stack (NULL if empty)
  */
-struct lstack {
+struct lstack_ {
 	struct lstack_link *top;
 };
 
 /**
- * LSTACK - define and initialize an empty stack
- * @name: the name of the lstack.
+ * LSTACK - declare a stack
+ * @type: the type of elements in the stack
+ * @link: the field containing the lstack_link in @type
  *
- * The LSTACK macro defines an lstack and initializes it to an empty
- * stack.  It can be prepended by "static" to define a static lstack.
+ * The LSTACK macro declares an lstack.  It can be prepended by
+ * "static" to define a static lstack.  The stack begins in undefined
+ * state, you must either initialize with LSTACK_INIT, or call
+ * lstack_init() before using it.
  *
  * See also:
  *	lstack_init()
  *
  * Example:
- *	LSTACK(my_stack);
+ *	struct element {
+ *		int value;
+ *		struct lstack_link link;
+ *	};
+ *	LSTACK(struct element, link) my_stack;
+ */
+#define LSTACK(etype, link)						\
+	TCON_WRAP(struct lstack_,					\
+		  TCON_CONTAINER(canary, etype, link))
+
+/**
+ * LSTACK_INIT - initializer for an empty stack
+ *
+ * The LSTACK_INIT macro returns a suitable initializer for a stack
+ * defined with LSTACK.
+ *
+ * Example:
+ *	struct element {
+ *		int value;
+ *		struct lstack_link link;
+ *	};
+ *	LSTACK(struct element, link) my_stack = LSTACK_INIT;
  *
  *	assert(lstack_empty(&my_stack));
  */
-#define LSTACK(name) \
-	struct lstack name = { NULL, }
+#define LSTACK_INIT				\
+	TCON_WRAP_INIT({ NULL, })
+
+/**
+ * lstack_entry - convert an lstack_link back into the structure containing it.
+ * @s: the stack
+ * @l: the lstack_link
+ *
+ * Example:
+ *	struct element {
+ *		int value;
+ *		struct lstack_link link;
+ *	} e;
+ *	LSTACK(struct element, link) my_stack;
+ *	assert(lstack_entry(&my_stack, &e.link) == &e);
+ */
+#define lstack_entry(s_, l_) tcon_container_of((s_), canary, (l_))
+
 
 /**
  * lstack_init_from_top - initialize a stack with a given top element
  * @s: the lstack to initialize
  * @e: pointer to the top element of the new stack
- * @member: member of the element containing the lstack_link
  *
  * USE WITH CAUTION: This is for handling unusual cases where you have
  * a pointer to an element in a previously constructed stack but can't
@@ -62,32 +101,35 @@ struct lstack {
  * should use lstack_init().
  *
  * Example:
- *	LSTACK(stack1);
- *	struct lstack stack2;
  *	struct element {
  *		int value;
  *		struct lstack_link link;
- *	} el;
+ *	} e;
+ *	LSTACK(struct element, link) stack1 = LSTACK_INIT;
+ *	LSTACK(struct element, link) stack2;
  *
- *	lstack_push(&stack1, &el, link);
+ *	lstack_push(&stack1, &e);
  *
- *	lstack_init_from_top(&stack2,
- *	                     lstack_top(&stack1, struct element, link), link);
+ *	lstack_init_from_top(&stack2, lstack_top(&stack1));
  */
-#define lstack_init_from_top(s, e, member) \
-	(lstack_init_((s), &(e)->member))
+#define lstack_init_from_top(s_, e_)	\
+	(lstack_init_(tcon_unwrap(s_), tcon_member_of((s_), canary, (e_))))
 
 /**
  * lstack_init - initialize a stack
  * @h: the lstack to set to an empty stack
  *
  * Example:
- *	struct lstack *sp = malloc(sizeof(*sp));
+ *	struct element {
+ *		int value;
+ *		struct lstack_link link;
+ *	};
+ *	LSTACK(struct element, link) *sp = malloc(sizeof(*sp));
  *	lstack_init(sp);
  */
-#define lstack_init(s) \
-	(lstack_init_((s), NULL))
-static inline void lstack_init_(struct lstack *s, struct lstack_link *top)
+#define lstack_init(s_) \
+	(lstack_init_(tcon_unwrap(s_), NULL))
+static inline void lstack_init_(struct lstack_ *s, struct lstack_link *top)
 {
 	s->top = top;
 }
@@ -97,47 +139,29 @@ static inline void lstack_init_(struct lstack *s, struct lstack_link *top)
  * @s: the stack
  *
  * If the stack is empty, returns true.
- *
- * Example:
- *	assert(lstack_empty(sp));
  */
-static inline bool lstack_empty(const struct lstack *s)
+#define lstack_empty(s_) \
+	lstack_empty_(tcon_unwrap(s_))
+static inline bool lstack_empty_(const struct lstack_ *s)
 {
 	return (s->top == NULL);
 }
 
 /**
- * lstack_entry - convert an lstack_link back into the structure containing it.
- * @e: the lstack_link
- * @type: the type of the entry
- * @member: the lstack_link member of the type
- *
- * Example:
- *	struct stacker {
- *		char *name;
- *		struct lstack_link sl;
- *	} st;
- *	assert(lstack_entry(&st.sl, struct stacker, sl) == &st);
- */
-#define lstack_entry(n, type, member) container_of_or_null(n, type, member)
-
-/**
  * lstack_top - get top entry in a stack
  * @s: the stack
- * @type: the type of stack entries
- * @member: the lstack_link entry
  *
  * If the stack is empty, returns NULL.
  *
  * Example:
- *	struct stacker *t;
+ *	struct element *t;
  *
- *	t = lstack_top(sp, struct stacker, sl);
- *	assert(lstack_pop(sp, struct stacker, sl) == t);
+ *	t = lstack_top(sp);
+ *	assert(lstack_pop(sp) == t);
  */
-#define lstack_top(s, type, member) \
-	lstack_entry(lstack_top_((s)), type, member)
-static inline struct lstack_link *lstack_top_(const struct lstack *s)
+#define lstack_top(s_) \
+	lstack_entry((s_), lstack_top_(tcon_unwrap(s_)))
+static inline struct lstack_link *lstack_top_(const struct lstack_ *s)
 {
 	return s->top;
 }
@@ -146,13 +170,12 @@ static inline struct lstack_link *lstack_top_(const struct lstack *s)
  * lstack_push - add an entry to the top of the stack
  * @s: the stack to add the node to
  * @e: the item to push
- * @member: the lstack_link field of *e
  *
  * The lstack_link does not need to be initialized; it will be overwritten.
  */
-#define lstack_push(s, e, member) \
-	lstack_push_((s), &((e)->member))
-static inline void lstack_push_(struct lstack *s, struct lstack_link *e)
+#define lstack_push(s_, e_) \
+	lstack_push_(tcon_unwrap(s_), tcon_member_of((s_), canary, (e_)))
+static inline void lstack_push_(struct lstack_ *s, struct lstack_link *e)
 {
 	e->down = lstack_top_(s);
 	s->top = e;
@@ -161,19 +184,17 @@ static inline void lstack_push_(struct lstack *s, struct lstack_link *e)
 /**
  * lstack_pop - remove and return the entry from the top of the stack
  * @s: the stack
- * @type: the type of stack entries
- * @member: the lstack_link field of @type
  *
  * Note that this leaves the returned entry's link in an undefined
  * state; it can be added to another stack, but not deleted again.
  */
-#define lstack_pop(s, type, member) \
-	lstack_entry(lstack_pop_((s)), type, member)
-static inline struct lstack_link *lstack_pop_(struct lstack *s)
+#define lstack_pop(s_)					\
+	lstack_entry((s_), lstack_pop_(tcon_unwrap((s_))))
+static inline struct lstack_link *lstack_pop_(struct lstack_ *s)
 {
 	struct lstack_link *top;
 
-	if (lstack_empty(s))
+	if (lstack_empty_(s))
 		return NULL;
 
 	top = lstack_top_(s);
diff --git a/ccan/lstack/test/run.c b/ccan/lstack/test/run.c
index d67ba32..1bc7175 100644
--- a/ccan/lstack/test/run.c
+++ b/ccan/lstack/test/run.c
@@ -10,7 +10,7 @@ struct stacker {
 
 int main(void)
 {
-	LSTACK(s);
+	LSTACK(struct stacker, sl) s = LSTACK_INIT;
 	struct stacker a = { "Alice" };
 	struct stacker b = { "Bob" };
 	struct stacker c = { "Carol" };
@@ -20,42 +20,43 @@ int main(void)
 	plan_tests(18);
 
 	ok1(lstack_empty(&s));
-	ok1(lstack_top(&s, struct stacker, sl) == NULL);
+	diag("top = %p\n", lstack_top(&s));
+	ok1(lstack_top(&s) == NULL);
 
-	lstack_push(&s, &a, sl);
+	lstack_push(&s, &a);
 
 	ok1(!lstack_empty(&s));
-	ok1(lstack_top(&s, struct stacker, sl) == &a);
+	ok1(lstack_top(&s) == &a);
 
-	lstack_push(&s, &b, sl);
+	lstack_push(&s, &b);
 
 	ok1(!lstack_empty(&s));
-	ok1(lstack_top(&s, struct stacker, sl) == &b);
+	ok1(lstack_top(&s) == &b);
 
-	lstack_push(&s, &c, sl);
+	lstack_push(&s, &c);
 
 	ok1(!lstack_empty(&s));
-	ok1(lstack_top(&s, struct stacker, sl) == &c);
+	ok1(lstack_top(&s) == &c);
 
-	stacker = lstack_pop(&s, struct stacker, sl);
+	stacker = lstack_pop(&s);
 	ok1(stacker == &c);
 
 	ok1(!lstack_empty(&s));
-	ok1(lstack_top(&s, struct stacker, sl) == &b);
+	ok1(lstack_top(&s) == &b);
 
-	stacker = lstack_pop(&s, struct stacker, sl);
+	stacker = lstack_pop(&s);
 	ok1(stacker == &b);
 
 	ok1(!lstack_empty(&s));
-	ok1(lstack_top(&s, struct stacker, sl) == &a);
+	ok1(lstack_top(&s) == &a);
 
-	stacker = lstack_pop(&s, struct stacker, sl);
+	stacker = lstack_pop(&s);
 	ok1(stacker == &a);
 
 	ok1(lstack_empty(&s));
-	ok1(lstack_top(&s, struct stacker, sl) == NULL);
+	ok1(lstack_top(&s) == NULL);
 
-	ok1(lstack_pop(&s, struct stacker, sl) == NULL);
+	ok1(lstack_pop(&s) == NULL);
 
 	/* This exits depending on whether all tests passed */
 	return exit_status();
-- 
2.4.3



More information about the ccan mailing list