c function merge help - c

I have two functions:
void free_this(THIS *this)
{
THIS *this_tmp;
while (this_tmp = this)
{
if (this->str)
free(this->str);
this = this_tmp->next;
free(this_tmp);
}
}
void free_that(THAT *that)
{
THAT *that_tmp;
while (that_tmp = that)
{
if (that->id)
free(that->id);
that = that_tmp->next;
free(that_tmp);
}
}
Since they are very similar I was trying to come up with one function to handle them both. I can already just use a pointer to point to the correct data to free (i.e. point to either str from THIS struct or to id of THAT struct) however I can't figure out how to get around what type of struct is being dealt with since I can't just use a void pointer since void* has no member named 'NEXT'.
Any ideas?
Maybe I should just combine the two structs THIS and THAT into one somehow? here they are:
typedef struct this {
struct this *next;
char *str;
} THIS;
typedef struct that {
struct that *next;
char *id;
unsigned short result;
OTHERTHING *optr;
} THAT;
Could I possibly use the offsetof function somehow to get the next element?

You could implement the free function with a void * and field offsets. Untested:
void free_either(void *either, size_t other_offset, size_t next_offset)
{
void *either_tmp;
while (either_tmp = either)
{
free((char *)either + other_offset);
either_tmp = (char *)either + next_offset;
free(either);
}
}
free_either(this,offsetof(THIS,str),offsetof(THIS,next));
free_either(that,offsetof(THAT,id),offsetof(THAT,next));
You could then create macros to replace the old free_this or free_that functions.

Depends on the exact structure of THIS and THAT. If they are very similar, especially if str and id have the same offsets, you may be able to merge them into one object.
structure THIS {
void* str;
...
};
structure THIS {
void* id; /* is at the same offset as str */
...
};
union THAS {
structure THIS this;
structure THAT that;
void* pointer; /* at the same offset as str and id */
};
/* and use it like */
void free_thas(THAS* thas) {
free(thas->pointer);
...
}
If you have a bad feeling about this, your are right. Some small change in THIS may cause THAT to explode and so on. Don't do it.

You have two different singly linked list types here. You could get around this by creating only a single type:
typedef struct node {
struct node *next;
void *data;
} NODE;
and have data point to either a char* (or simply a char) or another struct with the three data fields from THAT. Of course you have to remember to free() the data in your free_node() function.

Yet another way is through some primitive inheritance:
struct node {
struct node *next;
}
struct this {
struct node mynode;
...
}
struct that {
struct node mynode;
...
}
free_any(struct node *this)
{
struct node *this_tmp;
while (this_tmp = this)
{
this = this_tmp->next;
free(this_tmp);
}
}
This only works if "node" is at the top of the structures, and only allows you to thread one linked-list through these structures.
Also, this doesn't allow you to free anything specific to that type of structure; to do that, you would have to setup a callback function ( either by passing it in the free or in some control structure ) that would get invoked. I would probably instead implement a "pop" function which removes the element from the list, and to free the entire list I would pop off each element and then free them as required.

There are more fancy ways to do what you want - but the following example will suffice.
void free_that(void *mem, int type)
{
switch(type) {
case THIS_FLAG: {
THIS *this = (THIS*)mem;
for(this; this->str != NULL; this = this->next)
(void)free(this->str);
break;
}
case THAT_FLAG: {
THAT *that = (THAT*)mem;
for(that; that->id != NULL; that = that->next)
(void)free(that->id);
}
default: {
(void)free(mem);
}
}
return;
}
The more fancy way would be to add a void *mem as the first element in the structure and assign str and id as pointers that point to mem (where you malloc the memory). Doing this allows you to either always free the mem element or free the offset of zero cast to void*.

Related

Is it possible to write a generic traverse function in C for different list structures so long as they contain the "next" field?

First time asking a question but I did look around Google and stackoverflow to see if someone has asked something similar before. In malloc, recasting and free, it looked like the OP asked something similar for example. But it was more complicated.
I was wondering whether it's possible to create a generic function for a list structure in C that traverses the list given that you know that the different types of structures will always have a "next" field.
For example, given these two list-type structures:
typedef struct _list1 {
int value;
list1 *next;
} list1;
typedef struct _list2 {
int value;
char *string;
list2 *next;
} list2;
Is it possible to create a generic void freeList((void *) list) function or something which looks something like the below? I am aware it's a simple thing to write both free functions for each individual list separately.
void freeList((void *) list) {
// Included this because the structs would have different sizes
// so I thought it would be possible to cast it in order to properly dereference the field.
if (sizeof *list == sizeof list1)
*list = (list1) list;
else if (sizeof *list == sizeof list2)
*list = (list2) list;
if (!list) return;
else {
free(list->next);
free(list);
}
}
So far, my experiments with the code shown above didn't fare well given that gcc would complain about dereferencing a void * pointer.
Making a heterogeneous list can be achieved by the use of a tagged union, or just a tag and casting:
struct list_item {
struct list_item *next;
enum datatype type;
void *contents;
};
or
struct list_item {
struct list_item *next;
enum datatype type;
union {
int some_int;
char some_char;
} contents;
};
Then while traversing the list you just have to verify the type stored in type before using the contents of the element.
This check:
if (sizeof *list == sizeof list1)
*list = (list1) list;
else if (sizeof *list == sizeof list2)
*list = (list2) list;
doesn't work because sizeof is a static construct: its value is defined at compilation time. You're just asking for the sizeof void.
is it possible to create a generic function for a list structure in C that traverses the list given that you know that the different types of structures will always have a "next" field.
Yes, as mentioned before; you must be careful that every structure starts with the "next" field; the two structures in your post should therefore be reordered like this:
typedef struct _list1 {
list1 *next;
int value;
} list1;
typedef struct _list2 {
list2 *next;
int value;
char *string;
} list2;
It is not clean code, because the compiler could reorder (and pad) the fields of the structure, but in general it should work.
Is it possible to create a generic void freeList((void) *list) function or something which looks something like...
This is possible if your structs do not refer malloced memory; or they do, but in a uniform (and known) way (note the first case is a sub-case of this last).
If the structs contain pointers pointing to memory that has to be freed, in fact, while freeing the struct the freeList() function should also free the referenced memory. A few solutions come to my mind:
1 - If all the different structs contain the same "pointers" layout, the routine can free those pointers in a uniform manner, knowing in advance what to do. In such scenario, one can also use pointer fields that are not used by all the structs, but only some.
2 - Every single instance of a struct could contain some helper field describing the pointer's layout. For example, just after the "next" field, another "mempntcnt" field could tell how many pointers (to be freed) follow the "next" field. Or, this "mempntcnt" could be passed as a parameter to freeList().
3 - This problem could be managed by a totally separated mechanism, outside the scope of freeList(). Much depends on the final usage: I mean, for a given (kind of) linked list, first call a routine that frees all the memory referenced by the list itself, then free the list by calling the common freeList(). After all, if different structs are needed, then different routines are used on them...
I hope I've been clear enough...
If you ensure that the next pointer is the first member of the struct then this is possible.
typedef struct list1 {
// next pointer must be first
struct list1 *next;
int value;
} list1;
typedef struct list2 {
// next pointer must be first
struct list2 *next;
int value;
char *string;
} list2;
void freeList(void *list) {
if (list) {
freeList(*(void**)list);
free(list);
}
}

Best practice for generic data structure implementation in C

In my adventures implementing generic data structures in C, I've come across a dilemma. For example, in the following code:
void add_something(avl_tree_t * my_tree) {
int new_element = 123;
avl_insert(my_tree, (void*)&new_element);
}
int main() {
avl_tree_t * my_tree = avl_create();
add_something(my_tree);
// do stuff
avl_print(my_tree, function_that_prints_ints);
exit(0);
}
In which avl_insert is defined as
void avl_insert(avl_tree_t * tree, void * data) {
avl_node_t * new_node = malloc(sizeof(struct avl_node));
new_node->data = data;
// do tree balancing stuff
}
In order for my generic insertion function to work, I have to pass it a void * item to store. However, in order for that to work, in this case I need to pass in the address of the new int item I'm adding so that I can then dereference it to a void *. If I am not mistaken, when we're back in the main function, the memory address in which I stored my new element will be compromised.
One way I looked into to solve this issue is to pass in the size of the things I am storing in the tree as a parameter for avl_create, and then allocating memory for a copy of each element I insert. This works because you don't need the original address or value for whatever you added.
Another thing that works is only using the data structure in the span of a single function, which is obviously not viable.
My question is this: what is the best way to go about storing statically allocated data in a generic data structure, be it basic C types or user made structures?
Thank you in advance.
To store pointers to data with automatic storage duration, yes, you would have to know the size of the elements in the container and allocate and copy the pointed-to data.
The simplest way is to just allocate and copy in all cases, optionally using a user-specified clone() or create() function to make deep copies, if necessary. This also entails the use of a user-specified destroy() function to dispose of the copies properly (again, if necessary).
To be able to avoid the allocation, then you have to have some kind of state variable that lets you know if the container should allocate, or just copy the pointer value itself.
Note that this should apply to the container object, not to the individual nodes or elements. If a container stores data in one way or the other, it should store all data that way. See Principle of Least Astonishment.
This is the more complex approach, since you have to be sure to use the correct process for adding and deleting elements based on the state variable. It's ususally much simpler to just make sure you never pass in a pointer to a value with automatic storage duration.
Use a mix-in style; e.g. do not make data part of the node but the node part of the data:
struct avl_node {
struct avl_node *parent;
struct avl_node *left;
struct avl_node *right;
};
struct person {
char const *name;
struct avl_node node;
};
struct animal {
struct avl_node node;
int dangerousness;
};
Constructors for animal are like
struct animal *animal_create(double d)
{
struct animal *animal = malloc(sizeof *animal);
*animal = (struct animal) {
.node = AVL_NODE_INIT(),
.dangerousness = d,
};
return animal;
}
The generic AVL tree operations could look like
void avl_tree_insert(struct avl_node **root, struct avl_node *node,
int (*cmp)(struct avl_node const *a, struct avl_node const *b))
{
/* .... */
}
and a cmp function for animal like
int animal_cmp(struct avl_node const *a_, struct avl_node const *b_)
{
struct animal const *a = container_of(a_, struct animal, node);
struct animal const *b = container_of(b_, struct animal, node);
return a->dangerousness - b->dangerousness;
}

Generic programming in C

I am writing a generic linked list implementation in pure C.
struct Node {
void *value;
struct Node *next;
};
struct LinkedList {
struct Node *start;
struct Node *end;
};
void LinkedList_new(struct LinkedList* llist) {
llist->start = 0;
llist->end = 0;
return;
}
void addNode( struct LinkedList *ll, void *_value ) {
if ( NULL == ll->start ) {
ll->start = (struct Node *) malloc( sizeof(struct Node) );
ll->end = ll->start;
} else {
ll->end->next = (struct Node *) malloc( sizeof(struct Node) );
ll->end = ll->end->next;
}
ll->end->value = _value;
return;
};
This all works great. My problem is when I get to printing value to the screen. I can't seem to find a generic implementation for printing.
Is there a way to determine the TYPE allocated to void *? (And then just do conversion using a switch statement)
void printFunc(int aInt) {
char str[15];
sprintf(str, "%d", aInt);
printf(str);
}
This is an implementation that works for int. Worst case I was thinking was writing a different function for each TYPE. Is this really my only route when using void *?
Is there a better way to do this?
No, there's no way to figure that out from the pointer alone. That would require type information to be stored at some well-defined location in all run-time structures, which is simply not how C uses the machine.
The common solution is for the user of the datatype to provide the print function that the application needs, since the application will know the type of data being stored. That is, there is usually an iteration function that takes a function pointer, calling the user's function (which might print the element) on each element of the list.
Here's how such a function could look:
void LinkedList_foreach(const LinkedList *start,
bool (*func)(void *element, void *data), void *data);
The above should call func() for each element of the list, passing it the element's data and the additional user-supplied data pointer which can be used by the caller to maintain state for the traversal. The callback func() should return false to stop the iteration, true to keep going.
To print an integer, assuming the integers are stored in the pointers, you could have:
static bool print_int(void *element, void *data)
{
printf("%d\n", (int) element);
return true;
}
Also, please don't cast the return value of malloc() in C.

Retrieving memory address with multiple nested struct pointers

I have a (hopefully) very simple issue that has been giving me problems for a while now. Given these structs
typedef struct
{
void * entity;
} link_t;
typedef struct
{
link_t * current;
} list_t;
and a function prototype
void *list_get_entity(list_t *list);
I need the function list_get_entity to return the address of the data that "entity" is pointing to. The best I've been able to do so far is
void *list_get_entity(list_t *list)
{
return list->current->entity;
}
which at least compiles and runs, but gives me gibberish. If for some reason the full file is needed to figure something out please let me know, although I'm sure there's other bugs in there I have yet to find because of this error.
Edit: fixed the code
To get the address of the data entity is pointing to just return it directly
return list->current->entity;
The void* is an address hence returning it directly by value will give the caller the address of the data
typedef struct
{
void * entity;
} link_t;
typedef struct
{
link_t * current;
} list_t;
void * list_get_entity(list_t *list)
{
return list->current->entity;
}
list->current is a pointer to link_t; list->current->entity is a pointer to entity type.
If you add &() around list->current->entity, it becomes pointer to pointer to void.

Need implementation for generic list in C that avoids duplicates at insertion

I'm a newbie in C and I am trying to implement a linked list which nodes are defined as follows:
typedef struct _cListNode
{
void *_data; //generic pointer to any data type
struct _cListNode *next; //next node in the list
} cListNode;
I need the InsertElement(cList myList, void *dataToInsert) function not to grow the list when the element that is being inserted is already in (i.e. no duplicates). My current problem is that I can't find a way to compare dataToInsert (the parameter) with _data (inside my node).
I thought of traversing the list externally before calling the InsertElement function and taking care of the comparisons outside the implementation of the list where I do know what the type is but I was hoping for a better design/solution.
Given two void pointers it is not possible to compare their data. This is because you do not know the size of the types of each of the pointers. If you want to compare their data, then you would need to store the pointers and the size of their data. Then you could use memcmp to compare the memory pointed at:
typedef struct _cListNode
{
void *_data; //generic pointer to any data type
size_t size;
struct _cListNode *next; //next node in the list
} cListNode;
int memcmp ( const void * ptr1, const void * ptr2, size_t num );
So:
memcmp(node_data_ptr, new_data_ptr, size_of_item_pointed_at);
You should only do the memcmp if the size is the same for both pointers, otherwise they are clearly different and you don't want to end up comparing invalid memory.
Your other option is to compare the pointers themselves and see if they are pointing at the same section of memory. It depends on what you mean by "duplicate".
You may want to do something like this. I'm supposing your linked list structure is as follows:
typedef struct _cList
{
cListNode* head;
cListNode* tail;
size_t size;
} cList;
int contains(cList* list, void* data, size_t dataSize)
{
cListNode* temp = list->head;
while(temp)
{
if(!memcmp(data, temp->_data, dataSize))
return 1;
temp = temp->next;
}
return 0;
}
void InsertElement(cList* myList, void *dataToInsert, size_t dataSize)
{
if(!contains(myList,dataToInsert, dataSize))
{
//Insert Data
}
else
{
//Data Is already present.
}
}
You should create the struct cListNode as specified in #jmh's answer.

Resources