I'm a little confused about it. The exercise is very long so I hope I wrote everything that's relevant for my question. I have a given header file (part of it):
typedef void *(*copy_element)(const void *);
typedef void *(*free_element)(void **);
typedef struct group {
size_t group_size;
void **data;
copy_element copy_element_func;
free_element free_element_func;
} group;
group *group_alloc(copy_element copy_element_func, free_element free_element_func);
void group_free(group **p);
int add(group *group, const void *value);
I need to implement group.c
as a generic struct.
My question is how can I implement add and alloc functions for **data
?
With a known type I would use malloc and realloc with the size of the type, but here I'm not sure what to do.
group *group_alloc() {
group *p = malloc(sizeof(group))
if(p == NULL) {
//
}
p->group_size = 0;
void **ptr = malloc(sizeof(void*));
p->data = ptr;
return p;
}
In the exercise, Group should contain a dynamic array of values.
Thanks!
In fact, you should not care for the size nor the type of the elements, because the caller shall provide 2 functions that deal with copy and deallocation of those elements, so you can at the group
functions level handle them as fully opaque pointers.
Here is a possible implementation. This code also contains a small demo showing how to handle null terminated strings:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
typedef void *(*copy_element)(const void *);
typedef void *(*free_element)(void **);
typedef struct group {
size_t group_size;
void **data;
copy_element copy_element_func;
free_element free_element_func;
} group;
group *group_alloc(copy_element copy_element_func, free_element free_element_func);
void group_free(group **p);
int add(group *group, const void *value);
/***
Allocate a new group that will use the 2 provided functions.
The group will be initially empty
*/
group *group_alloc(copy_element copy_element_func, free_element free_element_func) {
group * g = malloc(sizeof(*g));
g->group_size = 0;
g->data = NULL;
g->copy_element_func = copy_element_func;
g->free_element_func = free_element_func;
return g;
}
/*********
* Add a new element to a group.
* Will use the copy_element_func member to build a copy of the element
* This implementation returns the number of elements in the group
*/
int add(group *group, const void *value) {
size_t sz = group->group_size + 1; // do not change anything on alloc error
void **data = realloc(group->data, sz * sizeof(void *));
if (data == NULL) { // allocation error
return 0;
}
// use copy_element_func to build a copy of the element
data[sz - 1] = group->copy_element_func(value);
group->group_size = sz;
group->data = data;
return (int) sz;
}
/******************
* Free a group.
* First free all elements of the group (using the free_element_func member)
* and then the group itself
*/
void group_free(group **p) {
group *g = *p;
if (g != NULL) {
for (int i = 0; i < g->group_size; i++) {
// again use free_element_func that should be able to free an element
g->free_element_func(g->data + i);
}
free(g);
}
*p = NULL;
}
// Example functions for null terminated strings
void * copy_string(const void *input) {
return strdup(input);
}
void * free_string(void **str) {
free(*str);
*str = NULL;
return *str;
}
// demo code
int main() {
group *g = group_alloc(©_string, &free_string);
int i = add(g, "foo");
printf("%d\n", i); // should display 1
i = add(g, "bar");
printf("%d\n", i); // should display 2
for (i = 0; i < g->group_size; i++) {
printf("%s\n", ((char **)g->data)[i]); // should display foo then bar
}
group_free(&g);
printf("%p\n", g); // should display a NULL pointer
return 0;
}
Disclaimer: this code blindly assumes the availability of the strdup
function, while it is optional and does not test for allocation errors...