gcc - página - que es link en html
Cómo hacer que gcc advierta sobre pasar una enumeración incorrecta a una función (6)
Como han señalado otros, C no diferencia entre un tipo enumerado y el tipo entero subyacente. (Algunos compiladores pueden incluir verificación de tipos para enum
s o typedef
s como extensiones; YMMV).
Para obtener la verificación de tipo en C, puede usar struct
s, pero luego pierde el uso de los operadores de comparación incorporados y la capacidad de switch
una variable. Sin embargo, puedes intentar algo como esto:
typedef struct {
enum {
reg8_val_A,
reg8_val_B,
reg8_val_C,
} val;
} reg8;
#define reg8_A (reg8){.val = reg8_val_A}
#define reg8_B (reg8){.val = reg8_val_B}
#define reg8_C (reg8){.val = reg8_val_C}
…
bool
is_A_or_B(reg8 reg) {
if reg.val == reg8_A.val // one way to compare
return true;
switch (reg.val) {
case reg8_val_B: // the other way to compare; note that
return true; // “case reg8_B.val:” will *not* work
case reg8_val_C:
return false;
default:
fprintf(stderr, "bad reg value %d/n", reg.val);
abort();
}
}
(Utiliza algunas características C99).
gcc no parece producir una advertencia con el siguiente código. ¿Cómo puedo hacer que produzca una advertencia?
typedef enum
{
REG8_A,
REG8_B,
REG8_C
}REG8;
typedef enum
{
REG16_A,
REG16_B,
REG16_C
}REG16;
void function(REG8 reg8)
{
}
int main(void)
{
function(REG16_A); // Should warn about wrong enum
}
Con GCC 4.6, http://gcc.gnu.org/onlinedocs/gcc/Warning-Options.html usar las opciones -Wconversion
y -Werror
para evitar conversiones de tipos implícitos. Da un error con el código publicado por Paul R. Pero el código original compila bien de todos modos. No sé por qué.
La única forma en que puedo ver la generación de una advertencia es si está preparado para pasar punteros en lugar de enums desnudos, por ej.
typedef enum
{
REG8_A,
REG8_B,
REG8_C
} REG8;
typedef enum
{
REG16_A,
REG16_B,
REG16_C
} REG16;
void function(REG8 * reg8)
{
}
int main(void)
{
REG16 r = REG16_A;
function(&r);
return 0;
}
No es exactamente una solución elegante, pero da una advertencia, al menos con gcc -Wall
:
$ gcc -Wall warn_enum.c -o warn_enum
warn_enum.c: In function ‘main’:
warn_enum.c:23: warning: passing argument 1 of ‘function’ from incompatible pointer type
$
La razón de tal comportamiento es que está utilizando el compilador de C en lugar de C ++. Y en C enum los tipos no son realmente tipos, las enumeraciones en C solo tienen constantes int y se pueden mezclar libremente con cualquier número entero y cualquier aritmética.
En C ++, en cambio, tiene enums reales como le gustaría pensar en ellos, y la verificación de tipo necesaria se realiza según lo establecido por el estándar de lenguaje.
Su problema se puede resolver de dos maneras:
Use el compilador C ++.
De esta forma tendrás enums reales , como quieras.
Cambie su código para que esté en estilo C puro, es decir, no use enums, ya que en C son simplemente conjuntos constantes en los que el compilador solo le ayuda a ordenar los valores constantes. Y en C, usted será el responsable de mantener constantes los "tipos" de constantes pasadas. Una vez más: para C, los miembros enum son solo constantes int, no se pueden escribir.
#define REG8_A 0
#define REG8_B 1
#define REG8_C 2
#define REG16_A 0
#define REG16_B 1
#define REG16_C 2
Para una manera de hacer esto en C usando GCC''s -Wenum-compare
(que está habilitado por defecto si habilita -Wall
), debe realizar una comparación en la constante de enumeración antes de pasarla a la función para obtener el diagnóstico deseado .
-Wenum-compare
Advierta sobre una comparación entre valores de diferentes tipos enumerados. En C ++ enumeral, las discrepancias en las expresiones condicionales también se diagnostican y la advertencia se habilita de forma predeterminada. En C esta advertencia es habilitada por -Wall.
http://gcc.gnu.org/onlinedocs/gcc/Warning-Options.html
Para que tal comparación ocurra automáticamente cuando llamamos a la función, podemos envolver la función en una macro. Para la legibilidad también defino un macro SAFE_ENUM que realiza una comparación inofensiva en la constante de enumeración (esto es lo que finalmente desencadena la advertencia al intentar pasar la constante de enumeración incorrecta a foo
o bar
).
/**
SAFE_ENUM: evaluate an enumeration constant safely
TYPE: the enumeration type
VAL: the enumeration constant to evaluate
*/
#define SAFE_ENUM(TYPE, VAL) ((VAL) == (TYPE)0 ? (VAL) : (VAL))
typedef enum
{
REG8_DEFAULT,
REG8_A,
REG8_B,
REG8_C
} Reg8;
typedef enum
{
REG16_DEFAULT,
REG16_A,
REG16_B,
REG16_C
} Reg16;
void foo(Reg8 reg8)
#define foo(reg8) foo(SAFE_ENUM(Reg8, reg8))
{
printf("%s called with value %d/n", __func__, reg8);
}
void bar(Reg16 reg16)
#define bar(reg16) bar(SAFE_ENUM(Reg16, reg16))
{
printf("%s called with value %d/n", __func__, reg16);
}
int main(void)
{
foo(REG8_A); // ok
bar(REG16_A); // ok
foo(REG16_B); // warning
bar(REG8_B); // warning
Reg16 a_reg16 = 42;
foo(a_reg16); // warning: foo requires a Reg8 but you gave it a Reg16
}
$ g++ test3.cpp -o test3
test3.cpp: In function ‘int main()’:
test3.cpp:22: error: cannot convert ‘REG16’ to ‘REG8’ for argument ‘1’ to ‘void function(REG8)’