Я хотел бы предложить другой подход. Вместо создания нескольких типов списков для разных типов данных и использования приведения или макросимволизации для применения к ним одного и того же алгоритма, создайте единый общий тип списка, который делегирует специфичное для типа поведение различным функциям, и присоедините эти функции к типу списка с функцией указатели. Например:
struct generic_node {
void *data;
struct generic_node *next;
};
struct generic_list {
struct generic_node head;
int (*cmp)(void * const a, void * const b);
void *(*cpy)(void * const);
void (*del)(void *);
};
cmp указывает на функцию, которая будет возвращать -1, если * a <* b, 0, если * a == * b, и 1, если * a> * b, где a и b были преобразованы из void * в правильные типы указателей. Например,
int compareInts(void * const a, void * const b)
{
int * const la = a;
int * const lb = b;
if (*a < *b) return -1;
if (*a == *b) return 0;
if (*a > *b) return 1;
}
int compareMyStruct(void * const a, void * const b)
{
struct myStruct * const la = a;
struct myStruct * const lb = b;
if (la->foo < lb->foo && strcmp(la->bar,lb->bar) < 0 && ...) return -1;
if (la->foo == lb->foo && strcmp(la->bar,lb->bar) == 0 && ...) return 0;
if (la->foo > lb->foo && strcmp(la->bar, lb->bar) > 0 && ...) return 1;
}
cpy указывает на функцию, которая делает глубокую копию входного параметра:
void *copyInt(void * const data)
{
int *theCopy = malloc(sizeof *theCopy);
*theCopy = *((int *) data);
return theCopy;
}
void *copyMyStruct(void * const data)
{
struct myStruct * const lData = data;
struct myStruct *newStruct = malloc(sizeof *newStruct);
newStruct->foo = lData->foo;
newStruct->bar = malloc(strlen(lData->bar) + 1);
strcpy(newStruct->bar, lData->bar);
...
return newStruct;
}
И, наконец, del указывает на функцию, которая освобождает элементы данных:
void delInt(void * data)
{
free(data);
}
void delMyStruct(void * data)
{
struct myStruct * lData = data;
free(lData->bar);
...
free(lData);
}
Теперь вашим алгоритмам списков не нужно беспокоиться о поведении конкретного типа; они просто вызывают соответствующую функцию через указатель функции:
void listAdd(struct generic_list * const theList, void * const data)
{
struct generic_node *cur = &(theList->head);
struct generic_node *entry = malloc(sizeof *entry);
entry->data = theList->cpy(data);
while (cur->next != NULL && theList->cmp(cur->next->data, entry->data) < 0)
cur = cur->next;
entry->next = cur->next;
cur->next = entry;
}
/** */
void listClear(struct generic_list * const theList)
{
struct generic_node *cur = theList->head.next;
while (cur != NULL)
{
struct generic_node *entry = cur;
cur = cur->next;
theList->del(entry->data);
free(entry);
}
}