I wasn't aware of that approach.
I don't know if this is OT but I'll assume it's just within bounds...
Without knowing how the macros are (typically) defined I can't
understand why there is a three-list limit.
There is no such limit: the number of lists an object can be on
depends only on the number of "list-of" elements within the object.
The example I used (vnodes) merely happens to need to be on three
(or sometimes fewer) lists.
Linux uses a macro not to define the structure member, but to
initialise it. Your example would equate to:
struct vnode {
...
struct list_head v_freelist; /* vnode freelist */
...
struct list_head v_mntvnodes; /* vnodes for mount point */
...
struct list_head v_synclist; /* vnodes with dirty buffers */
...
} vnode_instance;
...
INIT_LIST_HEAD(vnode_instance.v_freelist);
... and for the others ...
Is this any less ugly to you?
Not really
The main problem with the above -- which was also the case with
the CMU list macros from which the BSD ones were initially derived
-- is that they are (obviously) not type-safe. Consider a structure
that heads two *different* kinds of lists. For instance, a struct
describing a chapter in a book might have a list of pages, and also
a list of sections:
struct page;
struct section;
struct chapter {
...
LIST_HEAD(, page) pages;
LIST_HEAD(, section) sections;
...
};
struct page {
...
LIST_ENTRY(page) list;
...
};
struct section {
...
LIST_ENTRY(section) list;
...
};
If you want to iterate through the sections of a chapter, you write:
/* input: ch is the chapter */
struct section *sec;
LIST_FOREACH(sec, ch->sections, list) { ... }
If you accidentally try to iterate "sec" through the "pages":
LIST_FOREACH(sec, ch->pages, list) { ... }
you get a compile-time diagnostic. Of course, if you want to
iterate through the pages, just use:
struct page *pg;
LIST_FOREACH(pg, ch->pages, list) { ... }
If the list head and list entry data structures do not have some
sort of type-name decoration -- the parameters to the BSD sys/queue.h
macros, for instance -- you cannot propagate the element-type into
them (because C is statically typed).
(It is hard to get the various fields mixed up in simple examples
like these, but -- speaking from experience -- it is all too easy
to get them mixed up in real code.)