Commit c89f34af authored by Rusty Russell's avatar Rusty Russell

tlist: typesafe variant of list module.

I chose not to do the "macro creates set of routines" approach, as
we can be almost as safe with a struct containing a pointer to the member
type.
parent 9a83a074
../../licenses/LGPL-3
\ No newline at end of file
#include <stdio.h>
#include <string.h>
#include "config.h"
/**
* tlist - typesafe double linked list routines
*
* 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.
*
* You use TLIST_TYPE() to define the specific struct tlist_<name>, then use
* the tlist_* variants of the various list_* operations.
*
* Example:
* #include <err.h>
* #include <stdio.h>
* #include <stdlib.h>
* #include <ccan/tlist/tlist.h>
*
* // Defines struct tlist_children
* TLIST_TYPE(children, struct child);
* struct parent {
* const char *name;
* struct tlist_children children;
* unsigned int num_children;
* };
*
* struct child {
* const char *name;
* struct list_node list;
* };
*
* 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];
* tlist_init(&p.children);
* for (i = 2; i < argc; i++) {
* c = malloc(sizeof(*c));
* c->name = argv[i];
* tlist_add(&p.children, c, list);
* p.num_children++;
* }
*
* printf("%s has %u children:", p.name, p.num_children);
* tlist_for_each(&p.children, c, list)
* printf("%s ", c->name);
* printf("\n");
* return 0;
* }
*
* License: LGPL
* Author: Rusty Russell <rusty@rustcorp.com.au>
*/
int main(int argc, char *argv[])
{
if (argc != 2)
return 1;
if (strcmp(argv[1], "depends") == 0) {
printf("ccan/list\n");
return 0;
}
return 1;
}
#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 tlist_children children;
struct tlist_cousins cousins;
struct child child = { "child" };
struct cousin cousin = { "cousin" };
tlist_init(&children);
tlist_init(&cousins);
tlist_add(&children, &child, list);
tlist_add(&cousins, &cousin, list);
tlist_del_from(&cousins, &cousin, list);
#ifdef FAIL
#if !HAVE_FLEXIBLE_ARRAY_MEMBER
#error Need flexible array members to check type
#endif
tlist_add(&children, &cousin, list);
#endif
return 0;
}
#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 tlist_children children;
struct tlist_cousins cousins;
struct child child = { "child" };
struct cousin cousin = { "cousin" };
tlist_init(&children);
tlist_init(&cousins);
tlist_add(&children, &child, list);
tlist_add(&cousins, &cousin, list);
tlist_del_from(&cousins, &cousin, list);
#ifdef FAIL
#if !HAVE_FLEXIBLE_ARRAY_MEMBER
#error Need flexible array members to check type
#endif
tlist_add_tail(&children, &cousin, list);
#endif
return 0;
}
#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 tlist_children children;
struct tlist_cousins cousins;
struct child child = { "child" };
struct cousin cousin = { "cousin" };
tlist_init(&children);
tlist_init(&cousins);
tlist_add(&children, &child, list);
tlist_add(&cousins, &cousin, list);
#ifdef FAIL
#if !HAVE_FLEXIBLE_ARRAY_MEMBER
#error Need flexible array members to check type
#endif
tlist_del_from(&children, &cousin, list);
#endif
return 0;
}
#include <ccan/tlist/tlist.h>
TLIST_TYPE(children, struct child);
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 tlist_children 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
tlist_init(&children);
tlist_add(&children, &child, list);
tlist_for_each(&children, c, list);
return 0;
}
#include <ccan/tlist/tlist.h>
TLIST_TYPE(children, struct child);
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 tlist_children 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
tlist_init(&children);
tlist_add(&children, &child, list);
tlist_for_each_safe(&children, c, n, list);
return 0;
}
#include <ccan/tlist/tlist.h>
TLIST_TYPE(children, struct child);
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 tlist_children children;
struct child child = { "child" };
void *c;
tlist_init(&children);
tlist_add(&children, &child, list);
c = tlist_tail(&children,
#ifdef FAIL
#if !HAVE_FLEXIBLE_ARRAY_MEMBER
#error Need flexible array members to check type
#endif
struct cousin,
#else
struct child,
#endif
list);
return 0;
}
#include <ccan/tlist/tlist.h>
TLIST_TYPE(children, struct child);
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 tlist_children children;
struct child child = { "child" };
void *c;
tlist_init(&children);
tlist_add(&children, &child, list);
c = tlist_top(&children,
#ifdef FAIL
#if !HAVE_FLEXIBLE_ARRAY_MEMBER
#error Need flexible array members to check type
#endif
struct cousin,
#else
struct child,
#endif
list);
return 0;
}
#define CCAN_LIST_DEBUG 1
#include <ccan/tlist/tlist.h>
#include <ccan/tap/tap.h>
TLIST_TYPE(children, struct child);
struct parent {
const char *name;
struct tlist_children children;
unsigned int num_children;
};
struct child {
const char *name;
struct list_node list;
};
int main(int argc, char *argv[])
{
struct parent parent;
struct child c1, c2, c3, *c, *n;
unsigned int i;
struct tlist_children tlist = TLIST_INIT(tlist);
plan_tests(44);
/* Test TLIST_INIT, and tlist_empty */
ok1(tlist_empty(&tlist));
ok1(tlist_check(&tlist, NULL));
parent.num_children = 0;
tlist_init(&parent.children);
/* Test tlist_init */
ok1(tlist_empty(&parent.children));
ok1(tlist_check(&parent.children, NULL));
c2.name = "c2";
tlist_add(&parent.children, &c2, list);
/* Test tlist_add and !tlist_empty. */
ok1(!tlist_empty(&parent.children));
ok1(c2.list.next == &parent.children.raw.n);
ok1(c2.list.prev == &parent.children.raw.n);
ok1(parent.children.raw.n.next == &c2.list);
ok1(parent.children.raw.n.prev == &c2.list);
/* Test tlist_check */
ok1(tlist_check(&parent.children, NULL));
c1.name = "c1";
tlist_add(&parent.children, &c1, list);
/* Test list_add and !list_empty. */
ok1(!tlist_empty(&parent.children));
ok1(c2.list.next == &parent.children.raw.n);
ok1(c2.list.prev == &c1.list);
ok1(parent.children.raw.n.next == &c1.list);
ok1(parent.children.raw.n.prev == &c2.list);
ok1(c1.list.next == &c2.list);
ok1(c1.list.prev == &parent.children.raw.n);
/* Test tlist_check */
ok1(tlist_check(&parent.children, NULL));
c3.name = "c3";
tlist_add_tail(&parent.children, &c3, list);
/* Test list_add_tail and !list_empty. */
ok1(!tlist_empty(&parent.children));
ok1(parent.children.raw.n.next == &c1.list);
ok1(parent.children.raw.n.prev == &c3.list);
ok1(c1.list.next == &c2.list);
ok1(c1.list.prev == &parent.children.raw.n);
ok1(c2.list.next == &c3.list);
ok1(c2.list.prev == &c1.list);
ok1(c3.list.next == &parent.children.raw.n);
ok1(c3.list.prev == &c2.list);
/* Test tlist_check */
ok1(tlist_check(&parent.children, NULL));
/* Test tlist_top */
ok1(tlist_top(&parent.children, struct child, list) == &c1);
/* Test list_tail */
ok1(tlist_tail(&parent.children, struct child, list) == &c3);
/* Test tlist_for_each. */
i = 0;
tlist_for_each(&parent.children, c, list) {
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 tlist_for_each_safe, tlist_del and tlist_del_from. */
i = 0;
tlist_for_each_safe(&parent.children, c, n, list) {
switch (i++) {
case 0:
ok1(c == &c1);
tlist_del(c, list);
break;
case 1:
ok1(c == &c2);
tlist_del_from(&parent.children, c, list);
break;
case 2:
ok1(c == &c3);
tlist_del_from(&parent.children, c, list);
break;
}
ok1(tlist_check(&parent.children, NULL));
if (i > 2)
break;
}
ok1(i == 3);
ok1(tlist_empty(&parent.children));
/* Test list_top/list_tail on empty list. */
ok1(tlist_top(&parent.children, struct child, list) == NULL);
ok1(tlist_tail(&parent.children, struct child, list) == NULL);
return exit_status();
}
#ifndef CCAN_TLIST_H
#define CCAN_TLIST_H
#include <ccan/list/list.h>
#if HAVE_FLEXIBLE_ARRAY_MEMBER
/**
* TLIST_TYPE - declare a typed list type (struct tlist)
* @suffix: the name to use (struct tlist_@suffix)
* @type: the type the list will contain (void for any type)
*
* This declares a structure "struct tlist_@suffix" to use for
* lists containing this type. The actual list can be accessed using
* ".raw" or tlist_raw().
*
* Example:
* // Defines struct tlist_children
* TLIST_TYPE(children, struct child);
* struct parent {
* const char *name;
* struct tlist_children children;
* unsigned int num_children;
* };
*
* struct child {
* const char *name;
* struct list_node list;
* };
*/
#define TLIST_TYPE(suffix, type) \
struct tlist_##suffix { \
struct list_head raw; \
const type *tcheck[]; \
}
/**
* tlist_raw - access the raw list inside a typed list head.
* @h: the head of the typed list (struct tlist_@suffix)
* @test_var: a pointer to the expected element type.
*
* This elaborate 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 tlist_raw(h, test_var) \
(sizeof((h)->tcheck[0] == (test_var)) ? &(h)->raw : &(h)->raw)
#else
#define TLIST_TYPE(suffix, type) \
struct tlist_##suffix { \
struct list_head raw; \
}
#define tlist_raw(h, test_var) (&(h)->raw)
#endif
/**
* TLIST_INIT - initalizer for an empty tlist
* @name: the name of the list.
*
* Explicit initializer for an empty list.
*
* See also:
* tlist_init()
*
* Example:
* static struct tlist_children my_list = TLIST_INIT(my_list);
*/
#define TLIST_INIT(name) { LIST_HEAD_INIT(name.raw) }
/**
* tlist_check - check head of a list for consistency
* @h: the tlist_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);
* tlist_check(&p->children, "bad child list");
* tlist_for_each(&p->children, c, list)
* printf(" -> %s\n", c->name);
* }
*/
#define tlist_check(h, abortstr) \
list_check(&(h)->raw, (abortstr))
/**
* tlist_init - initialize a tlist
* @h: the tlist to set to the empty list
*
* Example:
* ...
* struct parent *parent = malloc(sizeof(*parent));
*
* tlist_init(&parent->children);
* parent->num_children = 0;
*/
#define tlist_init(h) list_head_init(&(h)->raw)
/**
* tlist_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.
* @member: the member of n 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";
* tlist_add(&parent->children, child, list);
* parent->num_children++;
*/
#define tlist_add(h, n, member) list_add(tlist_raw((h), (n)), &(n)->member)
/**
* tlist_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.
* @member: the member of n to add to the list.
*
* The list_node does not need to be initialized; it will be overwritten.
* Example:
* tlist_add_tail(&parent->children, child, list);
* parent->num_children++;
*/
#define tlist_add_tail(h, n, member) \
list_add_tail(tlist_raw((h), (n)), &(n)->member)
/**
* tlist_del_from - delete an entry from a linked list.
* @h: the tlist @n is in
* @n: the entry to delete
* @member: the member of n to remove from the list.
*
* 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.
*
* See also: tlist_del()
*
* Example:
* tlist_del_from(&parent->children, child, list);
* parent->num_children--;
*/
#define tlist_del_from(h, n, member) \
list_del_from(tlist_raw((h), (n)), &(n)->member)
/**
* tlist_del - delete an entry from an unknown linked list.
* @n: the entry to delete from the list.
* @member: the member of @n which is in the list.
*
* Example:
* tlist_del(child, list);
* parent->num_children--;
*/
#define tlist_del(n, member) \
list_del(&(n)->member)
/**
* tlist_empty - is a list empty?
* @h: the tlist
*
* If the list is empty, returns true.
*
* Example:
* assert(tlist_empty(&parent->children) == (parent->num_children == 0));
*/
#define tlist_empty(h) list_empty(&(h)->raw)
/**
* tlist_top - get the first entry in a list
* @h: the tlist
* @type: the type of the entry
* @member: the list_node member of the type
*
* If the list is empty, returns NULL.
*
* Example:
* struct child *first;
* first = tlist_top(&parent->children, struct child, list);
*/
#define tlist_top(h, type, member) \
list_top(tlist_raw((h), (type *)NULL), type, member)
/**
* tlist_tail - get the last entry in a list
* @h: the tlist
* @type: the type of the entry
* @member: the list_node member of the type
*
* If the list is empty, returns NULL.
*
* Example:
* struct child *last;
* last = tlist_tail(&parent->children, struct child, list);
*/
#define tlist_tail(h, type, member) \
list_tail(tlist_raw((h), (type *)NULL), type, member)
/**
* tlist_for_each - iterate through a list.
* @h: the tlist
* @i: an iterator of suitable type for this list.
* @member: the list_node member of @i
*
* 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:
* tlist_for_each(&parent->children, child, list)
* printf("Name: %s\n", child->name);
*/
#define tlist_for_each(h, i, member) \
list_for_each(tlist_raw((h), (i)), (i), member)
/**
* tlist_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.
* @member: the list_node member of the structure
*
* 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;
* tlist_for_each_safe(&parent->children, child, next, list) {
* tlist_del(child, list);
* parent->num_children--;
* }
*/
#define tlist_for_each_safe(h, i, nxt, member) \
list_for_each_safe(tlist_raw((h), (i)), (i), (nxt), member)
#endif /* CCAN_TLIST_H */
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment