c++ - telefonica - macroweb
¿Por qué uno usaría MACRO+0!=0 (4)
¡Hagamos una mesa!
X #if X #if X+0 != 0
<undef> false false
<empty> error false
0 false false
1 true true
2 true true
a false false
xyz false false
12a error error
12 a error error
Entonces, la única diferencia que hemos encontrado (gracias a los comentaristas) es el caso en el que X está definido pero no tiene valor (como una cadena vacía).
Nunca antes había visto la variante
+0 != 0
.
En mi base de código actual veo este siguiente patrón:
#if SOMETHING_SUPPORTED+0 != 0
...
#endif
Desafortunadamente, esta es una base de código muy antigua y nadie sabe cómo y por qué comenzó. Creo que comenzó en C y se convirtió lentamente en C con clases y ahora tiende a C ++
No puedo ver ninguna ventaja obvia de usar una construcción anterior en lugar del "clásico", pero tal vez me falta algo:
#if SOMETHING_SUPPORTED
...
#endif
¿Sabes por qué usaría
#if MACRO+0 != 0
lugar de
#if MACRO
?
Es otra forma de escribir
#if defined(MACRO) && MACRO != 0
El +0 está ahí para garantizar que el resultado sea un número.
Si
MACRO
no está definido, evita el error de sintaxis que resultaría de
#if MACRO != 0
.
Cosas de mala calidad, pero no te metas a menos que tengas que hacerlo.
La pista aquí es que la base del código es muy antigua.
Es probable que este truco exista porque el código una vez fue portado a un compilador con algún preprocesador muy antiguo que no trata las macros
indefinidas
como 0 en preprocesadores
#if
condicionales.
Es decir, a partir de 1989 ANSI C se estandarizó que si tenemos:
#if foo + bar - xyzzy
la directiva está sujeta a reemplazo de macro, de modo que si
foo
,
bar
o
xyzzy
son macros, se reemplazan.
Luego, los identificadores restantes que no fueron reemplazados se reemplazan con
0
.
Entonces, si
foo
se define como
42
, pero
bar
y
xyzzy
no se definen en absoluto, obtenemos:
#if 42 + 0 - 0
y no, digamos, mala sintaxis:
#if 42 + -
o algún otro comportamiento, como diagnósticos sobre la
bar
no definida.
En un preprocesador donde las macros indefinidas se tratan como espacios en blanco,
#if SOMETHING_SUPPORTED
expande a solo
#if
, que es erróneo.
Esta es la única forma en que este truco
IDENT+0
tiene sentido real.
Simplemente no querrá hacer esto si puede confiar en que el preprocesamiento cumple con la norma ISO C.
La razón es que si se espera que
SOMETHING_SUPPORTED
tenga valores numéricos, es erróneamente disperso para definirlo simplemente como un espacio en blanco.
Lo ideal es detectar cuándo sucedió esto y detener la compilación con un diagnóstico.
En segundo lugar, si admite un uso tan sesgado, es casi seguro que desea que un símbolo explícitamente definido pero en blanco se comporte como si tuviera el valor 1, no el valor 0. De lo contrario, está creando una trampa. Alguien podría hacer esto en la línea de comando del compilador:
-DSOMETHING_SUPPORTED=$SHELL_VAR # oops, SHELL_VAR expanded to nothing
o en código:
#define SOMETHING_SUPPORTED /* oops, forgot "1" */
¡Nadie va a
agregar
un
#define
o
-D
para un símbolo con la intención de
desactivar
la función que controla!
El programador que inserte un
#define SOMETHING_SUPPORTED
sin el
1
se sorprenderá por el comportamiento de
#if SOMETHING_SUPPORTED+0
que omite el material que se pretendía habilitar.
Es por eso que sospecho que pocos programadores de C que leen esto alguna vez han visto tal uso, y por qué sospecho que es solo una solución para el comportamiento del preprocesador cuyo efecto previsto es omitir el bloque si falta
SOMETHING_SUPPORTED
.
El hecho de que crea una "trampa de programador" es solo un efecto secundario de la solución.
Para solucionar un problema de este tipo de preprocesador sin crear una trampa de programador es tener, en algún lugar temprano en la unidad de traducción, esto:
#ifndef SOMETHING_SUPPORTED
#define SOMETHING_SUPPORTED 0
#endif
y luego en otro lugar solo use
#if SOMETHING_SUPPORTED
.
Tal vez ese enfoque no se le ocurrió al programador original, o tal vez ese programador pensó que el truco
+0
era bueno y le dio valor a su autocontención.
#if X+0 != 0
es diferente a
#if X
en el caso de que
X
se defina como vacío (nota: esto es diferente al caso de
X
no se define), por ejemplo:
#define X
#if X // error
#if X+0 != 0 // no error; test fails
Es muy común definir macros vacías: la configuración del proyecto puede generar un encabezado común que contiene un montón de líneas
#define USE_FOO
,
#define USE_BAR
para habilitar las características que admite el sistema, etc.
El
!= 0
es redundante, el código podría haber sido
#if X+0
.
Entonces, el beneficio de usar
#if X+0
es que si
X
se define como vacía, la compilación continúa con el bloqueo omitido, en lugar de desencadenar un error.
Si esta es una buena idea es discutible, personalmente usaría
#ifdef
para macros booleanas como
USE_SOME_FEATURE
y
#if
para macros donde el valor podría ser un rango de enteros, por ejemplo;
y me gustaría ver un error si accidentalmente uso
#if
con algo definido como vacío.