En una macro GNU C envSet(name), ¿qué significa (void) “” nombre?

6 minutos de lectura

avatar de usuario
OMGtechy

Me encontré con esta sintaxis hoy y no pude entender lo que significaba:

// Uses the GNU C statement expression extension
#define envSet(name) ({ \
static int initialised; \
static bool set; \
(void) "" name; \
if (!initialised || !g_cacheEnv) { \
    const char *value = getenv(name); \
    set = value != NULL; \
    initialised = true; \
} \
set; \
})

La línea específica que no puedo entender es:

(void) "" name; \

¿Podría alguien arrojar algo de luz sobre esto?

  • No creo que sea una extensión…

    – Eugenio Sh.

    30 de septiembre de 2016 a las 13:42

  • @EugeneSh. la línea específica a la que me refería no lo es, pero el contexto en el que se usó es 🙂

    – OMGtechy

    30 de septiembre de 2016 a las 13:44

  • Bueno, entonces el título probablemente sea un poco engañoso…

    – Eugenio Sh.

    30/09/2016 a las 13:45

  • @OMGtechy, ¿de dónde es esa macro? (por curiosidad)

    – ilkkachu

    2 de octubre de 2016 a las 7:34

  • Quizás una alternativa más clara es (void) "Must be a string literal: " name; que documenta la intención y también debería generar un mensaje de error autodocumentado. Un buen compilador también debería optimizar la cadena en el binario.

    -chi

    2 oct 2016 a las 12:10

avatar de usuario
Lundin

Parece una forma de garantizar estáticamente que name es un literal de cadena y no algún otro tipo.

Si lo haces (void)"" "hello"; entonces es una expresión C válida.

Pero si haces algo como (void)"" 1; entonces obtienes un error de sintaxis.

  • Vale la pena mencionar que precediendo por (void) es silenciar la advertencia “no utilizada” (o lo que sea).

    – Eugenio Sh.

    30 de septiembre de 2016 a las 13:26


  • Y quizás también valga la pena mencionar que esto utiliza el hecho de que el preprocesador concatena los literales de cadena adyacentes, por lo que no funcionará si el lado derecho (name) no es un literal de cadena. La conversión se aplica al resultado de la concatenación, no a "" por sí mismo. Editar: oh, no vi la otra respuesta. je.

    – relajarse

    30 de septiembre de 2016 a las 13:38


  • Técnicamente, ocurre la concatenación de cadenas literales después el preprocesador pero antes de todo lo demas. Obtiene una fase de traducción para sí mismo (número 6). La forma más obvia en que esto es visible es que no están fusionados en -E salida, y también puede observarlo con varios trucos macro (la expansión macro es la fase 4).

    – zwol

    30/09/2016 a las 14:35

  • Mi comentario sobre la respuesta de @ milleniumbug también se aplica aquí.

    – PJ Trail

    30 de septiembre de 2016 a las 15:13

  • @PJTraill Lo menciono principalmente por el bien de las personas que podrían confundirse al ver literales de cadena no fusionándose en -E producción.

    – zwol

    30/09/2016 a las 15:57

Se concatenan dos literales de cadena consecutivos. Presumiblemente está comprobando si name es un literal de cadena. Si no es así, el compilador reportará un error.

(void) cast suprimirá advertencias como “declaración sin efecto”.

  • gracias, acepté el otro porque era un ejemplo de cómo lo manejaría el PP 🙂

    – OMGtechy

    30 de septiembre de 2016 a las 13:37

  • @OMGtechy en general, eres libre de aceptar cualquier respuesta, sin dar una excusa. Solo digo…:)

    – máquina_1

    30 de septiembre de 2016 a las 13:53

  • Parece como si fallara envSet(+1). Uno esperaría además getenv(name) para hacer que el compilador valide name a menos que use opciones de verificación suicidamente débiles, lo que me hace preguntarme si esto está destinado a algún entorno peculiar, o simplemente a un ejercicio extraño.

    – PJ Trail

    30 de septiembre de 2016 a las 15:12


  • @machine_1 Solo quiero ser cortés cuando alguien se ha tomado el tiempo de responder a mi pregunta

    – OMGtechy

    30 de septiembre de 2016 a las 17:19

  • @milleniumbug: la primera vez que se ejecuta una expansión particular de la macro, llamará a “getenv”. Si se ejecuta de nuevo, no volverá a llamar a “getenv”, sino que simplemente arrojará el resultado que arrojó la primera vez. Esa será una optimización útil siempre que no cambie ni el entorno ni la cadena pasada. Sin embargo, sería una mala idea si la cadena pasada pudiera cambiar entre invocaciones. Por lo general, se garantiza que los literales de cadena no cambien entre invocaciones y representan la mayoría de los casos de uso para los que una función como esta sería apropiada.

    – Super gato

    30/09/2016 a las 20:17

Mirando el código, creo que el propósito es que llame getenv la primera vez que se llama, almacene en caché el resultado y luego use el resultado almacenado en caché sin tener que llamar getenv más. Si getenv se usa con un literal de cadena, todas las llamadas posteriores solicitarán la misma variable de entorno; si nada pudiera cambiar esa variable de entorno, en consecuencia devolverían el mismo resultado. Si al código se le dio un puntero a una cadena que luego cambió, el resultado almacenado en caché probablemente no sería correcto para la nueva cadena, por lo que el propósito del truco “” es garantizar que eso no suceda.

Debido a que cada literal de cadena que podría usarse necesitaría tener su propia variable estática asociada, el fragmento de código indicado no puede convertirse en una función. Por otro lado, la cantidad de código necesaria para cada repetición parece un poco excesiva. Además, si la misma variable se prueba en más de un lugar del código, cada uno podría terminar con su propio conjunto de variables y código de verificación del entorno.

Dependiendo de cómo se use la función, puede terminar siendo mucho más rápida que el código que necesita probar una variable ambiental cada vez que se llama, y ​​se puede usar desde dentro de una función que se llama dentro de un bucle sin configuración previa (si código de cliente llamado función de “configuración avanzada”, la búsqueda de nombre debe realizarse allí, eliminando la necesidad de verificar dentro del bucle para ver si se ha realizado la búsqueda).

  • Este es de hecho el contexto en el que se usa, y por qué 🙂

    – OMGtechy

    1 oct 2016 a las 20:51

  • Gracias por proporcionar la respuesta más completa e iluminadora hasta ahora. No puedo evitar pensar que…static const char * const name_const = name; /* Require a compile-time constant */ const char * value = getenv (name_const);… ¡hubiera logrado lo mismo mucho más claramente! ¿O me estoy perdiendo algo?

    – PJ Trail

    2 oct 2016 a las 16:27


  • @PJTraill: Dado char foo[16];creo que ese enfoque aceptaría (const char*)fooaunque el contenido de foo podría cambiarse arbitrariamente entre consultas.

    – Super gato

    2 oct 2016 a las 16:31


  • Creo que tiene razón, aunque un compilador sensato utilizado con sensatez debería advertirle que está descartando const. Y los usuarios voluntariosos de C siempre pueden arrojarse al pozo donde el gusano roe y el fuego arde para siempre.

    – PJ Trail

    2 oct 2016 a las 16:36

  • @PJTraill: A falta de reparto, asignar la dirección de foo sería una violación de la restricción. El elenco, sin embargo, generalmente suprimirá cualquier diagnóstico. Además, si hubiera un union con ambos un const-miembro calificado y no calificado de tipos idénticos, creo que escribir a través del miembro no calificado y leer a través de los miembros calificados sería un comportamiento definido, pero no se esperaría que produzca ningún diagnóstico.

    – Super gato

    2 oct 2016 a las 16:48

¿Ha sido útil esta solución?

Esta web utiliza cookies propias y de terceros para su correcto funcionamiento y para fines analíticos y para mostrarte publicidad relacionada con sus preferencias en base a un perfil elaborado a partir de tus hábitos de navegación. Al hacer clic en el botón Aceptar, acepta el uso de estas tecnologías y el procesamiento de tus datos para estos propósitos. Configurar y más información
Privacidad