Como mencionado em muitas das minhas perguntas anteriores, estou trabalhando com K&R e atualmente estou no pré-processador. Uma das coisas mais interessantes - algo que eu nunca soube antes de qualquer uma de minhas tentativas anteriores de aprender C - é o ##
operador de pré - processador. De acordo com K&R:
O operador pré-processador
##
fornece uma maneira de concatenar os argumentos reais durante a expansão da macro. Se um parâmetro no texto de substituição for adjacente a a##
, o parâmetro será substituído pelo argumento real, o##
e o espaço em branco ao redor serão removidos e o resultado será verificado novamente. Por exemplo, a macropaste
concatena seus dois argumentos:
#define paste(front, back) front ## back
então
paste(name, 1)
cria o tokenname1
.
Como e por que alguém usaria isso no mundo real? Quais são os exemplos práticos de seu uso e há algumas dicas a serem consideradas?
fonte
std::wstring BuildDate = WIDEN(__DATE__) L" " WIDEN(__TIME__);
e criar implicitamente a string inteira de uma vez.Uma coisa que você deve estar ciente ao usar os operadores de pré-processamento token-paste ('
##
') ou stringizing ('#
') é que você precisa usar um nível extra de indireção para que funcionem corretamente em todos os casos.Se você não fizer isso e os itens passados para o operador de colagem de token forem macros, você obterá resultados que provavelmente não são o que deseja:
A saída:
fonte
__LINE__
é um nome de macro especial que é substituído pelo pré-processador com o número da linha atual do arquivo de origem.Aqui está uma pegadinha que encontrei ao atualizar para uma nova versão de um compilador:
O uso desnecessário do operador token-paste (
##
) não é portátil e pode gerar espaços em branco indesejados, avisos ou erros.Quando o resultado do operador token-colando não é um token de pré-processador válido, o operador token-colando é desnecessário e possivelmente prejudicial.
Por exemplo, pode-se tentar construir literais de string em tempo de compilação usando o operador token-colando:
Em alguns compiladores, isso produzirá o resultado esperado:
Em outros compiladores, isso incluirá espaços em branco indesejados:
Versões razoavelmente modernas do GCC (> = 3.3 ou mais) não conseguirão compilar este código:
A solução é omitir o operador token-paste ao concatenar tokens de pré-processador para operadores C / C ++:
O capítulo da documentação do GCC CPP sobre concatenação tem informações mais úteis sobre o operador de colagem de token.
fonte
Isso é útil em todos os tipos de situações, para não se repetir desnecessariamente. A seguir está um exemplo do código-fonte do Emacs. Gostaríamos de carregar várias funções de uma biblioteca. A função "foo" deve ser atribuída
fn_foo
e assim por diante. Definimos a seguinte macro:Podemos então usá-lo:
A vantagem é não ter que escrever ambos
fn_XpmFreeAttributes
e"XpmFreeAttributes"
(e correr o risco de errar a grafia de um deles).fonte
Uma pergunta anterior no Stack Overflow pedia um método suave de geração de representações de string para constantes de enumeração sem muitos redigitações propensas a erros.
Ligação
Minha resposta a essa pergunta mostrou como aplicar pouca magia de pré-processador permite definir sua enumeração assim (por exemplo) ...;
... Com o benefício de que a expansão da macro não apenas define a enumeração (em um arquivo .h), ela também define uma matriz correspondente de strings (em um arquivo .c);
O nome da tabela de strings vem da colagem do parâmetro macro (ou seja, Cor) em StringTable usando o operador ##. Aplicativos (truques?) Como este são onde os operadores # e ## são inestimáveis.
fonte
Você pode usar a colagem de token quando precisar concatenar parâmetros de macro com outra coisa.
Pode ser usado para modelos:
Neste caso, LINKED_LIST (int) daria a você
Da mesma forma, você pode escrever um modelo de função para travessia de lista.
fonte
Eu o uso em programas C para ajudar a impor corretamente os protótipos para um conjunto de métodos que devem estar em conformidade com algum tipo de convenção de chamada. De certa forma, isso pode ser usado para orientação a objetos do homem pobre em C direto:
se expande para algo assim:
Isso impõe a parametrização correta para todos os objetos "derivados" quando você:
o acima em seus arquivos de cabeçalho, etc. Também é útil para manutenção, se você desejar alterar as definições e / ou adicionar métodos aos "objetos".
fonte
SGlib usa ## para basicamente falsificar modelos em C. Como não há sobrecarga de função, ## é usado para colar o nome do tipo nos nomes das funções geradas. Se eu tivesse um tipo de lista chamado list_t, obteria funções nomeadas como sglib_list_t_concat e assim por diante.
fonte
Eu o uso para uma declaração inicial em um compilador C não padrão para incorporado:
fonte
##
?Eu o uso para adicionar prefixos personalizados a variáveis definidas por macros. Então, algo como:
expande para:
fonte
O uso principal é quando você tem uma convenção de nomenclatura e deseja que sua macro tire vantagem dessa convenção de nomenclatura. Talvez você tenha várias famílias de métodos: image_create (), image_activate () e image_release () também file_create (), file_activate (), file_release () e mobile_create (), mobile_activate () e mobile_release ().
Você pode escrever uma macro para lidar com o ciclo de vida do objeto:
Claro, uma espécie de "versão mínima de objetos" não é o único tipo de convenção de nomenclatura à qual isso se aplica - quase a grande maioria das convenções de nomenclatura usa uma subcadeia comum para formar os nomes. Poderia me nomes de função (como acima), ou nomes de campo, nomes de variáveis ou quase qualquer outra coisa.
fonte
Um uso importante no WinCE:
Ao definir a descrição do bit de registro, fazemos o seguinte:
E ao usar o BITFMASK, basta usar:
fonte
É muito útil para registro. Você pode fazer:
Ou, se seu compilador não suportar função e função :
As "funções" acima registram mensagens e mostram exatamente qual função registrou uma mensagem.
Minha sintaxe C ++ pode não estar totalmente correta.
fonte