tlist2: a alternate to tlist that encodes the member offset into the container type
authorCody P Schafer <dev@codyps.com>
Tue, 31 May 2016 19:09:13 +0000 (15:09 -0400)
committerRusty Russell <rusty@rustcorp.com.au>
Thu, 2 Jun 2016 01:58:35 +0000 (11:28 +0930)
Signed-off-by: Cody P Schafer <dev@codyps.com>
13 files changed:
Makefile-ccan
ccan/tlist2/LICENSE [new symlink]
ccan/tlist2/_info [new file with mode: 0644]
ccan/tlist2/test/.c [new file with mode: 0644]
ccan/tlist2/test/compile_fail-tlist_add_2.c [new file with mode: 0644]
ccan/tlist2/test/compile_fail-tlist_add_tail_2.c [new file with mode: 0644]
ccan/tlist2/test/compile_fail-tlist_del_from_2.c [new file with mode: 0644]
ccan/tlist2/test/compile_fail-tlist_for_each_2.c [new file with mode: 0644]
ccan/tlist2/test/compile_fail-tlist_for_each_safe_2.c [new file with mode: 0644]
ccan/tlist2/test/compile_fail-tlist_tail_2.c [new file with mode: 0644]
ccan/tlist2/test/compile_fail-tlist_top_2.c [new file with mode: 0644]
ccan/tlist2/test/run_2.c [new file with mode: 0644]
ccan/tlist2/tlist2.h [new file with mode: 0644]

index 56dc82cd55598bc3a6460b69a6fa04ca3d44ef3f..47249b90a0bc092c814219963b97ffd22e5809a9 100644 (file)
@@ -123,6 +123,7 @@ MODS := a_star \
        time \
        timer \
        tlist \
+       tlist2 \
        ttxml \
        typesafe_cb \
        version \
diff --git a/ccan/tlist2/LICENSE b/ccan/tlist2/LICENSE
new file mode 120000 (symlink)
index 0000000..7455044
--- /dev/null
@@ -0,0 +1 @@
+../../licenses/LGPL-3
\ No newline at end of file
diff --git a/ccan/tlist2/_info b/ccan/tlist2/_info
new file mode 100644 (file)
index 0000000..3546beb
--- /dev/null
@@ -0,0 +1,78 @@
+#include "config.h"
+#include <stdio.h>
+#include <string.h>
+
+/**
+ * tlist2 - typesafe double linked list routines, alternative form
+ *
+ * The list header contains routines for manipulating double linked lists;
+ * this extends it so you can create list head types which only accomodate
+ * a specific entry type.
+ *
+ * Compared to 'tlist', this:
+ *
+ *  - does not allow (or require) declaring an extra struct (uses anonymous
+ *    structs)
+ *  - encodes the member offset into the type (and as a result doesn't need the
+ *    member name, but requires the source list)
+ *
+ * Based on tlist by: Rusty Russell <rusty@rustcorp.com.au>
+ *
+ * Example:
+ *     #include <err.h>
+ *     #include <stdio.h>
+ *     #include <stdlib.h>
+ *     #include <ccan/tlist2/tlist2.h>
+ *
+ *     struct child {
+ *             const char *name;
+ *             struct list_node list;
+ *     };
+ *
+ *     struct parent {
+ *             const char *name;
+ *             TLIST2(struct child, list) children;
+ *             unsigned int num_children;
+ *     };
+ *
+ *     int main(int argc, char *argv[])
+ *     {
+ *             struct parent p;
+ *             struct child *c;
+ *             unsigned int i;
+ *
+ *             if (argc < 2)
+ *                     errx(1, "Usage: %s parent children...", argv[0]);
+ *
+ *             p.name = argv[1];
+ *             tlist2_init(&p.children);
+ *             for (i = 2; i < argc; i++) {
+ *                     c = malloc(sizeof(*c));
+ *                     c->name = argv[i];
+ *                     tlist2_add(&p.children, c);
+ *                     p.num_children++;
+ *             }
+ *
+ *             printf("%s has %u children:", p.name, p.num_children);
+ *             tlist2_for_each(&p.children, c)
+ *                     printf("%s ", c->name);
+ *             printf("\n");
+ *             return 0;
+ *     }
+ *
+ * License: LGPL
+ * Author: Cody P Schafer <dev@codyps.com>
+ */
+int main(int argc, char *argv[])
+{
+       if (argc != 2)
+               return 1;
+
+       if (strcmp(argv[1], "depends") == 0) {
+               printf("ccan/list\n");
+               printf("ccan/tcon\n");
+               return 0;
+       }
+
+       return 1;
+}
diff --git a/ccan/tlist2/test/.c b/ccan/tlist2/test/.c
new file mode 100644 (file)
index 0000000..29b08fe
--- /dev/null
@@ -0,0 +1,35 @@
+#include <ccan/tlist/tlist.h>
+
+TLIST_TYPE(children, struct child);
+TLIST_TYPE(cousins, struct cousin);
+
+struct child {
+       const char *name;
+       struct list_node list;
+};
+
+struct cousin {
+       const char *name;
+       struct list_node list;
+};
+
+int main(int argc, char *argv[])
+{
+       struct tlist2_children children;
+       struct tlist2_cousins cousins;
+       struct child child = { "child" };
+       struct cousin cousin = { "cousin" };
+
+       tlist2_init(&children);
+       tlist2_init(&cousins);
+       tlist2_add(&children, &child, list);
+       tlist2_add(&cousins, &cousin, list);
+       tlist2_del_from(&cousins, &cousin, list);
+#ifdef FAIL
+#if !HAVE_FLEXIBLE_ARRAY_MEMBER
+#error Need flexible array members to check type
+#endif
+       tlist2_add(&children, &cousin, list);
+#endif
+       return 0;
+}
diff --git a/ccan/tlist2/test/compile_fail-tlist_add_2.c b/ccan/tlist2/test/compile_fail-tlist_add_2.c
new file mode 100644 (file)
index 0000000..2151b2d
--- /dev/null
@@ -0,0 +1,32 @@
+#include <ccan/tlist2/tlist2.h>
+
+struct child {
+       const char *name;
+       struct list_node list;
+};
+
+struct cousin {
+       const char *name;
+       struct list_node list;
+};
+
+int main(int argc, char *argv[])
+{
+       TLIST2(struct child, list) children;
+       TLIST2(struct cousin, list) cousins;
+       struct child child = { "child" };
+       struct cousin cousin = { "cousin" };
+
+       tlist2_init(&children);
+       tlist2_init(&cousins);
+       tlist2_add(&children, &child);
+       tlist2_add(&cousins, &cousin);
+       tlist2_del_from(&cousins, &cousin);
+#ifdef FAIL
+#if !HAVE_FLEXIBLE_ARRAY_MEMBER
+#error Need flexible array members to check type
+#endif
+       tlist2_add(&children, &cousin);
+#endif
+       return 0;
+}
diff --git a/ccan/tlist2/test/compile_fail-tlist_add_tail_2.c b/ccan/tlist2/test/compile_fail-tlist_add_tail_2.c
new file mode 100644 (file)
index 0000000..2b4652c
--- /dev/null
@@ -0,0 +1,32 @@
+#include <ccan/tlist2/tlist2.h>
+
+struct child {
+       const char *name;
+       struct list_node list;
+};
+
+struct cousin {
+       const char *name;
+       struct list_node list;
+};
+
+int main(int argc, char *argv[])
+{
+       TLIST2(struct child, list) children;
+       TLIST2(struct cousin, list) cousins;
+       struct child child = { "child" };
+       struct cousin cousin = { "cousin" };
+
+       tlist2_init(&children);
+       tlist2_init(&cousins);
+       tlist2_add(&children, &child);
+       tlist2_add(&cousins, &cousin);
+       tlist2_del_from(&cousins, &cousin);
+#ifdef FAIL
+#if !HAVE_FLEXIBLE_ARRAY_MEMBER
+#error Need flexible array members to check type
+#endif
+       tlist2_add_tail(&children, &cousin);
+#endif
+       return 0;
+}
diff --git a/ccan/tlist2/test/compile_fail-tlist_del_from_2.c b/ccan/tlist2/test/compile_fail-tlist_del_from_2.c
new file mode 100644 (file)
index 0000000..c5651e5
--- /dev/null
@@ -0,0 +1,31 @@
+#include <ccan/tlist2/tlist2.h>
+
+struct child {
+       const char *name;
+       struct list_node list;
+};
+
+struct cousin {
+       const char *name;
+       struct list_node list;
+};
+
+int main(int argc, char *argv[])
+{
+       TLIST2(struct child, list) children;
+       TLIST2(struct cousin, list) cousins;
+       struct child child = { "child" };
+       struct cousin cousin = { "cousin" };
+
+       tlist2_init(&children);
+       tlist2_init(&cousins);
+       tlist2_add(&children, &child);
+       tlist2_add(&cousins, &cousin);
+#ifdef FAIL
+#if !HAVE_FLEXIBLE_ARRAY_MEMBER
+#error Need flexible array members to check type
+#endif
+       tlist2_del_from(&children, &cousin);
+#endif
+       return 0;
+}
diff --git a/ccan/tlist2/test/compile_fail-tlist_for_each_2.c b/ccan/tlist2/test/compile_fail-tlist_for_each_2.c
new file mode 100644 (file)
index 0000000..776d141
--- /dev/null
@@ -0,0 +1,32 @@
+#include <ccan/tlist2/tlist2.h>
+
+struct child {
+       const char *name;
+       struct list_node list;
+};
+
+struct cousin {
+       const char *name;
+       struct list_node list;
+};
+
+int main(int argc, char *argv[])
+{
+       TLIST2(struct child, list) children;
+       struct child child = { "child" };
+#ifdef FAIL
+#if !HAVE_FLEXIBLE_ARRAY_MEMBER
+#error Need flexible array members to check type
+#endif
+       struct cousin *c;
+#else
+       struct child *c;
+#endif
+
+       tlist2_init(&children);
+       tlist2_add(&children, &child);
+
+       tlist2_for_each(&children, c)
+               (void) c; /* Suppress unused-but-set-variable warning. */
+       return 0;
+}
diff --git a/ccan/tlist2/test/compile_fail-tlist_for_each_safe_2.c b/ccan/tlist2/test/compile_fail-tlist_for_each_safe_2.c
new file mode 100644 (file)
index 0000000..d06f64a
--- /dev/null
@@ -0,0 +1,31 @@
+#include <ccan/tlist2/tlist2.h>
+
+struct child {
+       const char *name;
+       struct list_node list;
+};
+
+struct cousin {
+       const char *name;
+       struct list_node list;
+};
+
+int main(int argc, char *argv[])
+{
+       TLIST2(struct child, list) children;
+       struct child child = { "child" };
+#ifdef FAIL
+#if !HAVE_FLEXIBLE_ARRAY_MEMBER
+#error Need flexible array members to check type
+#endif
+       struct cousin *c, *n;
+#else
+       struct child *c, *n;
+#endif
+
+       tlist2_init(&children);
+       tlist2_add(&children, &child);
+
+       tlist2_for_each_safe(&children, c, n);
+       return 0;
+}
diff --git a/ccan/tlist2/test/compile_fail-tlist_tail_2.c b/ccan/tlist2/test/compile_fail-tlist_tail_2.c
new file mode 100644 (file)
index 0000000..e30e1cd
--- /dev/null
@@ -0,0 +1,29 @@
+#include <ccan/tlist2/tlist2.h>
+
+struct child {
+       const char *name;
+       struct list_node list;
+};
+
+struct cousin {
+       const char *name;
+       struct list_node list;
+};
+
+int main(int argc, char *argv[])
+{
+       TLIST2(struct child, list) children;
+       struct child child = { "child" };
+#ifdef FAIL
+       struct cousin *c;
+#else
+       struct child *c;
+#endif
+
+       tlist2_init(&children);
+       tlist2_add(&children, &child);
+
+       c = tlist2_tail(&children);
+       (void) c; /* Suppress unused-but-set-variable warning. */
+       return 0;
+}
diff --git a/ccan/tlist2/test/compile_fail-tlist_top_2.c b/ccan/tlist2/test/compile_fail-tlist_top_2.c
new file mode 100644 (file)
index 0000000..149dd58
--- /dev/null
@@ -0,0 +1,29 @@
+#include <ccan/tlist2/tlist2.h>
+
+struct child {
+       const char *name;
+       struct list_node list;
+};
+
+struct cousin {
+       const char *name;
+       struct list_node list;
+};
+
+int main(int argc, char *argv[])
+{
+       TLIST2(struct child, list) children;
+       struct child child = { "child" };
+#ifdef FAIL
+       struct cousin *c;
+#else
+       struct child *c;
+#endif
+
+       tlist2_init(&children);
+       tlist2_add(&children, &child);
+
+       c = tlist2_top(&children);
+       (void) c; /* Suppress unused-but-set-variable warning. */
+       return 0;
+}
diff --git a/ccan/tlist2/test/run_2.c b/ccan/tlist2/test/run_2.c
new file mode 100644 (file)
index 0000000..43f1128
--- /dev/null
@@ -0,0 +1,145 @@
+#define CCAN_LIST_DEBUG 1
+#include <ccan/tlist2/tlist2.h>
+#include <ccan/tap/tap.h>
+
+struct child {
+       const char *name;
+       struct list_node list;
+};
+
+struct parent {
+       const char *name;
+       TLIST2(struct child, list) children;
+       unsigned int num_children;
+};
+
+int main(int argc, char *argv[])
+{
+       struct parent parent;
+       struct child c1, c2, c3, *c, *n;
+       unsigned int i;
+       TLIST2(struct child, list) tlist = TLIST2_INIT(tlist);
+
+       plan_tests(48);
+       /* Test TLIST2_INIT, and tlist2_empty */
+       ok1(tlist2_empty(&tlist));
+       ok1(tlist2_check(&tlist, NULL));
+
+       parent.num_children = 0;
+       tlist2_init(&parent.children);
+       /* Test tlist2_init */
+       ok1(tlist2_empty(&parent.children));
+       ok1(tlist2_check(&parent.children, NULL));
+
+       c2.name = "c2";
+       tlist2_add(&parent.children, &c2);
+       /* Test tlist2_add and !tlist2_empty. */
+       ok1(!tlist2_empty(&parent.children));
+       ok1(c2.list.next == &tlist2_unwrap(&parent.children)->n);
+       ok1(c2.list.prev == &tlist2_unwrap(&parent.children)->n);
+       ok1(tlist2_unwrap(&parent.children)->n.next == &c2.list);
+       ok1(tlist2_unwrap(&parent.children)->n.prev == &c2.list);
+       /* Test tlist2_check */
+       ok1(tlist2_check(&parent.children, NULL));
+
+       c1.name = "c1";
+       tlist2_add(&parent.children, &c1);
+       /* Test list_add and !list_empty. */
+       ok1(!tlist2_empty(&parent.children));
+       ok1(c2.list.next == &tlist2_unwrap(&parent.children)->n);
+       ok1(c2.list.prev == &c1.list);
+       ok1(tlist2_unwrap(&parent.children)->n.next == &c1.list);
+       ok1(tlist2_unwrap(&parent.children)->n.prev == &c2.list);
+       ok1(c1.list.next == &c2.list);
+       ok1(c1.list.prev == &tlist2_unwrap(&parent.children)->n);
+       /* Test tlist2_check */
+       ok1(tlist2_check(&parent.children, NULL));
+
+       c3.name = "c3";
+       tlist2_add_tail(&parent.children, &c3);
+       /* Test list_add_tail and !list_empty. */
+       ok1(!tlist2_empty(&parent.children));
+       ok1(tlist2_unwrap(&parent.children)->n.next == &c1.list);
+       ok1(tlist2_unwrap(&parent.children)->n.prev == &c3.list);
+       ok1(c1.list.next == &c2.list);
+       ok1(c1.list.prev == &tlist2_unwrap(&parent.children)->n);
+       ok1(c2.list.next == &c3.list);
+       ok1(c2.list.prev == &c1.list);
+       ok1(c3.list.next == &tlist2_unwrap(&parent.children)->n);
+       ok1(c3.list.prev == &c2.list);
+       /* Test tlist2_check */
+       ok1(tlist2_check(&parent.children, NULL));
+
+       /* Test tlist2_top */
+       ok1(tlist2_top(&parent.children) == &c1);
+
+       /* Test list_tail */
+       ok1(tlist2_tail(&parent.children) == &c3);
+
+       /* Test tlist2_for_each. */
+       i = 0;
+       tlist2_for_each(&parent.children, c) {
+               switch (i++) {
+               case 0:
+                       ok1(c == &c1);
+                       break;
+               case 1:
+                       ok1(c == &c2);
+                       break;
+               case 2:
+                       ok1(c == &c3);
+                       break;
+               }
+               if (i > 2)
+                       break;
+       }
+       ok1(i == 3);
+
+       /* Test tlist2_for_each_rev. */
+       i = 0;
+       tlist2_for_each_rev(&parent.children, c) {
+               switch (i++) {
+               case 0:
+                       ok1(c == &c3);
+                       break;
+               case 1:
+                       ok1(c == &c2);
+                       break;
+               case 2:
+                       ok1(c == &c1);
+                       break;
+               }
+               if (i > 2)
+                       break;
+       }
+       ok1(i == 3);
+
+       /* Test tlist2_for_each_safe, tlist2_del and tlist2_del_from. */
+       i = 0;
+       tlist2_for_each_safe(&parent.children, c, n) {
+               switch (i++) {
+               case 0:
+                       ok1(c == &c1);
+                       tlist2_del_from(&parent.children, c);
+                       break;
+               case 1:
+                       ok1(c == &c2);
+                       tlist2_del_from(&parent.children, c);
+                       break;
+               case 2:
+                       ok1(c == &c3);
+                       tlist2_del_from(&parent.children, c);
+                       break;
+               }
+               ok1(tlist2_check(&parent.children, NULL));
+               if (i > 2)
+                       break;
+       }
+       ok1(i == 3);
+       ok1(tlist2_empty(&parent.children));
+
+       /* Test list_top/list_tail on empty list. */
+       ok1(tlist2_top(&parent.children) == (struct child *)NULL);
+       ok1(tlist2_tail(&parent.children) == (struct child *)NULL);
+       return exit_status();
+}
diff --git a/ccan/tlist2/tlist2.h b/ccan/tlist2/tlist2.h
new file mode 100644 (file)
index 0000000..82f20ec
--- /dev/null
@@ -0,0 +1,242 @@
+/* Licensed under LGPL - see LICENSE file for details */
+#ifndef CCAN_TLIST2_H
+#define CCAN_TLIST2_H
+#include <ccan/list/list.h>
+#include <ccan/tcon/tcon.h>
+
+/**
+ * TLIST2 - declare a typed list type (struct tlist)
+ * @etype: the type the list will contain
+ * @link: the name of the member of @etype that is the link
+ *
+ * This declares an anonymous structure to use for lists containing this type.
+ * The actual list can be accessed using tlist2_raw().
+ *
+ * Example:
+ *     #include <ccan/list/list.h>
+ *     #include <ccan/tlist2/tlist2.h>
+ *     struct child {
+ *             const char *name;
+ *             struct list_node list;
+ *     };
+ *     struct parent {
+ *             const char *name;
+ *             TLIST2(struct child, list) children;
+ *             unsigned int num_children;
+ *     };
+ *
+ */
+#define TLIST2(etype, link)                            \
+       TCON_WRAP(struct list_head,                     \
+               TCON_CONTAINER(canary, etype, link))
+
+/**
+ * TLIST2_INIT - initalizer for an empty tlist
+ * @name: the name of the list.
+ *
+ * Explicit initializer for an empty list.
+ *
+ * See also:
+ *     tlist2_init()
+ *
+ * Example:
+ *     TLIST2(struct child, list) my_list = TLIST2_INIT(my_list);
+ */
+#define TLIST2_INIT(name) TCON_WRAP_INIT( LIST_HEAD_INIT(*tcon_unwrap(&(name))) )
+
+/**
+ * tlist2_check - check head of a list for consistency
+ * @h: the tlist2 head
+ * @abortstr: the location to print on aborting, or NULL.
+ *
+ * Because list_nodes have redundant information, consistency checking between
+ * the back and forward links can be done.  This is useful as a debugging check.
+ * If @abortstr is non-NULL, that will be printed in a diagnostic if the list
+ * is inconsistent, and the function will abort.
+ *
+ * Returns non-NULL if the list is consistent, NULL otherwise (it
+ * can never return NULL if @abortstr is set).
+ *
+ * See also: list_check()
+ *
+ * Example:
+ *     static void dump_parent(struct parent *p)
+ *     {
+ *             struct child *c;
+ *
+ *             printf("%s (%u children):\n", p->name, p->num_children);
+ *             tlist2_check(&p->children, "bad child list");
+ *             tlist2_for_each(&p->children, c)
+ *                     printf(" -> %s\n", c->name);
+ *     }
+ */
+#define tlist2_check(h, abortstr) \
+       list_check(tcon_unwrap(h), (abortstr))
+
+/**
+ * tlist2_init - initialize a tlist
+ * @h: the tlist to set to the empty list
+ *
+ * Example:
+ *     ...
+ *     struct parent *parent = malloc(sizeof(*parent));
+ *
+ *     tlist2_init(&parent->children);
+ *     parent->num_children = 0;
+ */
+#define tlist2_init(h) list_head_init(tcon_unwrap(h))
+
+/**
+ * tlist2_raw - unwrap the typed list and check the type
+ * @h: the tlist
+ * @expr: the expression to check the type against (not evaluated)
+ *
+ * This macro usually causes the compiler to emit a warning if the
+ * variable is of an unexpected type.  It is used internally where we
+ * need to access the raw underlying list.
+ */
+#define tlist2_raw(h, expr) tcon_unwrap(tcon_container_check_ptr(h, canary, expr))
+
+/**
+ * tlist2_unwrap - unwrap the typed list without any checks
+ * @h: the tlist
+ */
+#define tlist2_unwrap(h) tcon_unwrap(h)
+
+/**
+ * tlist2_add - add an entry at the start of a linked list.
+ * @h: the tlist to add the node to
+ * @n: the entry to add to the list.
+ *
+ * The entry's list_node does not need to be initialized; it will be
+ * overwritten.
+ * Example:
+ *     struct child *child = malloc(sizeof(*child));
+ *
+ *     child->name = "marvin";
+ *     tlist2_add(&parent->children, child);
+ *     parent->num_children++;
+ */
+#define tlist2_add(h, n) list_add(tlist2_raw((h), (n)), tcon_member_of(h, canary, n))
+
+/**
+ * tlist2_add_tail - add an entry at the end of a linked list.
+ * @h: the tlist to add the node to
+ * @n: the entry to add to the list.
+ *
+ * The list_node does not need to be initialized; it will be overwritten.
+ * Example:
+ *     tlist2_add_tail(&parent->children, child);
+ *     parent->num_children++;
+ */
+#define tlist2_add_tail(h, n) \
+       list_add_tail(tlist2_raw((h), (n)), tcon_member_of((h), canary, (n)))
+
+/**
+ * tlist2_del_from - delete an entry from a linked list.
+ * @h: the tlist @n is in
+ * @n: the entry to delete
+ *
+ * This explicitly indicates which list a node is expected to be in,
+ * which is better documentation and can catch more bugs.
+ *
+ * Note that this leaves @n->@member in an undefined state; it
+ * can be added to another list, but not deleted again.
+ *
+ * Example:
+ *     tlist2_del_from(&parent->children, child);
+ *     parent->num_children--;
+ */
+#define tlist2_del_from(h, n) \
+       list_del_from(tlist2_raw((h), (n)), tcon_member_of((h), canary, (n)))
+
+/**
+ * tlist2_empty - is a list empty?
+ * @h: the tlist
+ *
+ * If the list is empty, returns true.
+ *
+ * Example:
+ *     assert(tlist2_empty(&parent->children) == (parent->num_children == 0));
+ */
+#define tlist2_empty(h) list_empty(tcon_unwrap(h))
+
+/**
+ * tlist2_top - get the first entry in a list
+ * @h: the tlist
+ *
+ * If the list is empty, returns NULL.
+ *
+ * Example:
+ *     struct child *first;
+ *     first = tlist2_top(&parent->children);
+ *     if (!first)
+ *             printf("Empty list!\n");
+ */
+#define tlist2_top(h) tcon_container_of((h), canary, list_top_(tcon_unwrap(h), 0))
+
+/**
+ * tlist2_tail - get the last entry in a list
+ * @h: the tlist
+ *
+ * If the list is empty, returns NULL.
+ *
+ * Example:
+ *     struct child *last;
+ *     last = tlist2_tail(&parent->children);
+ *     if (!last)
+ *             printf("Empty list!\n");
+ */
+#define tlist2_tail(h) tcon_container_of((h), canary, list_tail_(tcon_unwrap(h), 0))
+
+/**
+ * tlist2_for_each - iterate through a list.
+ * @h: the tlist
+ * @i: an iterator of suitable type for this list.
+ *
+ * This is a convenient wrapper to iterate @i over the entire list.  It's
+ * a for loop, so you can break and continue as normal.
+ *
+ * Example:
+ *     tlist2_for_each(&parent->children, child)
+ *             printf("Name: %s\n", child->name);
+ */
+#define tlist2_for_each(h, i)                                  \
+       list_for_each_off(tlist2_raw((h), (i)), (i), tcon_offset((h), canary))
+
+/**
+ * tlist2_for_each_rev - iterate through a list backwards.
+ * @h: the tlist
+ * @i: an iterator of suitable type for this list.
+ *
+ * This is a convenient wrapper to iterate @i over the entire list.  It's
+ * a for loop, so you can break and continue as normal.
+ *
+ * Example:
+ *     tlist2_for_each_rev(&parent->children, child)
+ *             printf("Name: %s\n", child->name);
+ */
+#define tlist2_for_each_rev(h, i)                                      \
+       list_for_each_rev_off(tlist2_raw((h), (i)), (i), tcon_offset((h), canary))
+
+/**
+ * tlist2_for_each_safe - iterate through a list, maybe during deletion
+ * @h: the tlist
+ * @i: an iterator of suitable type for this list.
+ * @nxt: another iterator to store the next entry.
+ *
+ * This is a convenient wrapper to iterate @i over the entire list.  It's
+ * a for loop, so you can break and continue as normal.  The extra variable
+ * @nxt is used to hold the next element, so you can delete @i from the list.
+ *
+ * Example:
+ *     struct child *next;
+ *     tlist2_for_each_safe(&parent->children, child, next) {
+ *             tlist2_del_from(&parent->children, child);
+ *             parent->num_children--;
+ *     }
+ */
+#define tlist2_for_each_safe(h, i, nxt)                                \
+       list_for_each_safe_off(tlist2_raw((h), (i)), (i), (nxt), tcon_offset((h), canary))
+
+#endif /* CCAN_TLIST2_H */