Pregunta sobre estructuras de datos genericas.

Iniciado por astinx, 18 Marzo 2012, 20:29 PM

0 Miembros y 1 Visitante están viendo este tema.

astinx

Hola, tengo una duda acerca de estructuras de datos genéricas, como por ejemplo, una lista. Hacer una lista genérica que contenga diferentes tipos de datos básicos, como int, char, char* o float es sencillo; definimos un puntero a void y luego por ejemplo si queremos imprimir la lista entera bajo diferentes criterios usamos punteros a funciones que casteen el puntero al tipo adecuado de puntero. Sin embargo, si tenemos una lista genérica que adicionalmente, además de los tipos mencionados anteriormente, contiene una estructura, como por ejemplo t_Persona, que contiene los campos nombre y apellido, ambos char*, ¿Como deberíamos proceder para hacer un puntero a función que lo imprima de manera adecuada?.

Osea mi problema es que yo quiero tener una lista genérica de esa forma, tener una función imprimir que reciba la lista y un puntero a función, donde la función a la que apunta es el criterio a usar. Si yo en mi lista tengo 2, 4, a, c, 'Hola mundo!', 3.45, t_Persona(nombre: 'Jorge' apellido:'Ruiz') (Disculpen la notación de este ultimo), entonces si yo llamara a la función imprimir y le pasara la lista y la función que castea a caracteres me imprimiría: (El ASCII de 2), (El ASCII de 4), a, c, H, (Basura), (Mas basura).

Mi punto es: Si quiero imprimir usando el criterio para imprimir una persona, voy a tener que castear los punteros a void a punteros a t_Persona, con un dato que realmente sea de t_Persona no va a haber problema, sin embargo cuando este iterando y me cruce un puntero que en realidad es un puntero a float, y lo castee a tipo persona, y trate de acceder a sus miembros, esto me va a tirar un Segmentation Fault con toda la razón.

Yo pensé, entonces simplemente tendría que exigir que el resto de los elementos, como mínimo, ocupen la misma cantidad de bytes que t_Persona, de esa manera cuando los castee, me devolverán basura, pero no me tirara Segmentation Fault.

Mi duda es simple: ¿Existe una manera mas limpia de lograr mi objetivo?

Les dejo un ejemplo de mi codigo:

#include <stdio.h>
#include <stdlib.h>

typedef struct t_Persona {
char* nombre;
char* apellido;
} t_Persona;

//Decimos que un puntero a void es sinonimo de una direccion.
typedef void* t_Direccion;

typedef struct t_Lista {
t_Direccion* dato;
//El size NO lo necesitamos simplemente para saber cuantos elementos tenemos, SI lo necesitamos para seguirle la pista
//A cuanto espacio tenemos que alocar.
int size;
} t_Lista;

//El tipo iterador contiene la lista y la posicion actual dentro de ella
typedef struct t_Iterador {
t_Lista* lista;
//Nodo actual para recorrer
int actual;
} t_Iterador;

t_Iterador tIterador_Crear () {
t_Iterador nue;
nue.lista= (t_Lista*) malloc (sizeof(t_Lista));
(nue.lista)->size=0;
(nue.lista)->dato=NULL;
nue.actual=0;
return nue;
}

void tIterador_Iniciar(t_Iterador* iter) {
iter->actual=0;
}

/* Notese la diferencia entre el crear y el iniciar; El crear nos crea una nueva instancia, y el iniciar
* prepara el iterador para iterar, recibiendo la lista sobre la cual va a trabajar y poniendo su posicion
* actual a cero. */

/*Un simple agregar al final*/

void tIterador_Agregar (t_Iterador* iter, t_Direccion i) {
if (iter->lista->size == 0) {
iter->lista->dato = (t_Direccion*) malloc (sizeof(t_Direccion)*(++iter->lista->size));
} else {
iter->lista->dato = (t_Direccion*) realloc (iter->lista->dato, sizeof(t_Direccion)*(++iter->lista->size));
}
iter->lista->dato[iter->actual++] = i;
}

/*Funciones que imprimen en diferentes formatos, utilizadas para el imprimir*/

void imprimirEnteros (t_Direccion x) {
printf("%i, ",*((int*)(x)));
}

void imprimirCaracteres (t_Direccion x) {
printf("%c, ",*((char*)(x)));
}

void imprimirStrings (t_Direccion x) {
printf("%s, ",((char*)(x)));
}

/* No escatimes en el uso del parentesis en estos casos!!! */
/* void imprimirPersonas (t_Direccion x) {
printf("%s %s, ",((t_Persona*)(x))->nombre, ((t_Persona*)(x))->apellido);
} */
/* En estos casos hay que asegurarse de que todos los elementos tienen el mismo tamaño*/

/*El imprimir generico*/
/*Nomenclatura de una funcion pasada como parametro a otra (puntero a funcion)
*TIPORETORNO (*NOMBRE)(PARAMETROS) Notese que para un puntero a funcion el asterisco se usa a la izquierda*/
void tIterador_Imprimir (t_Iterador* iter, void (*criterio)(t_Direccion)) {
criterio(iter->lista->dato[iter->actual]); /*Modo de uso de un puntero a funcion*/
}

int tIterador_Fin (t_Iterador* iter) {
return (iter->actual < iter->lista->size)?(1):(0);
}

void tIterador_Avanzar (t_Iterador* iter) {
iter->actual++;
}

int main(int argc, char** argv)
{

int a=65, b=66, c='a', d=68, e='c';
char* f = "Holaaa";
t_Iterador iter = tIterador_Crear();

tIterador_Iniciar(&iter);

/*Vamos a agregar los numeros del 65 al 70*/
tIterador_Agregar(&iter, &a);
tIterador_Agregar(&iter, &b);
tIterador_Agregar(&iter, &c);
tIterador_Agregar(&iter, &d);
tIterador_Agregar(&iter, &e);
tIterador_Agregar(&iter, f); //Look!

/*Imprimimos como enteros*/
tIterador_Iniciar(&iter);
/*Al imprimir le pasamos la funcion que va a usar como criterio de conversion*/
while ((tIterador_Fin(&iter))) {
tIterador_Imprimir(&iter, imprimirEnteros);
tIterador_Avanzar(&iter);
}
printf("\n");
/*Imprimimos como Caracteres*/
tIterador_Iniciar(&iter);
/*Al imprimir le pasamos la funcion que va a usar como criterio de conversion*/
while ((tIterador_Fin(&iter))) {
tIterador_Imprimir(&iter, imprimirCaracteres);
tIterador_Avanzar(&iter);
}
printf("\n");
/*Imprimimos como Strings*/
tIterador_Iniciar(&iter);
/*Al imprimir le pasamos la funcion que va a usar como criterio de conversion*/
while ((tIterador_Fin(&iter))) {
tIterador_Imprimir(&iter, imprimirStrings);
tIterador_Avanzar(&iter);
}

return 0;
}


Si usaramos el imprimirPersonas obviamente nos tiraría un segmentation fault.

Muchas gracias por detenerse a leer, y saludos!
La programación hoy en día es una carrera entre los ingenieros de software intentando construir mejores y más eficientes programas a prueba de idiotas y el Universo intentando producir mejores y más grandes idiotas. De momento, el Universo está ganando

Gallu

Lo único que se me ocurre es que a la lista, en lugar de guardar directamente el dato,   encapsules los mismo en un tipo de dato que contenga un puntero a función , dicha función será la que llamarás cuando intentes imprimir el valor del nodo en el que te encuentras , eje

typedef struct  nodoGenerico{
t_Direccion dato;
char * ( * toString)();
} nodoGenerico;

antes de agregar el nodoGenerico a la lista, deberás proporcionar la función que describirá al objeto apuntado por dato, si no lo haces, has de suponer que es un tipo de dato simple e imprimirlo como proceda .

Saludos
Nadie alcanza la meta con un solo intento, ni perfecciona la vida con una sola rectificación, ni alcanza altura con un solo vuelo.

astinx

Gracias, había pensado algo parecido, pero no tuve en cuenta el puntero a función, voy a intentarlo y después te digo como salio xD

Saludos!
La programación hoy en día es una carrera entre los ingenieros de software intentando construir mejores y más eficientes programas a prueba de idiotas y el Universo intentando producir mejores y más grandes idiotas. De momento, el Universo está ganando