#ifdef vs #if - o que é melhor / mais seguro como um método para ativar / desativar a compilação de seções específicas do código?

StackOverflow https://stackoverflow.com/questions/135069

Pergunta

Esta pode ser uma questão de estilo, mas há um pouco de uma divisão em nossa equipe dev e eu me perguntei se alguém tinha alguma idéia sobre o assunto ...

Basicamente, temos algumas declarações de depuração de impressão que desligar durante o desenvolvimento normal. Pessoalmente eu prefiro fazer o seguinte:

//---- SomeSourceFile.cpp ----

#define DEBUG_ENABLED (0)

...

SomeFunction()
{
    int someVariable = 5;

#if(DEBUG_ENABLED)
    printf("Debugging: someVariable == %d", someVariable);
#endif
}

Alguns da equipe preferem o seguinte entanto:

// #define DEBUG_ENABLED

...

SomeFunction()
{
    int someVariable = 5;

#ifdef DEBUG_ENABLED
    printf("Debugging: someVariable == %d", someVariable);
#endif
}

... qual desses métodos soa melhor para você e por quê? Meu sentimento é que o primeiro é mais seguro, porque há sempre algo definido e não há perigo que poderia destruir outros define em outro lugar.

Foi útil?

Solução

A minha primeira reacção foi #ifdef, é claro , mas acho que #if realmente tem algumas vantagens significativas para isso - aqui está o porquê:

Em primeiro lugar, você pode usar DEBUG_ENABLED no pré-processador e testes compilados. Exemplo - Muitas vezes, eu quero tempos de espera mais longos quando depuração está habilitado, portanto, usando #if, eu posso escrever este

  DoSomethingSlowWithTimeout(DEBUG_ENABLED? 5000 : 1000);

... em vez de ...

#ifdef DEBUG_MODE
  DoSomethingSlowWithTimeout(5000);
#else
  DoSomethingSlowWithTimeout(1000);
#endif

Em segundo lugar, você está em uma posição melhor se você quiser migrar de um #define a uma constante global. #defines geralmente são desaprovadas pela maioria dos programadores C ++.

E, Terceiro, você diz que uma divisão em sua equipe. Meu palpite é que isso significa diferentes membros já adoptaram diferentes abordagens, e você precisa de padronizar. Determinando que #if é o meio escolha preferida que o código usando #ifdef irá compilar -e Executar- mesmo quando DEBUG_ENABLED é falsa. E é muito mais fácil de rastrear e saída de remoção de depuração que é produzido quando não deveria ser do que vice-versa.

Oh, e um ponto menor legibilidade. Você deve ser capaz de usar verdadeiro falso / em vez de 0/1 em sua #define, e porque o valor é um único símbolo lexical, é o tempo que você não precisa de parênteses em torno dele.

#define DEBUG_ENABLED true

em vez de

#define DEBUG_ENABLED (1)

Outras dicas

Os dois estão hediondo. Em vez disso, faça o seguinte:

#ifdef DEBUG
#define D(x) do { x } while(0)
#else
#define D(x) do { } while(0)
#endif

Em seguida, sempre que você precisar depurar o código, colocá-lo dentro D();. E seu programa não está poluída com labirintos hediondos de #ifdef.

#ifdef apenas verifica se um símbolo é definido, dado

#define FOO 0

então

#ifdef FOO // is true
#if FOO // is false, because it evaluates to "#if 0"

Temos tido esse mesmo problema em vários arquivos e há sempre o problema com as pessoas se esquecendo de incluir um "apresenta flag" arquivo (com uma base de código de> 41.000 arquivos é fácil fazer).

Se você tivesse feature.h:

#ifndef FEATURE_H
#define FEATURE_H

// turn on cool new feature
#define COOL_FEATURE 1

#endif // FEATURE_H

Mas então Você se esqueceu de incluir o arquivo de cabeçalho no file.cpp:

#if COOL_FEATURE
    // definitely awesome stuff here...
#endif

Então você tem um problema, as interpreta compilador COOL_FEATURE sendo indefinido como "false" neste caso e não inclui o código. Sim gcc suporta uma bandeira que faz com que um erro de macros indefinidas ... mas a maioria quer define 3 código partido ou não define características de modo que este não seria tão portátil.

Adotamos uma maneira portátil de corrigir para este caso, bem como o teste para o estado de um recurso:. Macros de função

Se você mudou o feature.h acima para:

#ifndef FEATURE_H
#define FEATURE_H

// turn on cool new feature
#define COOL_FEATURE() 1

#endif // FEATURE_H

Mas, então, novamente se esqueceu de incluir o arquivo de cabeçalho no file.cpp:

#if COOL_FEATURE()
    // definitely awseome stuff here...
#endif

O pré-processador teria errored fora por causa do uso de uma função macro indefinida.

Para os efeitos da realização condicional compilação, #if e #ifdef são quase o mesmo, mas não completamente. Se sua compilação condicional depende de dois símbolos, em seguida, #ifdef não vai funcionar tão bem. Por exemplo, suponha que você tem dois símbolos de compilação condicional, PRO_VERSION e TRIAL_VERSION, você pode ter algo como isto:

#if defined(PRO_VERSION) && !defined(TRIAL_VERSION)
...
#else
...
#endif

Usando #ifdef acima torna-se muito mais complicado, especialmente ficando a parte #else ao trabalho.

Eu trabalho em código que usa compilação condicional extensivamente e temos uma mistura de #if & #ifdef. Nós tendemos a usar # ifdef / # ifndef para o caso simples e #if sempre que dois ou mais símbolos estão sendo avaliação.

Eu acho que é inteiramente uma questão de estilo. Nem realmente tem uma vantagem óbvia sobre o outro.

A consistência é mais importante do que qualquer escolha particular, por isso eu recomendo que você se reunir com sua equipe e escolher um estilo, e cumpri-lo.

Eu prefiro:

#if defined(DEBUG_ENABLED)

Uma vez que torna mais fácil para criar um código que procura a condição oposta muito mais fácil de detectar: ??

#if !defined(DEBUG_ENABLED)

vs.

#ifndef(DEBUG_ENABLED)

É uma questão de estilo. Mas eu recomendo uma maneira mais concisa de fazer isso:

#ifdef USE_DEBUG
#define debug_print printf
#else
#define debug_print
#endif

debug_print("i=%d\n", i);

Você pode fazer isso uma vez, em seguida, use sempre debug_print () para imprimir ou não fazer nada. (Sim, isso irá compilar em ambos os casos). Desta forma, seu código não será truncado com as directivas de pré-processamento.

Se você receber o aviso "expressão não tem efeito" e quer se livrar dele, aqui está uma alternativa:

void dummy(const char*, ...)
{}

#ifdef USE_DEBUG
#define debug_print printf
#else
#define debug_print dummy
#endif

debug_print("i=%d\n", i);

#if lhe dá a opção de defini-la como 0 para desativar a funcionalidade, enquanto ainda detectar que a chave está lá.
Pessoalmente, eu sempre #define DEBUG 1 para que eu possa pegá-lo com qualquer um #if ou #ifdef

#if e #define MY_MACRO (0)

Usando #if significa que você criou um "definir" macro, ou seja, algo que será pesquisado no código para ser substituído por "(0)". Este é o "inferno macro" Eu odeio ver em C ++, porque polui o código com modificações de código em potencial.

Por exemplo:

#define MY_MACRO (0)

int doSomething(int p_iValue)
{
   return p_iValue + 1 ;
}

int main(int argc, char **argv)
{
   int MY_MACRO = 25 ;
   doSomething(MY_MACRO) ;

   return 0;
}

dá o seguinte erro em g ++:

main.cpp|408|error: lvalue required as left operand of assignment|
||=== Build finished: 1 errors, 0 warnings ===|

Apenas um erro.

O que significa que a sua macro interagiu com sucesso com o seu código C ++: A chamada para a função foi bem sucedida. Neste caso simples, é divertido. Mas minha própria experiência com macros jogando silenciosamente com o meu código não está cheio de alegria e CUMPRIMENTO, então ...

#ifdef e #define MY_MACRO

Usando meios #ifdef você "define" alguma coisa. Não que você dar-lhe um valor. Ele ainda é poluente, mas, pelo menos, será "substituída por nada", e não visto por código C ++ como declaração de código lagitimate. O mesmo código acima, com um simples definir,-lo:

#define MY_MACRO

int doSomething(int p_iValue)
{
   return p_iValue + 1 ;
}

int main(int argc, char **argv)
{
   int MY_MACRO = 25 ;
   doSomething(MY_MACRO) ;

   return 0;
}

Dá os seguintes avisos:

main.cpp||In function ‘int main(int, char**)’:|
main.cpp|406|error: expected unqualified-id before ‘=’ token|
main.cpp|399|error: too few arguments to function ‘int doSomething(int)’|
main.cpp|407|error: at this point in file|
||=== Build finished: 3 errors, 0 warnings ===|

Então ...

Conclusão

Eu prefiro viver sem macros no meu código, mas por várias razões (que definem guardas de cabeçalho, ou macros de depuração), eu não posso.

Mas, pelo menos, eu gostaria de fazer-lhes o código menos interativa possível com o meu legítimo C ++. O que significa usar #define sem valor, utilizando #ifdef e #ifndef (ou mesmo #if definido como sugerido por Jim Buck), e acima de tudo, dando-lhes nomes tão longa e tão alienígena ninguém em seu / sua mente direita vai usar -lo "por acaso", e que de nenhuma maneira ele irá afetar código legítimo C ++.

Post Scriptum

Agora, como eu estou re-ler o meu post, eu me pergunto se eu não deveria tentar encontrar algum valor que não nunca nunca estaremos correta C ++ para adicionar ao meu definir. Algo como

#define MY_MACRO @@@@@@@@@@@@@@@@@@

que poderia ser usado com #ifdef e #ifndef, mas não deixe de compilação de código se usado dentro de uma função ... Eu tentei isso com sucesso em g ++, e deu o erro:

main.cpp|410|error: stray ‘@’ in program|

Interessante. : -)

O primeiro parece mais claro para mim. Parece make mais natural que uma bandeira, em comparação com definido / não definido.

Ambos são exatamente equivalente. Em uso idiomático, #ifdef é usado apenas para verificar se há definedness (eo que eu usaria no seu exemplo), enquanto #if é usado em expressões mais complexas, tais como #if defined (A) &&! Defined (B).

Um pouco OT, mas de ligar / desligar o registo com o pré-processador é definitivamente sub-óptima em C ++. Existem ferramentas de registro agradáveis ??como Apache do log4cxx que são open-source e não restringem como você distribuir seu aplicativo. Eles também permitem que você níveis de log mudança sem recompilação, têm sobrecarga muito baixa se você desativar o log, e dar-lhe a chance de desativar o log completamente na produção.

Isso não é uma questão de estilo em tudo. Além disso, a questão é, infelizmente, errado. Você não pode comparar estas directivas de pré-processamento no sentido de melhor ou mais seguro.

#ifdef macro

meios "se macro está definido" ou "se macro existe". O valor da macro não importa aqui. Pode ser o que for.

#if macro

se sempre comparar a um valor. No exemplo acima, é a comparação implícita padrão:

#if macro !=0

exemplo para o uso de #if

#if CFLAG_EDITION == 0
    return EDITION_FREE;
#elif CFLAG_EDITION == 1
    return EDITION_BASIC;
#else
    return EDITION_PRO;
#endif

Você agora pode colocar a definição de CFLAG_EDITION quer no seu código

#define CFLAG_EDITION 1 

ou você pode definir a macro como bandeira compilador. Também ver aqui .

Eu costumava usar #ifdef, mas quando eu mudei para Doxygen para documentação, descobri que macros comentado-out não pode ser documentada (ou, pelo menos, Doxygen produz um aviso). Isso significa que eu não possa documentar as macros característica-chave que não estão habilitados.

Embora seja possível definir as macros única para Doxygen, isso significa que as macros nas porções não activos do código serão documentados também. Eu, pessoalmente, quero mostrar os interruptores de recursos e de outra forma só documentar o que está atualmente selecionado. Além disso, torna o código bastante confuso se houver muitas macros que têm de ser definido apenas quando Doxygen processa o arquivo.

Portanto, neste caso, é melhor sempre definir a macros e uso #if.

Eu sempre usei #ifdef e compilador sinalizadores para defini-lo ...

Como alternativa, você pode declarar uma constante global, e usar o C ++, se, em vez do #if pré-processador. O compilador deve otimizar os ramos não utilizados afastado para você, e seu código será mais limpo.

Aqui está o que C ++ Gotchas por Stephen C. Dewhurst diz sobre o uso # se de.

Há uma diferença em caso de maneira diferente para especificar uma condicional definir para o motorista:

diff <( echo | g++ -DA= -dM -E - ) <( echo | g++ -DA -dM -E - )

saída:

344c344
< #define A 
---
> #define A 1

Isto significa que -DA é sinônimo de -DA=1 e se o valor for omitido, então ele pode levar a problemas no caso de uso #if A.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top