I'm trying to create a generic list that will allow any type to be entered. However, I am having problems with the comparism in the is_element_of function (since I am making use of void pointers). Any help?
typedef struct Item{
void* data;
} Item;
typedef struct Node{
Item Item;
struct Node* next;
struct Node* previous;
} Node;
typedef Node* List;
bool is_element_of(Item Item, List *pointertolist) {
bool isinlist = false;
Node *scan = *pointertolist;
while (scan->next != NULL) {
if ((scan->Item.data) == (Item.data)) {
printf("Match!");
isinlist = true;
} else {
printf("No Match!");
isinlist = false;
}
scan = scan->next;
}
return isinlist;
}
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.
You should set isInList to false at the start and only mark it to true when you find a match. Then terminate the loop:
typedef struct Item
{
void* data;
} Item;
typedef struct Node
{
Item Item;
struct Node* next;
struct Node* previous;
} Node;
typedef Node* List;
bool is_element_of(Item Item, List *pointertolist)
{
bool isInList = false;
Node *scan = *pointertolist;
while (scan != NULL && !isInList)
{
if ((scan->Item.data) == (Item.data))
{
printf("Match!\n");
isInList = true;
}
scan = scan->next;
}
if(!isInList)
printf("No Match!\n");
return isInList;
}
Test Function:
void testit()
{
Node n1;
Node n2;
Node n3;
Item item;
n1.Item.data = (void*)0x23;
n2.Item.data = (void*)0x24;
n3.Item.data = (void*)0x25;
n1.next = &n2;
n2.next = &n3;
n3.next = NULL;
List list = &n1;
item.data = (void*)0x23;
is_element_of(item, &list);
item.data = (void*)0x24;
is_element_of(item, &list);
item.data = (void*)0x25;
is_element_of(item, &list);
item.data = (void*)0x26;
is_element_of(item, &list);
}
Output Result:
Match!
Match!
Match!
No Match!
You cannot de-reference a void* and compare the value because it could be of any size (ex: how would you compare a int and a short?
Without more knowledge of you data (yes, its always about the input data ;-), its hard to give a more definite answer. But here are two trails you may pursue...
[edit] I assume that your list may contain data of any type within the same list. People often use void * because they want to link different types together.
The short (bad) answer
you have to choose between comparing values, or comparing pointers. if you don't mind comparing only pointers then you could simply set your Item by giving it the address of your data:
node.Item.data = &myval;
in which case you can see if you've already added this location in ram to your nodes. But this will not allow you to compare the same value in two different locations in your app. Thus if x and y in the following have the same value you wouldn't be able to compare that you have two nodes which point to 1.
x = 1;
y = 1;
node.Item.data = &x;
node2.Item.data = &y;
Furthermore, if you added any node which was allocated on the stack, you will quickly shovel yourself into a grave (as nodes may eventually refer to addresses which are not valid on the stack anymore)!
The longer (better) answer
When I have a generic accumulator system like this, instead of using a void *, I use a union.
enum {
tp_int = 0x0001,
tp_short = 0x0002,
tp_vptr = 0x0004, // void pointer
tp_sptr = 0x0008 // string pointer (whatever type you use
// for your strings... ? char *)
// ... add other types, including structs you may want to compare...
};
typedef struct Item {
int type; // <- essential!
union data {
int i;
short s;
void *ptr; // all pointer types can use the same, unless you want to compare
// compound values (a struct member?) where you'd then use the
// tp_xxx to properly select which
// comparison to use ... as below.
};
} Item;
typedef struct Node{
Item Item;
struct Node* next;
struct Node* previous;
} Node;
typedef Node* List;
bool is_element_of(Item Item, List *pointertolist) {
bool isinlist = false;
Node *scan = *pointertolist;
while (scan->next != NULL) {
//isinlist = false;
if (scan->Item.type == Item.type){
if (Item.type & (tp_vptr | tp_sptr)){
// compare pointer types
if ((scan->Item.ptr) == (Item.ptr)){
printf("pointer Match!");
isinlist = true;
break;
}
} else if (Item.type == tp_int){
// compare integers (4 bytes?)
if ((void *)(scan->Item.i) == (void *)(Item.i)){
printf("integer Match!");
isinlist = true;
break;
}
} else if (Item.type == tp_short){
// compare shorts (2 bytes?)
if ((scan->Item.s) == (Item.s)){
printf("short Match!");
isinlist = true;
break;
}
}
}
scan = scan->next;
}
return isinlist;
}
Note the above code may have one or two odd errors in it, I am not set up to compile it at the moment.
Here you can select which comparison to use. It also allows you to properly and safely use value types (like ints and shorts).
if you had more complex structs, you can easily compare them using their data instead of their pointers, so you could see if you had equivalent data.
You could even extend the above to check for similar strings, even if they have different memory locations. (when Item.type is tp_sptr :-)
Related
I'm currently doing an assignment for uni and I need to find the sum of a graph.
To do this I believe I need a linked list that I can use to remember which nodes have been visited. I have the linkedlist working correctly but I can't get a contains function to work. This is the code I have:
struct listnode
{
struct N *val;
struct listnode *next;
};
int contains(struct listnode *head,struct N* value)
{
struct listnode *current = head;
while (current)
{
if ((current -> val) == value)
{
return 1;
}
current = current -> next;
}
return 0;
}
note: N is a node of the graph.
Can anyone see any problems with what I'm doing?
EDIT: contains function should return 1 when N *value is in the list, 0 otherwise
EDIT2:
I have a push function:
void push(struct listnode *head,struct N *value)
{
if (head)
{
struct listnode *current = head;
while (current->next)
{
current = current -> next;
}
current->next = malloc(sizeof(struct listnode*));
current->next->val = value;
current->next->next = NULL;
}
else
{
head = malloc(sizeof(struct listnode*));
if (head)
{
head -> val = value;
head -> next = NULL;
}
else
{
printf("error");
exit(0);
}
}
}
and I want the following line to return 1:
contains(push(visited,p),p);
where p is a pointer to a struct N and visited is my global linked list
EDIT3:
this is my final sum function that I believe should work, but doesnt because of contains.
long sum(struct N *p)
{
if (p)
{
if (contains(visited,p) == 0) //if p hasnt been visited
{
push(visited,p); //make it visited
return (p -> data) + sum(p -> x) + sum(p -> y) + sum(p -> z);
}
else
{
return 0;
}
}
else
{
return 0;
}
}
Your contains function appears to be fine. The issue is that you are always passing a NULL list to it, which is caused by a faulty push function. You need a return in push, or to pass in a pointer with one more level of indirection, so you can assign to head outside of push. One more possible improvement is to notice that no matter what you pass in, the malloc and initialization of a new node is actually the same.
Finally, the main issue, that is really the most likely to cause a segfault is the fact that you are allocating enough space for a pointer to a node, not for the node itself.
Here is an example:
#ifdef BY_INDIRECTION
#define RET_TYPE void
#define IN_TYPE struct listnode **
#else
#define RET_TYPE struct listnode *
#define IN_TYPE struct listnode *
#endif
RET_TYPE push(IN_TYPE head, struct N *value)
{
struct listnode *current, **next;
if(head)
{
for(current = head; current->next; current = current->next) ;
next = &(current->next);
}
else
{
#ifdef BY_INDIRECTION
next = head;
#else
next = &head;
#endif
}
*next = malloc(sizeof(struct listnode));
if(!*next) {
printf("error");
exit(0);
}
(*next)->val = value;
(*next)->next = NULL;
#ifndef BY_INDIRECTION
return head
#endif
}
I have included both suggestions here. If you want to read the one where we use indirection (pass in a listnode ** and have void return), choose the path where BY_INDIRECTION is defined. If you want to have head returned (and pass in just a regular listnode *) read the path where BY_INDIRECTION is not defined.
The latter approach has a return value, so it can be used to write a shortened form like if(contains(push(head, value), value)) { ... }. The former approach does not, so you would have to do
push(&head, value);
if(contains(head, value)) { ... }
I would recommend using the indirect approach regardless because there are very few instances that you would want to check for containment after putting in a value.
This comparison:
if ((current -> val) == value)
it's comparing pointers. If you call your contains() function this way...
...
struct N val_to_find;
...
result = contains (list, &val_to_find);
You will never find the value, even if the contents of val_to_find are the same as the contents of any struct whose pointer is stored in the list.
If your intention for contains() is to find nodes that have the same data, and not just the same pointers, I'd suggest you something like this:
if (struct_n_comparing_function (current -> val, value) == EQUAL) ...
Where struct_n_comparing_function should have the following prototype:
int struct_n_comparing_function (struct N *a, struct N *b);
which compares the contents of the two structs pointed by a and b and return EQUAL if all the fields of the struct pointed by a have the same value as the fields of struct pointed by b.
I'm trying to type a function that takes 2 linked list. One has the values to be printed and the second has positions for the linked list values to be printed. It gives me an error that i put as comment in the code.
Structs
typedef int Item;
typedef struct node_struct * link;
typedef struct list_struct * list;
struct node_struct {
Item item;
link next;
};
struct list_struct {
link first;
int length;
};
Function:
list sublist(list A, list pos_list) {
link tempOne;
link tempTwo;
link node = malloc(sizeof *node);
tempOne = pos_list->first;
tempTwo = A->first;
int counter;
while(tempOne->next != NULL)
{
counter = 0;
while(counter < tempOne->item && tempOne->next != NULL)
{
tempTwo = tempTwo->next;
counter = counter+1;
}
node->item = tempTwo->item; //EXC_BAD_ACCESS code:1
node = node->next;
tempTwo = A->first;
tempOne = tempOne->next;
counter = 0;
}
return node;
There are bunch of bad practices in the code which makes understanding (and hence debugging and maintaining) such code very difficult for you and for us.
You are creating a pointer typdef when there is no intention to hide the actual data behind the pointer
You are creating a linked list of positions and a linked list of data, using the same data type. I understand in your case both are int, but then don't use the misleading typedef int Item and simply stick to using int
tempOne and tempTwo are probably the worst naming options in this case, not only for calling the variables with non-intuitive names like temp, but also calling the first arg as Two and second arg as One - as counter-intuitive as it can get
I can see cases where you use 2 different structures node_struct (which frankly I would call node) and list_struct see node_structcomment), but in this example, you don't need list_struct, it only adds more confusion to the code.
You should really do the "find" job (the inner for loop)in a separate function, so you can easily handle errors, and not confuse the inner loop with the outer loop
With that out of the way, You haven't specified if the pos_list actually contains relative positions (position from previous position) or absolute positions (like array index). I will assume it is absolute position.
after you do node = node->next; you need to malloc it again. Or rather just malloc it before using it on line node->item = tempTwo->item; and get rid of the malloc out side the loops
I don't have a c compiler handy, so couldn't test it. But I don't see any other issues
EDIT
I noticed that the return value for sublist is always just the last node, instead of the first node in the linked list - this is obviously going to be a problem too.
Below is how I would write this code. Remember, this is not a debugged and tested code, but mere expression of the idea (first draft if you will)
typedef struct Node_ Node;
struct Node_ {
int Item;
Node* Next;
};
Node* GetNodeAt(Node *dataList, int indx) {
for (int i = 0; i < indx && dataList != NULL; ++i)
dataList = dataList->Next;
return dataList;
}
Node* SubList(Node *dataList, Node *posList) {
Node* origDataList = dataList;
Node *currentRetNode = malloc(sizeof(Node));
Node *prevRetNode = NULL, *returnList = currentRetNode;
while (posList->Next != NULL) {
// Find the node in dataList
Node *node = GetNodeAt(dataList, posList->Item);
// create/manage the linked list to be returned
prevRetNode = currentRetNode;
currentRetNode->Next = malloc(sizeof(Node));
currentRetNode->Item = node->Item;
currentRetNode = currentRetNode->Next;
posList = posList->Next; // move to the next index
}
free(currentRetNode);
if (prevRetNode == NULL)
returnList = NULL;
else
prevRetNode->Next = NULL;
return returnList;
}
I am writing a generic function to create a linked list for structs.
Where I fall apart is in looping through the list to find where the
new node should go, as I'm unsure how to determine which struct type is
being used from within the function.
Am I able to use some if to determine the struct type?
Like
if(ID[0]==?? given that the IDs are common to both structs, but
the first char will determine struct type. I am thinking there must
be another way to determine the type using the type being passed to the function.
Sorry if this seems basic and I've overlooked something obvious.
Any thoughts would be appreciated.
typedef struct category* CategoryTypePtr;
typedef struct item* ItemTypePtr;
/*these structs have more members, but not relevant for this*/
typedef struct item
{
char itemID[ID_LEN + 1];
ItemTypePtr nextItem;
} ItemType;
typedef struct category
{
char categoryID[ID_LEN + 1];
CategoryTypePtr nextCategory;
ItemTypePtr headItem;
unsigned numItems;
} CategoryType;
typedef union types{
CategoryType cat;
ItemType item;
} Types;
int addNode(Types *type, char *str)
{
Types *new=NULL;
Types *current=NULL;
Types *prev = NULL;
Types *head=NULL;
char *ID;
const char* s ="|";
if((new=malloc(sizeof(Types)))== NULL)
{
fprintf(stderr,"Memory Allocation failure!!\n");
return false;
}
/*get ID from first str token this is uniform to both*/
ID=strtok(str,s);
current = head;
/* Search to find where in insert new list node*/
/*WHERE <XXXX> needs to be replace by cat or item, depending on which type it is*/
while (current != NULL && strcmp(current-><XXXX>->ID, ID)/*<<<---this is where I fall down
the XXXX represents cat or item*/
{
prev = current;
current = current->next;
}
/**
code to populate struct
a function that would be called
depending on Types type
*/
if (prev == NULL)
{
head = new;
}
else
{
prev->next = new;
}
return true;
}
Unlike some other languages, C doesn't have a built-in way to identify object types at runtime. Your method of putting an identifying character at the start of the structure is as good as any other.
in my assignment i have to create a binary tree where the user inputs the details.
the first thing the user does is enter 1 if they want to create a number tree or 2 if they want a word tree.
the type of tree they pick is the type it will be for the duration of the running of the program.
there are many functions (and a few structs) that must be written in order to complete the assignment.
my question is how can i write general functions that will work for both int and char?
for example if it is a number tree then the struct for node would include:
int key;
list_t* valueslist;
node* left;
node* right;
but if it was a word list than the struct would look the same except instead of int key it would be char key.
thanks in advance for any help!
The way you may go about it, is to define that data in the struct as a union like so:
struct _Node
{
...
union
{
char* c;
int i;
} data;
};
Than when user makes the choice, access the correct union member according to it.
EDIT
So, let's say the user picked a type, int for instance. And you wish to insert a new value into the tree. (I'll omit error checking fro brevity, but remember to check memory allocation succeeded).
struct _Node* newElem = allocNode();
if (get_user_elected_type() == INT)
newElem->data.i = user_input.i; // Your methods will also need to accept a union
This way has it's serious drawbacks (it's not easy to add a new type, for instance). And most of all it demonstrates how yucky generic programming can be in C. (Using void* can get just as yucky eventually).
There are few solutions to resolve this problem (what you are trying to do is called generic programming)
Use void * key, and fill it with the right data (this is
recommended, because is more generic, but it is also more complicated)
Use a union with 2 fields: an int and a char*
For a homework assignment, the simpler approach will be to use a union type for your data:
struct node {
union {
char *s
int i;
} data;
struct node *left;
struct node *right;
};
and create two sets of functions, one to manage integer values and the other to manage string values:
void insertIntNode(struct node *root, struct node *newNode)
{
if (newNode->data.i < root->data.i)
if (root->left != NULL)
insertIntNode(root->left, newNode);
else
root->left = newNode;
else
if (root->right != NULL)
insertIntNode(root->right, newNode);
else
root->right = newNode;
}
void insertWordNode(struct node *root, struct node *newNode)
{
if (strcmp(root->data.s, newNode->data.s) < 0)
if (root->left != NULL)
insertWordNode(root->left, newNode);
else
root->left = newNode;
else
if (root->right != NULL)
insertWordNode(root->right, newNode);
else
root->right = newNode;
}
bearing in mind you'll need to do some additional memory management for word data:
struct node *createWordNode(char *str)
{
struct node *r = malloc(sizeof *r);
if (r)
{
r->data.s = malloc(strlen(str) + 1);
if (r->s)
strcpy(r->data.s, str);
r->left = r->right = NULL;
}
return r;
}
void destroyWordNode(struct node **n)
{
free((*n)->data.s);
free(*n);
*n = NULL;
}
A more flexible approach is to use a void * to point to your data item, and then delegate all type-aware operations (allocation, assignment, comparison, display, etc.) to other functions which are hidden behind a set of function pointers. For example:
struct node {
void *data;
struct node *left;
struct node *right;
};
struct node *newNode(void *data, void *(*copy)(const void *))
{
struct node *n = malloc(sizeof *n);
if (n)
{
n->left = n->right = NULL;
n->data = copy(data);
}
return n;
}
void insert(struct node *root, struct node *newNode,
int (*compare)(const void *, const void *))
{
if (compare(newNode->data, root->data) < 0)
if (root->left != NULL)
insert(root->left, newNode, compare);
else
root->left = newNode;
else
if (root->right != NULL)
insert(root->right, newNode);
else
root->right = newNode;
}
In the examples above, the details of allocating memory for a node's data element and comparing two data elements are delegated to other functions, and pointers to those functions are passed as parameters to the list management functions. This way you wind up writing a single newNode and insert function, but one that's capable of handling arbitrary node data types. So, for your integer tree, you'd write functions like
void *copyInt(const void *data)
{
const int *src = data;
int *dst = malloc(sizeof *dst);
if (dst)
{
*dst = *src;
}
return dst;
}
int compareInt(const void *lhs, const void *rhs)
{
const int *ilhs = lhs;
const int *irhs = rhs;
if (*ilhs < *irhs)
return -1;
else if (*ilhs == *irhs)
return 0;
else
return 1;
}
then you'd call newNode and insert like
void insertIntValue(struct node *root, int value)
{
struct node *n = newNode(&value, copyInt);
if (n)
insert(root, n, compareInt);
}
The big disadvantage of this approach is that you throw type safety right out the window and into oncoming traffic; because we're using void * for everything. the compiler won't be able to catch type mismatches for us. There's nothing to stop you from passing the wrong copy or comparison function to the generic routines for a particular type.
Which brings us to our second disadvantage - you still need to write a type-aware interface (such as the insertIntValue function above) for each data type you want to support (insertFloatValue, insertStringValue, insertMyObnoxiousDataTypeValue, etc.) along with all of the delegates. Partly to avoid type-safety issues, and partly because our "generic" functions really aren't designed to be called directly. For example, the newNode function expects a pointer as the first parameter, meaning you can't write something like
struct node *n = newNode(10, copyInt);
or
struct node *n = newNode(3.14159, copyDouble);
IOW, you can't pass a literal as the first argument; you must pass the address of an object.
The third main disadvantage is you wind up doing a lot of memory management, which is a pain. You have to create copies of your inputs; otherwise, you wind up assigning the same pointer value (the one passed to newNode) to every node in your tree. Every malloc must have a matching free or you will wind up leaking a lot of memory. You have to be disciplined in how you allocate and deallocate your data items.
Building robust generic containers in C is, frankly, a massive pain in the ass. The only real reason to do it is so you can truly appreciate the value of templates in C++ and generics in Java and C#.
I need to write AVL-tree with generic type in C. The best way I know is to use [ void* ] and to write some functions for creating, copying, assignment and destruction. Please, tell me some better way.
I will give you an example on how you can achieve generics functionality in C. The example is on a linked list, but I am sure you can adapt it on your AVL tree if necessary.
First of all you will need to define a structure for list element. A possible (most simple implementation):
struct list_element_s {
void *data;
struct list_element_s *next;
};
typedef struct list_element_s list_element;
Where 'data' will act as the "container" where you are going to keep your information, and 'next' is the reference to the direct linked element. (NOTE: Your binary tree element should include a reference to the right / left children elements).
After you create you element structure, you will need to create your list structure. A good practice is to have some members that are pointing to functions: destructor (needed to free the memory being hold by 'data'), and comparator (to be able to compare two of your list elements).
A list structure implementation could look like this:
struct list_s {
void (*destructor)(void *data);
int (*cmp)(const void *e1, const void *e2);
unsigned int size;
list_element *head;
list_element *tail;
};
typedef struct list_s list;
After you design your data structure, you should design your data structure interface. Let's say our list will have the following, most simple, interface:
nmlist *list_alloc(void (*destructor)(void *data));
int list_free(list *l);
int list_insert_next(list *l, list_element *element, const void *data);
void *list_remove_next(list *l, list_element *element);
Where:
list_alloc : will alocate memory for your list.
list_free : will free memory allocated for list, and all 'data' being held by list_element(s).
list_insert_next : will insert a new element next to 'element' . If 'element' is NULL, the insertion will be made at the head of the list.
list_remove_next : will remove & return (void*)'data' being held by 'element->next' . If 'element' is NULL, it will perform "list->head removal".
And now the functions implementation:
list *list_alloc(void (*destructor)(void *data))
{
list *l = NULL;
if ((l = calloc(1,sizeof(*l))) != NULL) {
l->size = 0;
l->destructor = destructor;
l->head = NULL;
l->tail = NULL;
}
return l;
}
int list_free(list *l)
{
void *data;
if(l == NULL || l->destructor == NULL){
return (-1);
}
while(l->size>0){
if((data = list_remove_next(l, NULL)) != NULL){
list->destructor(data);
}
}
free(l);
return (0);
}
int list_insert_next(list *l, list_element *element, const void *data)
{
list_element *new_e = NULL;
new_e = calloc(1, sizeof(*new_e));
if (l == NULL || new_e == NULL) {
return (-1);
}
new_e->data = (void*) data;
new_e->next = NULL;
if (element == NULL) {
if (l->size == 0) {
l->tail = new_e;
}
new_e->next = l->head;
l->head = new_e;
} else {
if (element->next == NULL) {
l->tail = new_e;
}
new_e->next = element->next;
element->next = new_e;
}
l->size++;
return (0);
}
void *list_remove_next(list *l, list_element *element)
{
void *data = NULL;
list_element *old_e = NULL;
if (l == NULL || l->size == 0) {
return NULL;
}
if (element == NULL) {
data = l->head->data;
old_e = l->head;
l->head = l->head->next;
if (l->size == 1) {
l->tail = NULL;
}
} else {
if (element->next == NULL) {
return NULL;
}
data = element->next->data;
old_e = element->next;
element->next = old_e->next;
if (element->next == NULL) {
l->tail = element;
}
}
free(old_e);
l->size--;
return data;
}
And now, how to use your simple generic linked list implementation. In the following example the list is acting like a stack:
#include <stdlib.h>
#include <stdio.h>
#include "nmlist.h"
void simple_free(void *data){
free(data);
}
int main(int argc, char *argv[]){
list *l = NULL;
int i, *j;
l = list_alloc(simple_free);
for(i = 0; i < 10; i++){
j = calloc(1, sizeof(*j));
if(j != NULL){
*j = i;
list_insert_next(l, NULL, (void*) j);
}
}
for(i = 0; i < 10; i++){
j = (int*) list_remove_next(l, NULL);
if(j != NULL){
printf("%d \n", *j);
}
}
list_free(l);
return (0);
}
Note that instead of "int *j" you can use a pointer that references more complex structures. If you do, don't forget to modify your 'list->destructor' function accordingly.
What Alex said. In c, void * is what there is.
Assuming you must work in C, though... Why do you need to provide the create/copy/assignment/destruction functions to the library? Which features of this library require the AVL-tree code to use those operations?
The major operations on a search tree are insert, delete and lookup, correct? You will need to provide a comparison function for all of those operations, but you should let the clients of this library handle all of the other operations. Simple is probably better in this case.
To do true, performant generics in C, you hack with the preprocessor. This approach has many of the same disadvantages of the C++ template approach; namely that all (most, anyway) code must live in header files, and debugging and testing are a pain. The advantages are also there; that you can get superior performance and let the compiler do all sorts of inlining to speed things up, minimize allocations by reducing indirection, and a modicum of type safety.
The definition looks like (let's imagine we have a hash set)
int my_int_set(int x);
#define HASH_SET_CONTAINED_TYPE int
#define HASH_SET_TYPE my_int_set
#define HASH_SET_FUNC hash_int
#include "hash_set.h"
And then to use it, you simply use the type you created above:
my_int_set x;
my_int_set_init(&x);
my_int_set_add(&x, 7);
if (my_int_set_check(&x, 7)) printf("It worked!\n");
...
// or, if you prefer
my_int_set *x = my_int_set_create();
Internally, this is implemented by a whole bunch of token pasting, etc., and (as noted above) is a huge pain to test.
So something like:
#ifndef HASH_SET_CONTAINED_TYPE
#error Must define HASH_SET_CONTAINED_TYPE
#endif
... /// more parameter checking
#define HASH_SET_ENTRY_TYPE HASH_SET_TYPE ## _entry
typedef struct HASH_SET_ENTRY_TYPE ## _tag {
HASH_SET_CONTAINED_TYPE key;
bool present;
} HASH_SET_ENTRY_TYPE;
typedef struct HASH_SET_TYPE ## _tag {
HASH_SET_TYPE ## _entry data[];
size_t elements;
} HASH_SET_TYPE;
void HASH_SET_TYPE ## _add(HASH_SET_CONTAINED_TYPE value) {
...
}
...
#undef HASH_SET_CONTAINED_TYPE
... // remaining uninitialization
You can even add options; like #define HASH_SET_VALUE_TYPE or #define HASH_SET_DEBUG.