I'm not sure what you're looking to do, but I think you should start by reviewing the question: should a "node"
be a property of an object (a struct
data type) or should a "node"
be an accessor to a data type...?
Both work and I've used both.
When I need to link existing objects together, than a node will contain a reference data type... but unlike your list, the data is always accessed using a pointer (not containing the actual data type, but only using a reference).
This allows one (object) to many (lists) relationships.
However, many times the data type itself will need to be "chained" (in a single list - one to one relationship), in which case the "node" is a property of the data type and can be re-used in many different types.
A list to link existing types
Here's an example code where I used a linked list to link existing objects using a void
pointer.
I'm not sure this implementation adds anything to your initial concept, but it does show the "modularization" for a "one (objet) to many (lists)" approach.
/* *****************************************************************************
Simple List
***************************************************************************** */
typedef struct fio_ls_s {
struct fio_ls_s *prev;
struct fio_ls_s *next;
void *obj;
} fio_ls_s;
#define FIO_LS_INIT(name) \
{ .next = &(name), .prev = &(name) }
/** Adds an object to the list's head. */
static inline __attribute__((unused)) void fio_ls_push(fio_ls_s *pos,
void *obj) {
/* prepare item */
fio_ls_s *item = (fio_ls_s *)malloc(sizeof(*item));
if (!item)
perror("ERROR: fiobj list couldn't allocate memory"), exit(errno);
*item = (fio_ls_s){.prev = pos, .next = pos->next, .obj = obj};
/* inject item */
pos->next->prev = item;
pos->next = item;
}
/** Adds an object to the list's tail. */
static inline __attribute__((unused)) void fio_ls_unshift(fio_ls_s *pos,
void *obj) {
pos = pos->prev;
fio_ls_push(pos, obj);
}
/** Removes an object from the list's head. */
static inline __attribute__((unused)) void *fio_ls_pop(fio_ls_s *list) {
if (list->next == list)
return NULL;
fio_ls_s *item = list->next;
void *ret = item->obj;
list->next = item->next;
list->next->prev = list;
free(item);
return ret;
}
/** Removes an object from the list's tail. */
static inline __attribute__((unused)) void *fio_ls_shift(fio_ls_s *list) {
if (list->prev == list)
return NULL;
fio_ls_s *item = list->prev;
void *ret = item->obj;
list->prev = item->prev;
list->prev->next = list;
free(item);
return ret;
}
/** Removes an object from the containing node. */
static inline __attribute__((unused)) void *fio_ls_remove(fio_ls_s *node) {
void *ret = node->obj;
node->next->prev = node->prev->next;
node->prev->next = node->next->prev;
free(node);
return ret;
}
A list that is integrated in the data-type
Often I have objects that I know I will link together and that by nature will only belong to a single list ("one to one").
In these cases, placing the node
struct data within the data-type allows better locality and improved performance through a single allocation for both the data and the node information.
A good enough example for such a situation can be examined is this SO answer.