At the moment I am working on creating a small library for common data structures in C. This is mainly for learning purposes, but I do plan to use this library in other projects to see how well it works and where the problems are. So far I'm content with my hash and binary tree implementation, but I cannot decide on the design of the linked list.
All the data structures implemented so far work with void pointers and take no responsibility for creating or destroying data, i.e. they only reference the data. One design goal is to keep them as generic as possible to increase the reuseability.
Regarding the linked list, I've found three approaches so far:
Dedicated list head: the list features a dedicated list head which is used as an abstract data type.
Nodes only: like the example above, except that all functions operate on a
list_node. Used in GLib.In payload: add a
list_nodestructure to the payload data and calculate the offset to the payload with a macro. See lists in the linux kernelEDIT Generate typed lists with macros: use macros to create type-specific versions of the list structures and functions.
Example for 1 and 2:
/* list.h */
typedef struct list_t list;
typedef int (*comparator)(const void* a, const void* b);
list* list_new (comparator c);
void list_delete (list* l);
void list_insert_after (list* l, uint32_t index, void* data);
void list_remove (list* l, void* data);
uint32_t list_size (list* l);
/* other functions operating on lists */
/* list.c */
#include "list.h"
typedef struct list_node_t {
struct list_node_t* next;
struct list_node_t* prev;
void* data;
} list_node;
struct list_t {
list_node* begin;
list_node* end;
uint32_t size;
comparator cmp;
}
Now to the question: which of these approaches is the most versatile? Are there any other approaches?
size_tforindex,size, etc, rather thanuint32_t.list_removeandlist_insert_afterin the example above, because those two functions would find the element in question by comparing it to whatever is there where the data pointer points to.