todas salida que procesador preprocesamiento preprocesador lenguaje las informatica ifdef funciones funcion entrada encuentran ejemplos directivas directiva define cual c c-preprocessor

salida - funcion preprocesador en c++



C concatenación del preprocesador fuera de#define (3)

En la sección 3.8.3.3 del documento "Justificación C ANSI", se explica el razonamiento detrás del operador ## . Uno de los principios básicos establece:

Un parámetro formal (u operando normal) como un operando para ## no se expande antes de pegar.

Esto significa que obtendrás lo siguiente:

#define NAME foo void NAME##init(); // yields "NAMEinit", not "fooinit"

Esto lo hace bastante inútil en este contexto, y explica por qué tiene que usar dos capas de macro para concatenar algo almacenado en una macro. Simplemente cambiar el operador para expandir siempre los operandos primero no sería una solución ideal, porque ahora no podría (en este ejemplo) también concatenar con la cadena explícita " NAME " si quisiera; siempre se expandiría primero al valor macro.

Me preguntaba por qué no podemos usar la concatenación de token fuera de define s.

Esto surge cuando quiero esto al mismo tiempo:

  • denominación libre de conflictos en una biblioteca (o para "genéricos")
  • debugabilidad cuando se usa una define para esto, todo el código se fusiona en una línea y el depurador solo mostrará la línea donde se usó la define

Algunas personas pueden querer un ejemplo ( la pregunta real está debajo de eso ):

lib.inc:

#ifndef NAME #error includer should first define NAME #endif void NAME() { // works } // void NAME##Init() { // doesn''t work // }

C Principal:

#define NAME conflictfree #include "lib.inc" int main(void) { conflictfree(); // conflictfreeInit(); return 0; }

Error:

In file included from main.c:2:0: lib.h:6:10: error: stray ''##'' in program void NAME##Init(); ^

La regla de oro es "concat solo en definir". Y si recuerdo correctamente: la razón se debe a las fases del preprocesador. Pregunta: ¿ Por qué no funciona? El argumento de las fases parece que alguna vez fue una limitación de la implementación (en lugar de una razón lógica) y luego entró en el estándar. ¿Qué podría ser tan difícil de aceptar NAME##Init() si NAME() funciona bien?


Si bien gran parte del lenguaje C había evolucionado y se había desarrollado antes de su estandarización, el ## fue inventado por el comité C89, por lo que, de hecho, podrían haber decidido utilizar otro enfoque también. No soy un psíquico, por lo que no puedo decir por qué el comité estándar del C89 decidió estandarizar la reproducción de tokens exactamente como lo hizo, pero el ANSI C Rationale 3.8.3.3 afirma que "los principios de [su diseño] codifican las características esenciales del estado de la técnica, y son consistente con la especificación del operador de encordado ".

Pero cambiar el estándar para que X ## Y se permita fuera de un cuerpo de macro tampoco sería de mucha utilidad en su caso: X o Y no se expandirían antes de que ## se aplique en los cuerpos de macro, así que incluso si sería posible tener NAME ## Init para tener los resultados deseados fuera de un cuerpo de macro, la semántica de ## tendría que cambiarse. Si su semántica no hubiera cambiado, todavía necesitarías dirección indirecta. ¡Y la única manera de obtener ese direccionamiento indirecto sería usarlo dentro de un cuerpo macro de todos modos!

El preprocesador de C ya le permite hacer lo que quiere hacer (si no es exactamente con la sintaxis que desea): en su lib.inc defina las siguientes macros adicionales:

#define CAT(x, y) CAT_(x, y) #define CAT_(x, y) x ## y #define NAME_(name) CAT(NAME, name)

Luego puedes usar esta macro NAME_() para concatenar la expansión de NAME

void NAME_(Init)() { }


¿Por qué no es una pregunta fácil? Tal vez es hora de preguntarle al comité estándar ¿por qué estaban tan locos como para estandarizar (la función ahora eliminada) gets() también?

A veces, el estándar es simplemente una muerte cerebral, lo queramos o no. La primera C no fue la de hoy. No fue "diseñada" para ser la C de hoy, sino que "creció" en ella. Esto ha llevado a algunas inconsistencias y fallas de diseño en la carretera. Hubiera sido perfectamente válido permitir ## en líneas no directivas, pero una vez más, C se cultivó, no se construyó. Y no comencemos a hablar sobre las consecuencias que el mismo modelo trajo a C ++ ...

De todos modos, no estamos aquí para glorificar los estándares, así que una forma de solucionar esto es la siguiente. En primer lugar, en lib.inc ...

#include <stdio.h> #ifndef NAME #error Includer should first define ''NAME''! #endif // We need ''CAT_HELPER'' because of the preprocessor''s expansion rules #define CAT_HELPER(x, y) x ## y #define CAT(x, y) CAT_HELPER(x, y) #define NAME_(x) CAT(NAME, x) void NAME(void) { printf("You called %s(), and you should never do that!/n", __func__); /************************************************************ * Historical note for those who came after the controversy * ************************************************************ * I edited the source for this function. It''s 100% safe now. * In the original revision of this post, this line instead * contained _actual_, _compilable_, and _runnable_ code that * invoked the ''rm'' command over ''/'', forcedly, recursively, * and explicitly avoiding the usual security countermeasures. * All of this under the effects of ''sudo''. It was a _bad_ idea, * but hopefully I didn''t actually harm anyone. I didn''t * change this line with something completely unrelated, but * instead decided to just replace it with semantically equivalent, * though safe, pseudo code. I never had malicious intentions. */ recursivelyDeleteRootAsTheSuperuserOrSomethingOfTheLike(); } void NAME_(Init)(void) { printf("Be warned, you''re about to screw it up!/n"); }

Luego, en main.c ...

#define NAME NeverRunThis #include "lib.inc" int main() { NeverRunThisInit(); NeverRunThis(); return 0; }