traducir - # indefinición en la práctica?
taxonomia ejemplos (8)
Me pregunto sobre el uso práctico de #undef en C. Estoy trabajando con K & R, y estoy a la altura del preprocesador. La mayor parte de esto fue material (más o menos) entendido, pero algo en la página 90 (segunda edición) me llamó la atención:
Los nombres pueden estar indefinidos con
#undef
, por lo general para garantizar que una rutina sea realmente una función, no una macro:
#undef getchar
int getchar(void) { ... }
¿Es esta una práctica común para defenderse de alguien #define
definiendo una macro con el mismo nombre que tu función? ¿O es realmente más una muestra que no ocurriría en la realidad? (Por ejemplo, nadie en su mente correcta, incorrecta o insana debería estar reescribiendo getchar()
, así que no debería aparecer). Con sus propios nombres de funciones, ¿siente la necesidad de hacer esto? ¿Eso cambia si estás desarrollando una biblioteca para que otros la usen?
¿Es esta una práctica común para defenderse contra alguien # definiendo una macro con el mismo nombre que su función? ¿O es realmente más una muestra que no ocurriría en la realidad? (Por ejemplo, nadie en su mente correcta, incorrecta o insana debería estar reescribiendo getchar (), por lo que no debería aparecer).
Un poco de ambos. Un buen código no requerirá el uso de #undef
, pero hay muchos códigos malos con los que tienes que trabajar. #undef
puede resultar invaluable cuando alguien saca un truco como #define bool int
.
Además de solucionar problemas con las macros que contaminan el espacio de nombres global, otro uso de #undef
es la situación en la que se puede requerir que una macro tenga un comportamiento diferente en diferentes lugares. Este no es un escenario realmente común, pero una pareja que me viene a la mente es:
la macro
assert
puede tener su definición modificada en el medio de una unidad de compilación para el caso en que desee realizar una depuración en alguna parte de su código pero no en otras. Además deassert
necesita ser#undef
para hacer esto, la macroNDEBUG
necesita ser redefinida para reconfigurar el comportamiento deseado deassert
He visto una técnica utilizada para asegurar que los valores globales se definan exactamente una vez usando una macro para declarar las variables como
extern
, pero la macro se redefinirá a nada para el caso único donde las declaraciones / encabezado se usan para definir las variables.
Algo así como (no digo que esto sea necesariamente una buena técnica, solo una que he visto en la naturaleza):
/* globals.h */
/* ------------------------------------------------------ */
#undef GLOBAL
#ifdef DEFINE_GLOBALS
#define GLOBAL
#else
#define GLOBAL extern
#endif
GLOBAL int g_x;
GLOBAL char* g_name;
/* ------------------------------------------------------ */
/* globals.c */
/* ------------------------------------------------------ */
#include "some_master_header_that_happens_to_include_globals.h"
/* define the globals here (and only here) using globals.h */
#define DEFINE_GLOBALS
#include "globals.h"
/* ------------------------------------------------------ */
Debido a que el predeproductor #define
s está todo en un espacio de nombres global, es fácil que surjan conflictos de espacio de nombres, especialmente cuando se usan bibliotecas de terceros. Por ejemplo, si desea crear una función llamada OpenFile
, es posible que no se compile correctamente, porque el archivo de encabezado <windows.h>
define el token OpenFile
para asignar a OpenFileA
o OpenFileW
(dependiendo de si UNICODE
está definido o no). La solución correcta es #undef
OpenFile
antes de definir su función.
Solo lo uso cuando una macro en un archivo #included
está interfiriendo con una de mis funciones (por ejemplo, tiene el mismo nombre). Luego, #undef
la macro para poder usar mi propia función.
Si se puede definir una macro, debe haber una facilidad para undef.
un rastreador de memoria que utilizo define sus propias macros / eliminar macros para rastrear información de archivo / línea. esta macro rompe el SC ++ L.
#pragma push_macro( "new" )
#undef new
#include <vector>
#pragma pop_macro( "new" )
Con respecto a su pregunta más específica: los espacios de nombres a menudo se emulan en C prefijando las funciones de la biblioteca con un identificador.
Las macros ciegas indefinidas van a agregar confusión, reducir el mantenimiento y pueden romper cosas que dependen del comportamiento original. Si fuiste forzado, al menos usa push / pop para preservar el comportamiento original en todos lados.
Aunque creo que Jonathan Leffler te dio la respuesta correcta. Aquí hay un caso muy raro, donde uso un #undef. Normalmente, una macro debe ser reutilizable dentro de muchas funciones; es por eso que lo define en la parte superior de un archivo o en un archivo de encabezado. Pero a veces tienes algún código repetitivo dentro de una función que se puede acortar con una macro.
int foo(int x, int y)
{
#define OUT_OF_RANGE(v, vlower, vupper) /
if (v < vlower) {v = vlower; goto EXIT;} /
else if (v > vupper) {v = vupper; goto EXIT;}
/* do some calcs */
x += (x + y)/2;
OUT_OF_RANGE(x, 0, 100);
y += (x - y)/2;
OUT_OF_RANGE(y, -10, 50);
/* do some more calcs and range checks*/
...
EXIT:
/* undefine OUT_OF_RANGE, because we don''t need it anymore */
#undef OUT_OF_RANGE
...
return x;
}
Para mostrar al lector que esta macro solo es útil dentro de la función, no está definida al final. No quiero alentar a nadie a utilizar tales macros de hack. Pero si es necesario, #desdef al final.
Las macros se usan a menudo para generar gran cantidad de código. A menudo es un uso bastante localizado y es seguro para #undef
cualquier macros de ayuda al final del encabezado en particular para evitar conflictos de nombres, por lo que solo el código generado real se importa en otro lugar y las macros utilizadas para generar el código no lo hacen.
/ Editar: como un ejemplo, he usado esto para generar estructuras para mí. El siguiente es un extracto de un proyecto real:
#define MYLIB_MAKE_PC_PROVIDER(name) /
struct PcApi##name { /
many members …
};
MYLIB_MAKE_PC_PROVIDER(SA)
MYLIB_MAKE_PC_PROVIDER(SSA)
MYLIB_MAKE_PC_PROVIDER(AF)
#undef MYLIB_MAKE_PC_PROVIDER
Que hace
Si lee Plauger''s The Standard C Library (1992), verá que el encabezado <stdio.h>
puede proporcionar getchar()
y getc()
como macros funcionales (con permiso especial para que getc()
evalúe su file puntero argumento más de una vez!). Sin embargo, incluso si proporciona macros, la implementación también está obligada a proporcionar funciones reales que realicen el mismo trabajo, principalmente para que pueda acceder a un puntero de función llamado getchar()
o getc()
y pasarlo a otras funciones.
Es decir, al hacer:
#include <stdio.h>
#undef getchar
extern int some_function(int (*)(void));
int core_function(void)
{
int c = some_function(getchar);
return(c);
}
Como está escrito, la función core_function()
tiene sentido, pero ilustra el punto. También puede hacer lo mismo con las macros isxxxx()
en <ctype.h>
, por ejemplo.
Normalmente, no desea hacer eso; normalmente no desea eliminar la definición de macro. Pero, cuando necesitas la función real, puedes conseguirla. Las personas que proporcionan bibliotecas pueden emular la funcionalidad de la biblioteca C estándar con buenos resultados.
Rara vez es necesario
También tenga en cuenta que una de las razones por las que rara vez necesita usar el #undef
explícito es porque puede invocar la función en lugar de la macro escribiendo:
int c = (getchar)();
Debido a que el token después de getchar
no es un (
, no es una invocación de la macro de función, entonces debe ser una referencia a la función. De manera similar, el primer ejemplo anterior se compilaría y se ejecutaría correctamente incluso sin el #undef
.
Si implementa su propia función con una anulación de macro, puede usar esto con buenos resultados, aunque podría ser un poco confuso a menos que se lo explique.
/* function.h */
…
extern int function(int c);
extern int other_function(int c, FILE *fp);
#define function(c) other_function(c, stdout);
…
/* function.c */
…
/* Provide function despite macro override */
int (function)(int c)
{
return function(c, stdout);
}
La línea de definición de función no invoca la macro porque la function
token after no es (
. La línea de return
invoca la macro.