O que ## (hash duplo) faz em uma diretiva de pré-processador?

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

A linha acima é tirada do Unreal 4, e eu sei que poderia perguntá-la nos fóruns do irreal, mas acho que essa é uma pergunta geral do C ++ que merece ser feita aqui.

Eu entendo que a primeira linha define uma macro, no entanto, não sou muito versado em manobras de pré-processador em C ++ e estou perdido por aí. A lógica me diz que a barra invertida significa que a declaração continua na próxima linha.

FThreadSafeStaticStat parece um pouco com um modelo, mas há # acontecendo lá e uma sintaxe que nunca vi antes em C ++

Alguém poderia me dizer o que isso significa? Eu entendo que você pode não ter acesso ao Unreal 4, mas é apenas a sintaxe que não entendo.

DavidColson
fonte
7
Você pode ler sobre o operador ## em cppreference , entre outras coisas
Cubbi,
1
##é / poderia ser chamado de operador de concatenação.
dyp de
1
Oh, isso é muito legal! Isso explica bastante, obrigado. Mas por que a palavra-chave struct é usada? A linha se parece mais com uma definição de variável
DavidColson,
1
O structintroduz um especificador de tipo elaborado , tanto quanto posso dizer.
dyp
2
O nome oficial é "operador de colagem de token" porque combina dois tokens de pré-processamento para produzir outro. Observe que só é válido se o resultado for um token de pré-processamento válido, por exemplo, você não pode fazer + ## 3para fazer +3. (Mas você pode fazer + 3, é claro, sem a operadora)
MM

Respostas:

180

## é o operador de pré-processador para concatenação.

Então, se você usar

DEFINE_STAT(foo)

em qualquer lugar do código, ele é substituído por

struct FThreadSafeStaticStat<FStat_foo> StatPtr_foo;

antes que seu código seja compilado.

Aqui está outro exemplo de uma postagem minha para explicar isso melhor.

#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 seria compilado e executado com sucesso e produziria a seguinte saída:

Stumped?

Quando o pré-processador é invocado neste código,

  • begin é substituído por decode(a,n,i,m,a,t,e)
  • decode(a,n,i,m,a,t,e) é substituído por m ## a ## i ## n
  • m ## a ## i ## n é substituído por main

Assim, efetivamente, begin()é substituído por main().

Susam Pal
fonte
9
Eu não esperava pensar muito para aprender o comportamento de ##, mas acho que agora nunca esquecerei isso. Então obrigado.
NicoBerrogorry
2
Levei um segundo para segui-lo, mas essa foi uma resposta fantástica à pergunta. Obrigado.
n00dle
1

TLDR; ##é para concatenação e #é para stringificação (de cppreference ).

O ##concatena identificadores sucessivos e é útil quando você deseja passar uma função como parâmetro. Aqui está um exemplo em que fooaceita um argumento de função como seu primeiro argumento e os operadores ae bcomo o segundo e terceiro argumentos:

#include <stdio.h>
enum {my_sum=1, my_minus=2};
#define foo(which, a, b) which##x(a, b)
#define my_sumx(a, b) (a+b)
#define my_minusx(a, b) (a-b)

int main(int argc, char **argv) {
    int a = 2;
    int b = 3;
    printf("%d+%d=%d\n", a, b,  foo(my_sum, a, b));  // 2+3=5
    printf("%d-%d=%d\n", a, b, foo(my_minus, a, b)); // 2-3=-1
    return 0;
}

O #concatena o parâmetro e coloca a saída entre aspas. O exemplo é:

#include <stdio.h> 
#define bar(...) puts(#__VA_ARGS__)
int main(int argc, char **argv) {
    bar(1, "x", int); // 1, "x", int
    return 0;
}
hmofrad
fonte