Diretivas #if, #elif, #else e #endif (C/C++)

A diretiva #if, com as diretivas #elif, #else e #endif, controla a compilação de partes de um arquivo de origem. Se a expressão escrita (após #if) tiver um valor diferente de zero, o grupo de linhas imediatamente após a diretiva #if será mantido na unidade de tradução.

Gramática

conditional :
if-part elif-partsoptelse-partoptendif-line

if-part :
texto da linha if

if-line :
#ifconstant-expression
#ifdefidentifier
#ifndefidentifier

elif-parts :
texto da linha ELIF
ELIF-partes ELIF - texto de linha ELIF

elif-line :
#elifconstant-expression

else-part :
texto de outra linha

else-line :
#else

endif-line :
#endif

Comentários

Cada diretiva #if em um arquivo de origem deve ser correspondida pela diretiva de fechamento #endif. Qualquer número de diretivas #elif pode aparecer entre as diretivas #if e #endif, mas no máximo uma diretiva #else é permitida. A diretiva #else, se presente, deve ser a última diretiva antes de #endif.

As diretivas #if, #elif, #else e #endif podem se aninhar nas partes de texto de outras diretivas #if. Cada diretiva aninhada #else, #elif ou #endif pertence à diretiva #if anterior mais próxima.

Todas as diretivas de compilação condicional, como #if e #ifdef, devem corresponder a uma diretiva #endif de fechamento antes do fim do arquivo. Caso contrário, uma mensagem de erro será gerada. Quando as políticas de compilação condicional estão contidas em arquivos de inclusão, elas devem satisfazer às mesmas circunstâncias: não deve haver nenhuma política de compilação condicional sem correspondência no fim do arquivo de inclusão.

A substituição de macro é executada na parte de linha de comando após um comando #elif, para que uma chamada de macro possa ser usada em constant-expression.

O pré-processador seleciona uma das ocorrências do texto fornecidas para processamento adicional. Um bloco especificado em texto pode ser qualquer sequência de texto. Ele pode ocupar mais de uma linha. Normalmente, texto é o texto de programa que tem significado para o compilador ou o pré-processador.

O pré-processador processa o texto selecionado e o passa para o compilador. Se o texto contiver políticas de pré-processador, o pré-processador executa essas políticas. Somente os blocos de texto selecionados pelo pré-processador são compilados.

O pré-processador seleciona um único item de texto avaliando a expressão de constante após cada diretiva #if ou #elif até encontrar uma expressão de constante verdadeira (diferente de zero). Ele seleciona todo o texto (incluindo outras políticas de pré-processamento que começam com #) até a #elif, a #else ou a #endif associada.

Se todas as ocorrências de constant-expression forem falsas, ou se nenhuma diretiva #elif aparecer, o pré-processador selecionará o bloco de texto após a cláusula #else. Quando não há cláusula #else, e todas as instâncias de constant-expression no bloco #if são falsas, nenhum bloco de texto é selecionado.

A constant-expression é uma expressão constante inteira com estas restrições adicionais:

  • As expressões devem ter o tipo integral e podem incluir apenas constantes de inteiros, constantes de caracteres e o operador defined.

  • A expressão não pode usar sizeof ou um operador de conversão de tipos.

  • O ambiente de destino talvez não consiga representar todos os intervalos de inteiros.

  • A tradução representa o tipo int do mesmo modo que o tipo long, e unsigned int do mesmo modo que unsigned long.

  • O tradutor pode traduzir a constante de caracteres como um conjunto de valores de código diferentes do conjunto para o ambiente de destino. Para determinar as propriedades do ambiente de destino, use um aplicativo criado para esse ambiente para verificar os valores das macros LIMITS.H.

  • A expressão não deve consultar o ambiente e deve permanecer isolada de detalhes da implementação no computador de destino.

Operadores do pré-processador

definido

O operador do pré-processador defined pode ser usado em expressões de constantes especiais, como mostrado pela seguinte sintaxe:

defined(identifier)
definedidentifier

Essa expressão constante será considerada verdadeira (diferente de zero) se o identificador estiver definido. Caso contrário, a condição será false (0). Um identificador definido como texto vazio é considerado definido. O operador defined pode ser usado em diretivas #if e #elif, mas em nenhum outro lugar.

No seguinte exemplo, as diretivas #if e #endif controlam a compilação de uma de três chamadas de função:

#if defined(CREDIT)
    credit();
#elif defined(DEBIT)
    debit();
#else
    printerror();
#endif

A chamada de função para credit será compilada se o identificador CREDIT estiver definido. Se o identificador DEBIT estiver definido, a chamada de função para debit será compilada. Se nenhum identificador for definido, a chamada para printerror será compilada. Tanto CREDIT quanto credit é um identificador distinto em C e C++ porque os casos são diferentes.

As instruções de compilação condicional no exemplo a seguir pressupõem uma constante simbólica definida anteriormente denominada DLEVEL.

#if DLEVEL > 5
    #define SIGNAL  1
    #if STACKUSE == 1
        #define STACK   200
    #else
        #define STACK   100
    #endif
#else
    #define SIGNAL  0
    #if STACKUSE == 1
        #define STACK   100
    #else
        #define STACK   50
    #endif
#endif
#if DLEVEL == 0
    #define STACK 0
#elif DLEVEL == 1
    #define STACK 100
#elif DLEVEL > 5
    display( debugptr );
#else
    #define STACK 200
#endif

O primeiro bloco #if mostra dois conjuntos de diretivas #if, #else e #endif aninhadas. O primeiro conjunto de políticas será processado somente se DLEVEL > 5 for verdadeiro. Caso contrário, as instruções após #else serão processadas.

As diretivas #elif e #else do segundo exemplo são usadas para fazer uma de quatro escolhas, com base no valor de DLEVEL. A constante STACK é definida como 0, 100 ou 200, dependendo da definição de DLEVEL. Se DLEVEL for maior que 5, a declaração

#elif DLEVEL > 5
display(debugptr);

será compilada e STACK não será definido.

A compilação condicional é usada normalmente para evitar várias inclusões do mesmo arquivo de cabeçalho. Em C++, em que classes são definidas com frequência em arquivos de cabeçalho, constructos como esse podem ser usados para impedir várias definições:

/*  EXAMPLE.H - Example header file  */
#if !defined( EXAMPLE_H )
#define EXAMPLE_H

class Example
{
    //...
};

#endif // !defined( EXAMPLE_H )

O código acima verifica se a constante EXAMPLE_H foi definida. Em caso afirmativo, o arquivo já foi incluído e não precisa ser reprocessado. Caso contrário, a constante EXAMPLE_H será definida para marcar EXAMPLE.H como já processado.

__has_include

Visual Studio 2017 versão 15.3 e posteriores: determina se um cabeçalho de biblioteca está disponível para inclusão:

#ifdef __has_include
#  if __has_include(<filesystem>)
#    include <filesystem>
#    define have_filesystem 1
#  elif __has_include(<experimental/filesystem>)
#    include <experimental/filesystem>
#    define have_filesystem 1
#    define experimental_filesystem
#  else
#    define have_filesystem 0
#  endif
#endif

Confira também

Diretivas de pré-processador