I have a structs array which contains structs that have the following definition:
typedef struct
{
void *data;
void (*print)(void *);
status_ty (*add)(void *, int);
void (*free_element)(void *);
} element_ty;
Each element can be either an "int" element, a "float" element or a "string" element.
I want to be able to add
int
values to each element.
For example:
add (3, array, size_of_array);
will loop through the array and the value of 3
will be added to each one of the elements.
Add
is always adding an int value, For int and float elements this is normal addition, and for a string it should do concatenation.
For example:
My questions are:
add
function when add
gets a void *
and I'm passing a pointer type of element_ty
(the struct), which will point to this specific element in the array?
See my written code below, let me know if there are any mistakes thereThis is my pseudo code for the AddToStr
function:
int
value that should be addedstring
that is located in element -> data
to string
(?) to be able to concatenate it later? The current data
is stored as type of void *
.malloc
the string to be able to receive the int
value?void *
and store it in element -> data
?free()
the malloc
?I'm so confused.
This is what I have wrote so far:
void FreeStrElement(void *(element_ty *element))
{
free(element -> data);
}
/*------------------------------------------------------------------*/
void AddToInt(void *(element_ty *element), int IncrementValue)
{
int data = (int) element -> data;
int sum = data + IncrementValue;
element -> data = (void *) sum;
}
/*------------------------------------------------------------------*/
void AddToFlt(void *(element_ty *element), int IncrementValue)
{
float data = (float *) element -> data;
float sum = data + IncrementValue;
element -> data = (void *) *(float *)∑
}
/*------------------------------------------------------------------*/
void AddToStr(void *(element_ty *element), int IncrementValue)
{
??
}
/*------------------------------------------------------------------*/
void PrintStr(void *num)
{
printf("String in the element: %s\n", (char **) &num);
}
/*------------------------------------------------------------------*/
status_ty InitStr(const char *value, element_ty *element)
{
element -> data = (void *)value;
element -> print = PrintStr;
element -> add = AddtoStr;
element -> free_element = FreeStrElement;
}
The functions that are assigned to each element need to take an element*
as input, as they need to be able to modify the data
member as needed. This is especially important for "string" elements, since you need to be able to reallocate the string memory when adding more characters, which means assigning a new char*
pointer to the data
.
You can pass in the element*
pointers as void*
parameters (which you said is required), but you will have to type-cast the void*
back to element*
inside the function bodies.
Try something like this:
typedef struct
{
void *data;
void (*print)(void *);
status_ty (*add)(void *, int);
void (*free_element)(void *);
} element_ty;
/*------------------------------------------------------------------*/
void FreeStrElement(void *element)
{
element_ty *p_element = (element_ty *) element;
free(p_element->data);
}
/*------------------------------------------------------------------*/
status_ty AddToInt(void *element, int IncrementValue)
{
element_ty *p_element = (element_ty *) element;
int *p_value = (int*) &(p_element->data);
*p_value += IncrementValue;
return ...;
}
/*------------------------------------------------------------------*/
status_ty AddToFlt(void *element, int IncrementValue)
{
element_ty *p_element = (element_ty *) element;
float *p_value = (float*) &(p_element->data);
*p_value += IncrementValue;
return ...;
}
/*------------------------------------------------------------------*/
status_ty AddToStr(void *element, int IncrementValue)
{
element_ty *p_element = (element_ty *) element;
char *p_str = (char*) p_element->data;
char *new_str = realloc(p_str, strlen(p_str) + 13);
if (!new_str) return ...;
sprintf(new_str, "%s%d", p_str, IncrementValue);
p_element->data = new_str;
return ...;
}
/*------------------------------------------------------------------*/
void PrintInt(void *element)
{
element_ty *p_element = (element_ty *) element;
int *p_value = (int*) &(p_element->data);
printf("Integer in the element: %d\n", *p_value);
}
/*------------------------------------------------------------------*/
void PrintFlt(void *element)
{
element_ty *p_element = (element_ty *) element;
float *p_value = (float*) &(p_element->data);
printf("Float in the element: %f\n", *p_value);
}
/*------------------------------------------------------------------*/
void PrintStr(void *element)
{
element_ty *p_element = (element_ty *) element;
char *p_str = (char*) p_element->data;
printf("String in the element: %s\n", p_str);
}
/*------------------------------------------------------------------*/
status_ty InitInt(int value, element_ty *element)
{
int *p_value = (int*) &(element->data);
*p_value = value;
element->print = &PrintInt;
element->add = &AddToInt;
element->free_element = NULL;
return ...;
}
/*------------------------------------------------------------------*/
status_ty InitFlt(float value, element_ty *element)
{
float *p_value = (float*) &(element->data);
*p_value = value;
element->print = &PrintFlt;
element->add = &AddToFlt;
element->free_element = NULL;
return ...;
}
/*------------------------------------------------------------------*/
status_ty InitStr(const char *value, element_ty *element)
{
element->data = strdup(value);
element->print = &PrintStr;
element->add = &AddToStr;
element->free_element = &FreeStrElement;
return ...;
}
/*------------------------------------------------------------------*/
Then you can do things like this:
void add (int value, element_ty *elements, int n) {
for(int i = 0 i < n; ++i) {
element_ty *elem = &elements[i];
elem->add(elem, value);
}
}
void print (element_ty *elements, int n) {
for(int i = 0 i < n; ++i) {
element_ty *elem = &elements[i];
elem->print(elem);
}
}
void free_elements (element_ty *elements, int n) {
for(int i = 0 i < n; ++i) {
element_ty *elem = &elements[i];
if (elem->free_element) elem->free_element(elem);
}
}
That being said, this approach assumes sizeof(int)
and sizeof(float)
are <= sizeof(void*)
, which is usually the case, but not guaranteed.
This approach also violates Strict Aliasing rules. You can use memcpy()
to avoid that, but using a union
instead would be easier and cleaner (it also avoids the sizeof
issue, too), eg:
typedef struct
{
union {
int i;
float f;
char *s;
} data;
void (*print)(void *);
status_ty (*add)(void *, int);
void (*free_element)(void *);
} element_ty;
/*------------------------------------------------------------------*/
void FreeStrElement(void *element)
{
element_ty *p_element = (element_ty *) element;
free(p_element->data.s);
}
/*------------------------------------------------------------------*/
status_ty AddToInt(void *element, int IncrementValue)
{
element_ty *p_element = (element_ty *) element;
p_element->data.i += IncrementValue;
return ...;
}
/*------------------------------------------------------------------*/
status_ty AddToFlt(void *element, int IncrementValue)
{
element_ty *p_element = (element_ty *) element;
p_element->data.f += IncrementValue;
return ...;
}
/*------------------------------------------------------------------*/
status_ty AddToStr(void *element, int IncrementValue)
{
element_ty *p_element = (element_ty *) element;
char *p_str = p_element->data.s;
char *new_str = realloc(p_str, strlen(p_str) + 13);
if (!new_str) return ...;
sprintf(new_str, "%s%d", p_str, IncrementValue);
p_element->data.s = new_str;
return ...;
}
/*------------------------------------------------------------------*/
void PrintInt(void *element)
{
element_ty *p_element = (element_ty *) element;
printf("Integer in the element: %d\n", p_element->data.i);
}
/*------------------------------------------------------------------*/
void PrintFlt(void *element)
{
element_ty *p_element = (element_ty *) element;
printf("Float in the element: %f\n", p_element->data.f);
}
/*------------------------------------------------------------------*/
void PrintStr(void *element)
{
element_ty *p_element = (element_ty *) element;
printf("String in the element: %s\n", p_element->data.s);
}
/*------------------------------------------------------------------*/
status_ty InitInt(int value, element_ty *element)
{
element->data.i = value;
element->print = &PrintInt;
element->add = &AddToInt;
element->free_element = NULL;
return ...;
}
/*------------------------------------------------------------------*/
status_ty InitFlt(float value, element_ty *element)
{
element->data.f = value;
element->print = &PrintFlt;
element->add = &AddToFlt;
element->free_element = NULL;
return ...;
}
/*------------------------------------------------------------------*/
status_ty InitStr(const char *value, element_ty *element)
{
element->data.s = strdup(value);
element->print = &PrintStr;
element->add = &AddToStr;
element->free_element = &FreeStrElement;
return ...;
}
/*------------------------------------------------------------------*/