¿Es normal hacer este tipo de conversiones de punteros void?

Iniciado por samur88, 16 Mayo 2011, 15:27 PM

0 Miembros y 3 Visitantes están viendo este tema.

D4RIO

Obviamente, debes conocer las diferencias de tamaños para decidir si una union es buena idea o no, y saber si realmente necesitas ese almacenamiento. En ESTA situación, cualquier uso de uniones es DISPARATADO  ;D

¿Porqué?
Porque cuando aún no sabes si es un médico o un paciente, usas el flag para tratarlo. Entonces ¿realmente necesitas almacenar las estructuras en un espacio de memoria común? Sin importar el tipo, puedes tener dos funciónes diferentes, con lo que la función que hace de dispatcher, no tiene que hacer más que pasar el puntero a quien le corresponda... que me parece mucho más razonable  ;), y si quieres probar si es NULL, lo haces al principio, cosa que te quedaría como:

void
funcion_dispatcher(void *puntero, tipo_dispatcher flag) {
    if (!puntero)
        muere("funcion_dispatcher: puntero nulo!");

    switch (flag) {
        case esMedico:
            funcionMedico((struct medico*)puntero);
            break;
        case esPaciente:
            funcionPaciente((struct paciente*)puntero);
            break;
    }
}


O lo que me parece más visible/lindo:
void
funcion_dispatcher(void *puntero, tipo_dispatcher flag) {
    if (!puntero)
        muere("funcion_dispatcher: puntero nulo!");

    if (flag == esMedico)
            funcionMedico((struct medico*)puntero);

    if (flag == esPaciente)
            funcionPaciente((struct paciente*)puntero);
}


OpenBSDFreeBSD

pucheto

#11
Cita de: xD4RIOx en 19 Mayo 2011, 16:24 PM
Obviamente, debes conocer las diferencias de tamaños para decidir si una union es buena idea o no, y saber si realmente necesitas ese almacenamiento. En ESTA situación, cualquier uso de uniones es DISPARATADO  ;D
Estoy usando una union de punteros, no de structs... todos los punteros ocupan lo mismo, no hay ningun desperdicio de espacio (al menos q sean near, far etc, pero eso ya depende mucho del sistema y es raro encontrarlo en la programacion en modo usuario).

Cita de: xD4RIOx en 19 Mayo 2011, 16:24 PM
¿Porqué?
Porque cuando aún no sabes si es un médico o un paciente, usas el flag para tratarlo. Entonces ¿realmente necesitas almacenar las estructuras en un espacio de memoria común?
Sin importar el tipo, puedes tener dos funciónes diferentes, con lo que la función que hace de dispatcher, no tiene que hacer más que pasar el puntero a quien le corresponda... que me parece mucho más razonable  ;), y si quieres probar si es NULL, lo haces al principio, cosa que te quedaría como....
Guardo el flag, con la union de punteros pq estan asociados, estan muy relacionados... Como estan tan relacionados, los pongo en el mismo struct...

D4RIO

Cita de: pucheto en 19 Mayo 2011, 17:16 PM
Estoy usando una union de punteros, no de structs... todos los punteros ocupan lo mismo, no hay ningun desperdicio de espacio (al menos q sean near, far etc, pero eso ya depende mucho del sistema y es raro encontrarlo en la programacion en modo usuario).
Guardo el flag, con la union de punteros pq estan asociados, estan muy relacionados... Como estan tan relacionados, los pongo en el mismo struct...
No había visto lo de los punteros. Pero no quita lo que decía, porque usar un "union" donde siempre se usará un puntero, y acceder luego a sus miembros es a fin de cuentas... un casteo. Y si vamos a hacer un casteo es mejor ser explícitos.

Por otro lado, no están más relacionados de lo que significa pasar por un mismo dispatcher, porque al final serán tratados por funciónes diferentes. Como al fin y al cabo, usas el union para "castear" el puntero contenido en ese espacio de memoria (el union)...
switch(p.flag){
    case f_medico:
         //lo manejo como medico
        handleMedico(m.data.m);
        break;
    case f_paciente:
         //lo manejo como paciente
        handlePaciente(m.data.p);
        break;
}

...entonces resulta más legible ir al grano con un cast. A demás, al usar esta forma tienes dos problemas a tener en cuenta:

1 - Necesitas una variable extra en la pila local de la función... solo a fin de alojar el puntero que te llega por parámetro, y para enviarlo a otra función. Es innecesario.
2 - Si declaras desde el main los datos como instancias de la union para evitar las variables locales innecesarias, pierdes la posibilidad de tener flujos donde se manejen solo pacientes, o solo médicos (en el ejemplo), sin pasar previamente por un dispatcher. Si los dispatchers son siempre necesarios, es porque tienes dos flujos separados. Seguramente perderás más de dos bytes haciendo dispatchers para cada flujo, y es la diferencia entre crear una instancia de cada tipo, y una union.

Si se desea castear un puntero, entonces se usa un cast, solo por mantener el código simple.

Saludos
OpenBSDFreeBSD

pucheto

Cita de: xD4RIOx en 19 Mayo 2011, 18:23 PM
1 - Necesitas una variable extra en la pila local de la función... solo a fin de alojar el puntero que te llega por parámetro, y para enviarlo a otra función. Es innecesario.
Una cosa que no veo aca, hagas como hagas, si o si vas a tener que pasarle el puntero a la otra funci\'on. No veo donde esta el problema. En tu codigo tambien tenes que pasar el puntero a una funcion.

Cita de: xD4RIOx en 19 Mayo 2011, 18:23 PM
2 - Si declaras desde el main los datos como instancias de la union para evitar las variables locales innecesarias, pierdes la posibilidad de tener flujos donde se manejen solo pacientes, o solo médicos (en el ejemplo), sin pasar previamente por un dispatcher. Si los dispatchers son siempre necesarios, es porque tienes dos flujos separados. Seguramente perderás más de dos bytes haciendo dispatchers para cada flujo, y es la diferencia entre crear una instancia de cada tipo, y una union.

Si se desea castear un puntero, entonces se usa un cast, solo por mantener el código simple.

Saludos
Tampoco veo pq se pierde la posibilidad de tener secciones donde solo se manejan solo medicos o solo pacientes.

Si podes pone un ejemplo donde se vea mejor.

samur88

Una cosa, que me he liado, entonces que sería mas optimo hacer el cast directamente o hacer lo que propone pucheto mediante uniones y estructuras.

Un saludo y gracias por las respuestas.

D4RIO

El cast directamtnte es mejor.


Cita de: pucheto en 19 Mayo 2011, 18:56 PM
Una cosa que no veo aca, hagas como hagas, si o si vas a tener que pasarle el puntero a la otra funci\'on. No veo donde esta el problema. En tu codigo tambien tenes que pasar el puntero a una funcion.
No fue lo que expuse. La diferencia, aclaro, es que necesitas una variable local para usar la union.

Cita de: pucheto en 19 Mayo 2011, 18:56 PM
Tampoco veo pq se pierde la posibilidad de tener secciones donde solo se manejan solo medicos o solo pacientes.

Si podes pone un ejemplo donde se vea mejor.
Este sería el otro caso. Si no usas una variable local, pasas las uniones desde que las creas... probablemente desde el main. Entonces, si una función o grupo de funciones trabajan con "struct paciente", necesitas una funcion intermediaria (un dispatcher), que les pase el puntero. Eso significa una funcion para adaptar a cada flujo que use solo una estructura, cosa que no pasaría si las estructuras fueran siempre diferentes. Solo los flujos que aceptan ambos tipos necesitan el dispatcher.

Saludos
OpenBSDFreeBSD

Littlehorse

#16
Dependiendo el problema depende que solución es mas aceptable, y esto ultimo siempre va a ser subjetivo. Personalmente para este caso en particular me parece mas correcta la idea de Pucheto que hacer un cast directo. Tal vez las ventajas del planteo de Pucheto no son tan visibles en 10 lineas pero se notarían si tenes que ponerte al día en un proyecto relativamente extenso y encima mantenerlo, aunque esto por supuesto depende de cada uno.

Saludos
An expert is a man who has made all the mistakes which can be made, in a very narrow field.

pucheto

Cita de: xD4RIOx en 19 Mayo 2011, 21:25 PM
El cast directamtnte es mejor.

No fue lo que expuse. La diferencia, aclaro, es que necesitas una variable local para usar la union.
Este sería el otro caso. Si no usas una variable local, pasas las uniones desde que las creas... probablemente desde el main. Entonces, si una función o grupo de funciones trabajan con "struct paciente", necesitas una funcion intermediaria (un dispatcher), que les pase el puntero. Eso significa una funcion para adaptar a cada flujo que use solo una estructura, cosa que no pasaría si las estructuras fueran siempre diferentes. Solo los flujos que aceptan ambos tipos necesitan el dispatcher.

Saludos
Del 1er punto sigo sin ver que variable local uso...
Del punto 2, si una funcion trabaja con un paciente solamente, le paso el puntero al paciente, no toda el struct con el flag... el struct con el flag solamente lo uso cuando tenga q manejar de manera generica a medicos y pacientes por igual, y tal vez aplicarle alguna funcion generica que necesite tratarlos de maneras distintas. Depende mucho del contexto de uso todo esto.

D4RIO

Esto ya es un mareo. A ver:
Cita de: Littlehorse en 19 Mayo 2011, 23:49 PM
Dependiendo el problema depende que solución es mas aceptable, y esto ultimo siempre va a ser subjetivo. Personalmente para este caso en particular me parece mas correcta la idea de Pucheto que hacer un cast directo. Tal vez las ventajas del planteo de Pucheto no son tan visibles en 10 lineas pero se notarían si tenes que ponerte al día en un proyecto relativamente extenso y encima mantenerlo, aunque esto por supuesto depende de cada uno.

Supongo que será por lo relativo, pero yo, siendo nuevo en un proyecto, preferiría mil veces encontrar esto:
    if (flag == esMedico)
            funcionMedico((struct medico*)puntero);

    if (flag == esPaciente)
            funcionPaciente((struct paciente*)puntero);


que es explícito en lo que hace, y no esto:
    case f_medico:
        //lo manejo como medico
        handleMedico(m.data.m);
        break;
    case f_paciente:
        //lo manejo como paciente
        handlePaciente(m.data.p);
        break;


que es como... poco claro, a mi parecer.


Cita de: pucheto en 20 Mayo 2011, 03:10 AM
Del 1er punto sigo sin ver que variable local uso...
Si no lo entiendo mal, recibes un puntero en la función, y haces algo como:
Código (cpp) [Seleccionar]
void
funcion(void *punt ...) {
    personaHospital p = (personaHospital) *punt; /* algo asi, que requiere una variable local p, o un puntero */

    switch(p.flag){
        case f_medico:
            //lo manejo como medico
            handleMedico(m.data.m);
            break;
        case f_paciente:
            //lo manejo como paciente
            handlePaciente(m.data.p);
            break;
    }
}


Que no era necesaria sin la union...

Cita de: pucheto en 20 Mayo 2011, 03:10 AM
Del punto 2, si una funcion trabaja con un paciente solamente, le paso el puntero al paciente, no toda el struct con el flag... el struct con el flag solamente lo uso cuando tenga q manejar de manera generica a medicos y pacientes por igual, y tal vez aplicarle alguna funcion generica que necesite tratarlos de maneras distintas. Depende mucho del contexto de uso todo esto.
Creo que nunca se manejarían "por igual"... si tuvieran miembros en común que se manejen por igual, entonces metería algo como herencia. Una estructura compartida entre ambos al inicio de la estructura de cada uno. Y así los despachos con un único puntero siguen siendo válidos, aunque en realidad habrá código compartido tanto como código para cada implementación.

Estamos tratando de dar solución a ningún problema, creo que eso es lo más confuso  :)

Muestrame el código que pondrías para darme una mejor idea de tu idea.

Saludos
OpenBSDFreeBSD

pucheto

Ta, estabamos pensando distinto el problema... Yo suponia que el tipo 'personaHospital' venia ya de arriba, pq lo crea una funcion o algo asi... mi funcion con la version que yo proponia era tomar directamente el struct personaHospital, y no un puntero para despues convertirlo.

Mi idea era hacerlo algo similar a herencia.