2010-12-02 6 views
7

Es posible hacer ciertos tipos de tipo genérico-funciones como macros en C, por ejemplo, cosas como:Programación de tipo genérico con macros: ¿trucos para determinar el tipo?

#define SQRT(x) (sizeof(x) == sizeof(float) ? sqrtf((x)) : \ 
       sizeof(x) == sizeof(double) ? sqrt((x)) : \ 
       sqrtl((x))) 

esto funciona (en su mayoría) como se espera, siempre y cuando x es un tipo de punto flotante.

Pero, ¿qué ocurre si quiero una macro genérica de tipo que puede tomar un tipo entero o un tipo de puntero, que puede tener el mismo tamaño. ¿Hay alguna manera inteligente de probar si el argumento macro es un entero o un puntero? ¿Qué pasa con un entero versus un tipo de punto flotante?

Respuesta

3

No. Las macros no saben de qué tipo son. Realizan una copia y pegado literal de #define. La seguridad de tipo simplemente no existe aquí.

C no es un lenguaje fuertemente tipado en ningún sentido significativo. Si desea cierto tipo de seguridad, use C++, donde puede lograr un poco con plantillas y sobrecarga de funciones.

+0

Las macros no son inherentemente conocidas, pero a veces hay trucos. Por ejemplo, '((0 * (x) -1) <0)' puede determinar si 'x' está firmado o no (dado que' x' tiene un rango de conversión de 'int' o superior). –

0

Es posible tener algún tipo de sistema de comprobación de tipos, pero en realidad es una chapuza en C.

glib hace esto; Puedes echarle un vistazo a cómo lo hacen, o posiblemente usarlo tú mismo (de todos modos, es una biblioteca C ingeniosa).

3

Su resultado no es realmente tipo genérico, ya que el resultado es siempre long double independientemente de qué tipo de argumento es pasado - el tipo de resultado de ?: cuando el segundo y tercer operandos son tipos aritmética es el tipo que habría resultado de aplicar las conversiones aritméticas habituales a esos operandos. Para hacerlo así, podría utilizar typeof extensión de GCC:

#define SQRT(x) (__typeof__ (x))(sizeof(x) == sizeof(float) ? sqrtf((x)) : \ 
       sizeof(x) == sizeof(double) ? sqrt((x)) : \ 
       sqrtl((x))) 

entero frente de coma flotante también se puede hacer uso de typeof:

(__typeof__ (X))1.1 == 1 

no puedo pensar en una manera de hacer Integer- versus-puntero. Las técnicas descritas en this page son bastante interesantes, sin embargo.

3

Es posible detectar si una expresión es una expresión entera o una expresión char*, al menos en arquitecturas donde fundido desde un puntero a uintptr_t está bien definida:

#define INT_OR_CHARP(X) (((uintptr_t)((X)+1) - (uintptr_t)(X)) == 1) 

Esto detectará si X es un puntero a una tipo T con sizeof(T) > 1. Esto no funcionará para void* y otros casos de esquina. Y como el X se evalúa dos veces, tendría que observar los efectos secundarios.

para evitar problemas de desbordamiento de enteros si X es por ejemplo del tipo signed int puede reemplazar con (X)

(1 ? (X) : (uintmax_t)0) 

Esto garantiza que si X es una expresión entera esta será de tipo uintmax_t. El +1 tal vez se ajuste, pero el resultado siempre está bien definido y la diferencia entre las dos partes siempre será 1.Si X es una expresión de puntero, esto es así porque cualquier expresión entera constante de valor 0 también es una constante de puntero nulo .

En total esto da

#define INT_OR_CHARP(X) (((uintptr_t)((1 ? (X) : (uintmax_t)0)+1) - (uintptr_t)(1 ? (X) : (uintmax_t)0)) == 1) 
+0

No veo cómo '(1? (X): (uintmax_t) 0)' es útil. ¿Es eso una especie de truco con el operador '?:' Forzando tipos? Además, los tipos sin firmar no se desbordan. –

+0

@R .: 'X' puede ser de cualquier tipo entero. Entonces, si fuera 'int', digamos' (X) + 1' podría desbordarse. Si es un tipo entero (firmado o no) '(1? (X): (uintmax_t) 0)' siempre es del tipo 'uintmax_t' pero con el valor' X'. Si 'X' es de tipo puntero, esa expresión seguirá siendo del mismo tipo de puntero. –

1

La palabra clave _Generic se añadió en el C11 standard para este propósito.

Funciona como una declaración de cambio para tipos de expresiones.

Su ejemplo se puede escribir usando esta palabra clave como esto:

#define SQRT(X) _Generic((X), 
    float: sqrtf, \ 
    double: sqrt, \ 
    default: sqrtl \ 
)(X) 

GCC proporciona apoyo a esta palabra clave ya version 4.9.

Cuestiones relacionadas