The problem in C with what you are trying to do is that you need to know how to move through a structure (i.e. you need to know the types). The reason std::vector<T>
works the way it does is because it is using templates (a C++
concept). Now, that said, you could try something slightly different than what you suggested. If you do not want to use any arrays, you can store generic types. However, when retrieving the data and using it, the user will have to know what kind of data he or she is expecting. Below avoids arrays (although, a potentially cleaner solution exists in using them) and has a linked list implementation of something which gives you the nearly same flexibility of std::vector<T>
(performance benefits aside since this is a linked list with O(n)
operations for everything (you can be clever and reverse the list to achieve, perhaps, O(1)
insert, but this is merely for example)
#include <stdio.h>
#include <stdlib.h>
typedef struct _item3_t
{
void *x, *y, *z;
struct _item3_t* next;
} item3_t;
typedef struct
{
item3_t* head;
} vec3_t;
void insert_vec3(vec3_t* vec, void* x, void* y, void* z)
{
item3_t* item = NULL;
item3_t* tmp = NULL;
int i = 0;
if(vec == NULL)
return;
item = malloc(sizeof(item3_t));
item->x = x;
item->y = y;
item->z = z;
item->next = NULL;
tmp = vec->head;
if(tmp == NULL) { // First element
vec->head = item;
} else {
while(tmp->next != NULL)
tmp = item->next;
tmp->next = item;
}
}
// This is one method which simply relies on the generic method above
void insert_vec3_float(vec3_t* vec, float x, float y, float z)
{
float* xv, *yv, *zv;
if(vec == NULL)
return;
xv = malloc(sizeof(float));
yv = malloc(sizeof(float));
zv = malloc(sizeof(float));
*xv = x;
*yv = y;
*zv = z;
insert_vec3(vec, xv, yv, zv);
}
void init_vec3(vec3_t* vec)
{
if(vec == NULL)
return;
vec->head = NULL;
}
void destroy_vec3(vec3_t* vec)
{
item3_t* item = NULL, *next = NULL;
if(vec == NULL)
return;
item = vec->head;
while(item != NULL) {
next = item->next;
free(item->x);
free(item->y);
free(item->z);
free(item);
item = next;
}
}
item3_t* vec3_get(vec3_t* vec, int idx)
{
int i = 0;
item3_t* item = NULL;
if(vec == NULL)
return NULL;
item = vec->head;
for(i = 0 ; i < idx && item != NULL ; ++i)
item = item->next;
return item;
}
void do_something(item3_t* item)
{
if(item == NULL)
return;
float x = *((float*)item->x);
float y = *((float*)item->y);
float z = *((float*)item->z);
// To do - something? Note, to manipulate the actual
// values in the vector, you need to modify their values
// at their mem addresses
}
int main()
{
vec3_t vector;
init_vec3(&vector);
insert_vec3_float(&vector, 1.2, 2.3, 3.4);
printf("%f %f %f\n", *((float*)vec3_get(&vector, 0)->x), *((float*)vec3_get(&vector, 0)->y), *((float*)vec3_get(&vector, 0)->z));
do_something(vec3_get(&vector, 0));
destroy_vec3(&vector);
return 0;
}
This code should compile right out of box. What you have here is a linked list which is your "vector" (particularly, a vec3 structure). Each node in the list (i.e. every element in the std::vector<T>
sense) has 3 elements which are all void
pointers. Thus, you can store any data type you wish here. The only catch is that you need to allocate memory for those pointers to point to and when removing an element, you will need to free that memory (refer to the vec3_destroy
method for an example). Hope this helps a little more for understanding how these void pointers can work in your case.
To retrieve data, you won't be able to use the []
notation, but you can use the vec3_get
method in the same way. The do_something
method is an example stub of some way you might be able to accomplish something similar to what you mentioned in the OP.