2012-01-28 10 views
5

El siguiente código, creo, describe lo que estoy tratando de hacer. Específicamente, deseo convertir un puntero de función en un tipo de función genérico, con la única diferencia en la firma de diferentes tipos de puntero.punteros de funciones de fundición con diferentes tipos de punteros como un argumento

Ahora, soy consciente de que no es un requisito para los punteros de función para ser compatibles como se discute en this question, pero no estoy seguro de si tienen un argumento de diferentes satisface tipo puntero que requisito de compatibilidad.

El código se compila y se ejecuta, pero, como se espera, proporciona advertencias sobre la asignación desde un tipo de puntero incompatible. ¿Hay alguna manera de satisfacer al compilador y lograr lo que busco?

#include <stdio.h> 

int float_function(float *array, int length) 
{ 
    int i; 
    for(i=0; i<length; i++){ 
     printf("%f\n", array[i]); 
    } 
} 

int double_function(double *array, int length) 
{ 
    int i; 
    for(i=0; i<length; i++){ 
     printf("%f\n", array[i]); 
    } 
} 


int main() 
{ 
    float a[5] = {0.0, 1.0, 2.0, 3.0, 4.0};  
    double b[5] = {0.0, 1.0, 2.0, 3.0, 4.0}; 

    int (*generic_function)(void*, int) = NULL; 

    generic_function = &float_function; 
    generic_function(a, 5); 

    generic_function = &double_function; 
    generic_function(b, 5); 

    return 0; 
} 

Respuesta

5

La manera más limpia es en mi humilde opinión para realizar el molde dentro de la función. Esto obligará a todas las firmas de funciones a ser las mismas, y mantiene los moldes fuera del código de la persona que llama. (Esta es, por ejemplo, la forma en que qsort() la quiere)

int double_function(void *p, unsigned size) 
{ 
    double *array = p  
    unsigned uu; 

    for(uu=0; uu < size; uu++){ 
     printf("%f\n", array[uu]); 
    } 
return 42; 
} 
+0

De hecho, había pensado en eso. El problema es que estoy llamando a una biblioteca (¡que puede tener una gran cantidad de problemas en sí misma!). En consecuencia, necesitaría ajustar cada función que estoy llamando por separado, que era una especie de lo que intentaba evitar (aunque todavía mantiene la lógica interna simple). –

+0

No había pensado en el caso de la función de la biblioteca. Será difícil mantener contentos tanto al compilador como al lector humano.Tal vez las funciones de envoltura (o los horribles preprocesadores-hacks ;-) sean la forma correcta. – wildplasser

+0

Acepto que una función de envoltura es el método más limpio. Un poco más de código, pero tiene una buena separación lógica. También me permite cambiar la firma e incluir el código en el contenedor. –

1

EDIT: Como @Mat señala, para satisfacer la especificación de C, que había necesidad de emitir el puntero de función volver al tipo original antes de llamarlo, lo que haría que todo este ejercicio un poco menos útil ;

((int(*)(float*,int))generic_function)(a, 5); 

Otra solución (inspirado en la respuesta de @wildplasser) es envolver las funciones de funciones teniendo void * y la realización de un yeso en ese parámetro en su lugar. Hacerlo "por macro" es bastante simple;

#define WRAP(FN, TYPE) int FN##_wrapped(void* p, int len) { return FN((TYPE*)p, len); } 
WRAP(float_function, float) 
WRAP(double_function, double) 

Luego puede usar las siguientes líneas bastante limpias;

generic_function = float_function_wrapped; 
generic_function(a, 5); 

Dicho esto, fundición de puntero no es generalmente una solución que había abogado, pero tiene sus casos de uso.

+1

Eso simplemente apaga el compilador (oculta el problema debajo de la alfombra). No es un buen consejo IMO. – Mat

+0

¿Existe un problema inherente con la técnica de que el compilador no deba cerrarse? –

+1

Sí, está alternando entre tipos de punteros de función incompatibles y llamando a la función de conversión sin devolverla a su tipo real. Ese es un comportamiento indefinido de acuerdo con el estándar. – Mat

2

Sí, declararlo sin un prototipo.

int (*generic_function)() = NULL; 

Ahora puede asignar cualquier función que devuelve int, pero también puede pasar cualquier argumento y el compilador no está obligado a rechazar los argumentos incompatibles.

+0

Pero la desventaja de esto será que el compilador no se quejará si intenta utilizar un puntero a strcmp() o fprintf() como puntero a la función. – wildplasser

+0

@wildplasser ese es el mismo problema que obtienes cuando usas 'void *' como el tipo de parámetro. Todavía puede pasar un 'int *' cuando en el cuerpo quiere un 'doble *' y tal. –

+0

Sí, eso es correcto. Pero el compilador * al menos * podría verificar si un puntero y un int se pasan como argumentos. – wildplasser

Cuestiones relacionadas