Tipo de datos generics en C

Hola, estoy tratando de usar void * como un tipo de datos genérico en C. Lo que quiero es un mecanismo mediante el cual pueda almacenar cualquier cosa y obtener cualquier cosa. Escribí algún código, pero está fallando en el último caso. ¿Alguien puede echarle un vistazo al código? Si tiene alguna otra idea, hágamelo saber.

Sé lo que estoy intentando almacenar, por lo que en ese momento sé el tipo de datos, pero durante el nuevo juicio solo conozco la dirección y el tamaño de inicio.

Aquí está el código:

#include  #include  #include  #include  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); } 

Es mucho mejor usar una unión de todos los tipos que necesitará en lugar de void* , combinada con una enumeración del tipo seleccionado. Si no tiene una lista finita de tipos, use una estructura que contenga el puntero void* y el tamaño asignado al menos.

Por ejemplo:

 struct ANYTYPE { enum { typUndefined, typInt, // 1 typUint, typString, typByteString, typLong, // 5 typFloat, typDouble, } iType; void* value; }; 

Esto le permite desasignar fácilmente la memoria correctamente y crear un código genérico que pueda analizar el tipo antes de actuar sobre el valor. Tienes una opción similar con unión:

 struct ANYTYPE { enum { typUndefined, typInt, // 1 typUint, typString, typLong } iType; union { int i; unsigned int u; char* s; long l; } value; } 

En una unión, todos los elementos utilizan el mismo espacio de memoria y solo se debe acceder a uno. A través de la enumeración, usted sabe a qué elemento se debe acceder. Por supuesto, no tiene protección OO como en C ++, por lo que debe asegurarse de establecer correctamente la enumeración en todos los lugares donde use esta estructura.

Está copiando el valor entero (55) en el puntero int_out . Lo que sea que se encuentre en (la siguiente 4 a 8 bytes en) la dirección 55, es poco probable que tenga el valor numérico 55. En su lugar, intente

 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); 

Otro problema con su código: en las partes char_in / char_out , solo está copiando valores de puntero. Esto funciona (en este caso), pero no es probable lo que realmente pretendía, ¿o no? Si realmente desea almacenar el puntero, puede omitir la store y retrieve parte:

 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 

funcionará también Por otro lado: si tiene la intención de almacenar el contenido real de la cadena, debe copiarla (como, con _strdup como en su segundo ejemplo), y guardar el puntero en la copia de la cadena:

 char* str_in = _strdup("copy me"); void* generic_ptr = str_in; // Gets stored in your data structures 

Estás sobrescribiendo el puntero, luego anulando la referencia … si tu código tuviera este aspecto, funcionaría … pero eso podría no ser lo que buscas.

  int int_out; store ( storage_int, &int_in, sizeof(int)); retrive ( &int_out, storage_int, sizeof(int)); assert ( 55 == int_out); 

o:

  int *int_out; store ( storage_int, &int_in, sizeof(int)); retrive ( &int_out, storage_int, sizeof(int)); assert ( 55 == (int)int_out); 

Sin embargo, en el segundo caso esto es un poco dudoso, ya que asume que tanto ‘int’ como ‘int *’ son del mismo tamaño, por lo que es posible almacenar uno encima del otro cuando se recupera. Creo que este no es el caso en todos los sistemas.

Dirk es totalmente correcto. Otra forma de express esto, es que no hay espacio asignado para colocar los datos recuperados.

tratar:

 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); 

Por supuesto, es posible que desee utilizar algo que no sea malloc (), y no hay ninguna verificación para ver si malloc () ha fallado, pero esto debería ponerlo en el camino correcto.