This is primarily based on some combination of opinion, experience and the specific requirements at hand.
The following approach is possible, inspired by some container library work by Jacob Navia. I've never used it myself:
struct container_node {
struct container_node *link_here, *link_there, *link_elsewhere;
/*...*/
char data[0]; /* C90 style of "flexible array member" */
};
struct container_node *container_node_alloc(size_t data_size);
The allocation function allocates the node large enough so that data[0]
through data[data_size-1]
bytes of storage are available. Through another set of API functions, user data of arbitrary type be copied in and out.
The following approach is sometimes called "intrusive container". The container defines only a "base class" consisting of the link structure. The user must embed this structure into their own structure:
struct container_node {
struct container_node *next, *prev;
};
void container_insert(struct container *container, struct container_node *n);
struct container_node *container_first(struct container *container);
The user does this:
struct my_widget {
struct container_node container_links;
int widget_height;
/* ... */
};
/* .... */
/* We don't insert my_widget, but rather its links base. */
container_insert(&widg_container, &widget->container_links);
Some macros are used to convert between a pointer to the widget and a pointer to the container links. See the container_of
macro used widely in the Linux kernel:
struct my_widget *wptr = container_of(container_first(&widg_container),
struct my_widget, container_links);
See this question.
Then there approaches of storing a union
in each node, which provides an integer, floating-point-value or a pointer. In that case, the data is separately allocated (though not necessarily: if the caller controls the allocation of the nodes, it's still possible to put the node structure and the user data in a buffer that came from a single malloc
call).
Finally, there are also approaches which wrap these techniques with preprocessor templating, an example of which are the BSD QUEUE macros.