You'll need to delegate type-aware operations to a separate function, then attach that function to your list via a function pointer. I've taken the liberty of changing your struct type definitions to something I've used before that I feel is a little more natural. Feel free to disagree.
struct Node {
void *data;
struct Node *prev;
struct Node *next;
};
Unless you intend for the Item
struct type to hold anything other than a void *
, then get rid of it. Abstraction is a Good Thing, but there is such a thing as going overboard. Personally, I think it's cleaner to not create a bunch of typedefs, and I really don't like typedef'ing pointer types (pointer semantics are special and should not be hidden). Of course, YMMV.
struct List {
struct Node *head;
struct Node *tail;
int (*cmp)( const void *, const void *);
};
I've modified your List
type to contain your head and tail pointers, as well as a pointer to a comparison function. You can use this structure to create lists of any type; all you need to do is create a comparison function for that type and attach it to the list. For example, if you want your list to contain integers:
int compareInt( const void *lhs, const void *rhs )
{
const int *llhs = (const int *) lhs;
const int *lhrs = (const int *) rhs;
if ( *llhs < *lrhs )
return -1;
else if ( *llhs > *lrhs )
return 1;
return 0;
}
The comparison function follows the same model as those used by qsort
; it takes two parameters of const void *
and returns an int
value -- -1 if lhs < rhs, 1 if lhs > rhs, and 0 if lhs == rhs.
struct List intList = { NULL, NULL, compareInt };
bool contains( const Item data, struct List *l )
{
bool result = false;
assert( l != NULL && l->cmp != NULL);
struct Node *cur = l->head;
while ( cur != NULL )
{
if ( l->cmp( cur->data, data ) == 0 )
{
result = true;
break;
}
cur = cur->next;
}
return result;
}
So your function will walk through the list and compare values, returning true if it finds a match, false otherwise.
Now, this approach has a lot of drawbacks; it's complex, it's messy, it can be hard to follow, it can involve a lot of memory management, and you lose any pretense of type safety. There's nothing to stop you from associating the wrong function with a list, or mixing up types within the list. But, it does allow you to create "generic" containers that can handle any type, and you can add new types without having to hack the basic container logic. You only need to implement a new comparison function for each type.
Although, to be honest, you should implement not only comparison functions, but assignment, copy, display, and deallocation functions as well, attaching them to the list in the same way, along with a lightweight, type-aware interface for each type. It's more code, but it will save you heartburn in the long run.