You might take some info from the list.h
header in the Linux kernel. It implements a few support functions and macros for a generic list type that looks like this:
struct list {
struct list *prev, *next;
};
This holds no data, because if we want to make a list of ints, we simply say:
struct intlist {
struct list list;
int data;
};
Because a struct
pointer can safely be casted to the type of it's first element, a struct intlist*
can now be passed to all the macros and functions in list.h
that expect a struct list*
. The macros and functions in list.h
only manipulate the list structure, and can just as safely be used on a struct intlist*
. The same functions and macros would work just as well with a struct strlist*
or a struct stufflist*
.
In fact, the Linux kernel list.h
uses some evil trickery to allow you to place the struct list
field anywhere in your linked list structure you like, but some of that may not be portable beyond GCC. For consistency, readability, and other stuff, I would always put it at the beginning. I can't really think of a good reason to have it at the end.
Some people may consider this a hack. I consider it a well-thought-out lightweight generic list implementation.