Generic data type in C [ void * ]
Solution 1
It's much better to use a union of all types you will need instead of void*
, combined with an enumeration of the selected type.
If you don't have a finite list of types, then use a struct containing void*
pointer and the size allocated at least.
For example:
struct ANYTYPE
{
enum {
typUndefined,
typInt, // 1
typUint,
typString,
typByteString,
typLong, // 5
typFloat,
typDouble,
} iType;
void* value;
};
This allows you to easily deallocate the memory correctly and make generic code that can analyze the type before acting on the value. You have a similar option with union:
struct ANYTYPE
{
enum {
typUndefined,
typInt, // 1
typUint,
typString,
typLong
} iType;
union
{
int i;
unsigned int u;
char* s;
long l;
} value;
}
In a union, all elements are using same memory space and only one should be accessed. Via the enumeration, you know which element should be accessed. Of course you don't have OO protection like in C++, so you need to be sure you correctly set the enumeration everywhere you use this struct.
Solution 2
You are copying the integer value (55) into the pointer int_out
. Whatever lies at (the next 4 to 8 bytes at) address 55, it is unlikely to have the numerical value 55. Instead, try
int int_in = 55;
int int_out;
store ( storage_int, &int_in, sizeof(int));
retrieve ( &int_out, storage_int, sizeof(int));
assert ( 55 == int_out);
Another problem with your code: in the char_in
/char_out
parts, you are only ever copying pointer values. This works (in this case), but is not likely what you really intended, or is it? If you really want to store the pointer, you can skip the store
and retrieve
part:
char* str_in = "hello, world"; // Original value
void* generic_ptr = str_in; // ... a pointer to which is stored in some data structure
char* str_out = generic_ptr; // ... which can later be retrieved
will work as well. On the other hand: if you intend to store the actual content of the string, you have to copy it (like, with _strdup
as in your second example), and save the pointer to the copy of the string:
char* str_in = _strdup("copy me");
void* generic_ptr = str_in; // Gets stored in your data structures
Solution 3
You're overwriting the pointer, then dereferencing it... if your code looked like this it would work... but that may not be what you're after.
int int_out;
store ( storage_int, &int_in, sizeof(int));
retrive ( &int_out, storage_int, sizeof(int));
assert ( 55 == int_out);
or:
int *int_out;
store ( storage_int, &int_in, sizeof(int));
retrive ( &int_out, storage_int, sizeof(int));
assert ( 55 == (int)int_out);
However, in the second case this is a bit dubious, because it assumes that both 'int' and 'int*' are the same size, so you're able to store one on top of the other when retrieving. I believe this isn't the case on all systems.
Solution 4
Dirk is totally correct. Another way to express this, is that there is no space allocated to put retrieved data in.
try:
int_in = 55;
void* int_out = malloc (sizeof (int_in));
store ( storage_int, &int_in, sizeof(int));
retrieve ( int_out, storage_int, sizeof(int));
assert ( 55 == *(int*)int_out);
Of course, you may want to use something other than malloc(), and there is no check to see if malloc() failed, but this should get you on the right track.
Related videos on Youtube
Avinash
Something which i have done. https://github.com/davinash?tab=repositories
Updated on July 09, 2022Comments
-
Avinash almost 2 years
Hello I am trying to use void * as a generic data type in C. What I want is a mechanism using which I can store anything and get anything. I wrote some code, but it is failing in the last case. Can anybody please take a look at the code, If you have any other idea please let me know.
I know what I am trying to store, so at that point I know the data type, but during retrial I only know the starting address and size .
Here is the code :
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <assert.h> static void store( void *destination, void *source, size_t size ) { memcpy ( (char*)destination, source, size); } static void retrieve ( void *destination, void *source, size_t size) { memcpy ( destination, (char*)source, size); } void *storage_char_ptr = (void*) malloc ( sizeof( char* )); void *storage_int_ptr = (void*) malloc ( sizeof(int*)); void *storage_int = (void*) malloc ( sizeof( int)); int main() { int int_in = 65; void *int_out_ptr; int *ptr = ( int*) malloc ( sizeof(int)); memcpy ( ptr, &int_in, sizeof(int)); store ( storage_int_ptr, &ptr, sizeof(int*)); retrieve ( &int_out_ptr, storage_int_ptr, sizeof(int*)); assert ( int_in == *(int*)int_out_ptr); char *char_in = "HelloWorld!!"; void *char_out; store ( storage_char_ptr, &char_in, sizeof(char*)); retrieve ( &char_out, storage_char_ptr, sizeof(char*)); assert ( strcmp ( char_in, (char*)char_out ) == 0 ); char_in = _strdup("HelloWorld!!"); store ( storage_char_ptr, &char_in, sizeof(char*)); retrieve ( &char_out, storage_char_ptr, sizeof(char*)); assert ( strcmp ( char_in, (char*)char_out ) == 0 ); /* This is where it is failing */ int_in = 55; void* int_out; store ( storage_int, &int_in, sizeof(int)); retrieve ( &int_out, storage_int, sizeof(int)); assert ( 55 == *(int*)int_out); }
-
Avinash about 13 years@Benoit Thiery, Could you please explain more, any example if you have. I am intrested in struct solution.
-
Avinash about 13 yearsthanks Benoit, But I have finite list of types, So could you please shed more light on struct approach.
-
Prof. Falken about 13 years@Avinash, even you have a finite list of types, union is still better. A union is almost the same as a struct.
-
Avinash about 13 yearsbut how do i handle structure pointers in this case ?
-
Avinash about 13 yearsI am trying to write a stl vector in C, and I could do it if client allocating memory on the heap and pass as an element to me. But I want it to work with basic datatype and there should not be special way to pass variables on the stack or heap variables there is way to do this using MACROS but i was looking for option which avoid MACROS.
-
Lundin about 13 yearsThis is called a "variant". It is not necessarily better than malloc, it is a very memory ineffective way of writing generic data types. There is a reason why so few languages have support for their own variant type.
-
Lundin about 13 years@Avinash STL vectors handle dynamic memory allocation internally and make hardcopies of all data. As your code doesn't copy any data, it is rather a kind of lookup table than a container class.
-
Avinash about 13 years@Lundin, What does hard copy of all data means. Does it do memcpy internally ???