¿Qué hace ## (doble hash) en una directiva de preprocesador?

91
#define DEFINE_STAT(Stat) \
struct FThreadSafeStaticStat<FStat_##Stat> StatPtr_##Stat;

La línea anterior está tomada de Unreal 4, y sé que podría preguntarla en los foros de Unreal, pero creo que esta es una pregunta general de C ++ que merece ser formulada aquí.

Entiendo que la primera línea define una macro, sin embargo, no estoy bien versado en las travesuras del preprocesador en C ++, por lo que estoy perdido allí. La lógica me dice que la barra invertida significa que la declaración continúa en la siguiente línea.

FThreadSafeStaticStat se parece un poco a una plantilla, pero hay # allí y una sintaxis que nunca antes había visto en C ++

¿Alguien podría decirme qué significa esto? Entiendo que es posible que no tenga acceso a Unreal 4, pero es solo la sintaxis que no entiendo.

DavidColson
fuente
6
Puede leer sobre el operador ## en cppreference , entre otras cosas
Cubbi
1
##es / podría llamarse operador de concatenación.
dyp
1
¡Oh, eso es genial! Explica bastante, gracias. Pero, ¿por qué se usa la palabra clave struct? La línea se parece más a una definición variable
DavidColson
1
El structintroduce un especificador de tipo elaborado por lo que puedo decir.
dyp
2
El nombre oficial es "operador de pegado de tokens" porque combina dos tokens de preprocesamiento para producir otro. Tenga en cuenta que solo es válido si el resultado es un token de preprocesamiento válido, por ejemplo, no puede hacer + ## 3para hacer +3. (Pero puede hacerlo, + 3por supuesto, sin el operador)
MM

Respuestas:

175

## es el operador del preprocesador para la concatenación.

Entonces, si usas

DEFINE_STAT(foo)

en cualquier parte del código, se reemplaza con

struct FThreadSafeStaticStat<FStat_foo> StatPtr_foo;

antes de que se compile su código.

Aquí hay otro ejemplo de una publicación de mi blog para explicar esto con más detalle.

#include <stdio.h>

#define decode(s,t,u,m,p,e,d) m ## s ## u ## t
#define begin decode(a,n,i,m,a,t,e)

int begin()
{
    printf("Stumped?\n");
}

Este programa se compilaría y ejecutaría con éxito y produciría el siguiente resultado:

Stumped?

Cuando se invoca el preprocesador en este código,

  • begin es reemplazado por decode(a,n,i,m,a,t,e)
  • decode(a,n,i,m,a,t,e) es reemplazado por m ## a ## i ## n
  • m ## a ## i ## n es reemplazado por main

Así, efectivamente, begin()se reemplaza con main().

Susam Pal
fuente
8
No esperaba pensar tanto para aprender el comportamiento de ##, pero supongo que ahora nunca lo olvidaré. Así que gracias.
NicoBerrogorry
2
Me tomó un segundo seguirlo, pero esta fue una respuesta fantástica a la pregunta. Gracias.
n00dle