Based on my googling around generic lists in C I stumbled upon tagged unions. What I wanted to create was a data structure that can hold int
, float
, double
and char
, all in one list. There is a function to add an item to which type information has to be passed. What I am unsure about: I read that casting void
pointers is bad practice, but since I know the type of the variable the void
pointer points to I think it's safe. The structure is kind of inspired by VBA recordsets, where the list has a cursor that tells the function where the record is to be inserted. The actual data is stored within an array of structs inside of a struct. The outside struct contains cursor and length information.
Questions:
- Possibly unsafe?
- If adding a new item fails, the cursor is incremented regardless - I don't know how to implement a check for successful insert of the record.
- Code contains
switch
es that depend on type information; there may be a more efficient way to handle different types. - I am unsure about the best way to allocate the outer list struct - do I define it first, then pass to a function to allocate or do I define and allocate inside of a function, returning a pointer? Right know, I use the latter.
- I reallocate the list array by doubling its allocated size; for a large list, this will be inefficient I guess?
- I am unsure what the best way to address the items of the
my_list_elem
in the array of structs in the outer struct,my_list
, is.
Code:
#include <stdio.h>
#include <stdlib.h>
#define DEFAULT_LIST_LENGTH 5
//an actual list element, contains type information
typedef struct list_elem {
enum {is_int = 1, is_float, is_double, is_char} type;
union {
int i_val;
float f_val;
double d_val;
char* c_val;
} value;
} my_list_elem;
/* list container, contains array of list elements
as well as cursor and length of list
*/
typedef struct list {
my_list_elem *element;
unsigned int length; //number of elements, not bytes
unsigned int cursor;
} my_list;
//allocate a new my_list and return pointer
my_list * alloc_list() {
my_list *in_list = malloc(sizeof(my_list));
in_list->element = malloc(sizeof(my_list_elem) * DEFAULT_LIST_LENGTH);
in_list->length = DEFAULT_LIST_LENGTH;
in_list->cursor = 0;
return in_list;
}
//add new element to list
void add_element(my_list *dest, void *in_value, const int type) {
unsigned int tmp_cursor = 0;
tmp_cursor = dest->cursor;
//double list size if not big enough, to reduce number of realloc calls
if(tmp_cursor == dest->length) {
dest->element = realloc(dest->element, dest->length * sizeof(my_list_elem) * 2);
dest->length *= 2;
}
(dest->element[tmp_cursor]).type = type;
switch(type) {
case is_int:
(dest->element[tmp_cursor]).value.i_val = *(int *)in_value;
break;
case is_float:
(dest->element[tmp_cursor]).value.f_val = *(float *)in_value;
break;
case is_double:
(dest->element[tmp_cursor]).value.d_val = *(double *)in_value;
break;
case is_char:
(dest->element[tmp_cursor]).value.c_val = (char *)in_value;
break;
}
dest->cursor += 1;
}
//free list
void free_list(my_list *in_list) {
free(in_list->element);
free(in_list);
}
//print list report (total list)
void print_report(my_list* src) {
printf("Current stats of list: \n");
printf("========================\n");
printf("Current cursor: %d\n",src->cursor);
printf("Length (allocated): %d\n", src->length);
printf("========================\n");
for(int i = 0; i < src->cursor ; i++) {
switch(src->element[i].type) {
case is_int:
printf("Type: %d Value: %d\n", src->element[i].type, src->element[i].value.i_val);
break;
case is_float:
printf("Type: %d Value: %f\n", src->element[i].type, src->element[i].value.f_val);
break;
case is_double:
printf("Type: %d Value: %lf\n", src->element[i].type, src->element[i].value.d_val);
break;
case is_char:
printf("Type: %d Value: %s\n", src->element[i].type, src->element[i].value.c_val);
break;
}
}
printf("\n\nEND.\n");
}
int main()
{
my_list *new_list = alloc_list();
int my_val = 45;
void *ptr_my_val = &my_val;
add_element(new_list,ptr_my_val,1);
char *ptr_my_string = "TEST";
add_element(new_list, ptr_my_string, 4);
double my_double = 0.56843;
double* ptr_my_double = &my_double;
add_element(new_list, ptr_my_double, 3);
print_report(new_list);
free(new_list);
return 0;
}
Tried it using OnlineGDB, works fine.
If anyone answers this post: thanks in advance, you're really helping me learn!
Tried it using OnlineGDB, works fine.
next thing would automated unit tests. \$\endgroup\$