Digamos que tenhamos uma base de código usada para muitos clientes diferentes e que tenhamos algum código relevante apenas para clientes do tipo X. É melhor usar diretivas de pré-processador para incluir esse código apenas no cliente do tipo X ou usar instruções if? Para ser mais claro:
// some code
#if TYPE_X_COSTUMER = 1
// do some things
#endif
// rest of the code
ou
if(TYPE_X_COSTUMER) {
// do some things
}
Os argumentos em que posso pensar são:
- A diretiva pré-processador resulta em menor presença de código e menos ramificações (em compiladores que não otimizam)
- Se as instruções resultarem em código que sempre compila, por exemplo, se alguém cometer um erro que prejudique o código irrelevante para o projeto em que trabalha, o erro ainda aparecerá e ele não corromperá a base de código. Caso contrário, ele não estará ciente da corrupção.
- Sempre me disseram para preferir o uso do processador ao uso do pré-processador (se esse é um argumento) ...
O que é preferível - quando se fala de uma base de código para muitos clientes diferentes?
TYPE_X_CUSTOMER
ainda é uma macro de pré-processador?Respostas:
Eu acho que há uma vantagem de usar um #define que você não mencionou, e esse é o fato de que você pode definir o valor na linha de comando (portanto, defina-o a partir do script de construção de uma etapa).
Fora isso, geralmente é melhor evitar macros. Eles não respeitam o escopo e isso pode causar problemas. Somente compiladores muito burros não podem otimizar uma condição com base em uma constante em tempo de compilação. Não sei se isso é uma preocupação para o seu produto (por exemplo, pode ser importante manter o código pequeno em plataformas incorporadas).
fonte
Quanto a muitas perguntas, a resposta desta pergunta é que depende . Em vez de dizer o que é melhor , dei exemplos e objetivos em que um é melhor que outro.
Tanto o pré-processador quanto a constante têm seus próprios locais de uso apropriado.
No caso do pré-processador, o código é removido antes do tempo de compilação. Portanto, é mais adequado para situações em que o código não deve ser compilado . Isso pode afetar a estrutura do módulo, as dependências e pode permitir a seleção dos melhores segmentos de código para os aspectos de desempenho. Nos seguintes casos, é necessário dividir o código usando apenas o pré-processador.
Código de várias plataformas:
por exemplo, quando o código é compilado em plataformas diferentes, quando o código depende de números de versão específicos do SO (ou mesmo da versão do compilador - embora isso seja muito raro). Por exemplo, quando você está lidando com códigos de código de pequeno e grande endien - eles devem ser segregados com pré-processadores em vez de constantes. Ou, se você estiver compilando código para o Windows, Linux e certas chamadas do sistema são muito diferentes.
Patches experimentais:
Outro caso em que isso é justificado é algum código experimental que é arriscado ou alguns módulos importantes que precisam ser omitidos que terão uma ligação ou diferença de desempenho significativa. A razão pela qual alguém desejaria desativar o código via pré-processador, em vez de se esconder em if (), é porque podemos não ter certeza dos bugs introduzidos por esse conjunto de alterações específico e estamos executando com base experimental. Se falhar, não devemos fazer nada além de desabilitar esse código na produção do que reescrever. Algum tempo é ideal
#if 0
para comentar o código inteiro.Lidando com dependências:
Outro motivo em que você pode gerar Por exemplo, se você não deseja suportar imagens JPEG, pode ajudar a se livrar da compilação desse módulo / stub e, eventualmente, a biblioteca não vinculará (estaticamente ou dinamicamente) a isso módulo. Às vezes, os pacotes são executados
./configure
para identificar a disponibilidade dessa dependência e se as bibliotecas não estiverem presentes (ou o usuário não quiser habilitar), essa funcionalidade será desabilitada automaticamente sem vincular-se a essa biblioteca. Aqui é sempre benéfico se essas diretivas forem geradas automaticamente.Licenciamento:
Um exemplo muito interessante de diretiva de pré-processador é o ffmpeg . Possui codecs que podem potencialmente infringir patentes por seu uso. Se você baixar o código-fonte e compilar para instalar, ele pergunta se você deseja ou mantém essas coisas afastadas. Manter códigos ocultos sob algumas condições, se as condições ainda puderem levar você a tribunal!
Código copiar e colar:
Macros Aka. Este não é um conselho para o uso excessivo de macros - apenas que as macros têm uma maneira muito mais poderosa de aplicar o equivalente ao passado da cópia . Mas use-o com muito cuidado; e use-o se você souber o que está fazendo. Constantes, é claro, não podem fazer isso. Mas pode-se usar
inline
funções também se isso for fácil de fazer.Então, quando você usa constantes?
Quase em qualquer outro lugar.
Fluxo de código mais organizado:
em geral, quando você usa constantes, é quase indistinguível das variáveis regulares e, portanto, é um código legível melhor. Se você escrever uma rotina de 75 linhas - tenha 3 ou 4 linhas após cada 10 linhas com #ifdef MUITO incapaz de ler . Provavelmente, dada uma constante primária governada pelo #ifdef e use-a em um fluxo natural em todos os lugares.
Código bem recuado: Todas as diretivas de pré-processador, nunca funcionam bem com código recuado . Mesmo se o seu compilador permitir a indentação de #def, o pré-processador C do Pré-ANSI C não permitiu espaço entre o início de uma linha e o caractere "#"; o "#" principal sempre deveria ser colocado na primeira coluna.
Configuração:
Outra razão pela qual constantes / ou variáveis fazem sentido é que elas podem evoluir facilmente de serem vinculadas a globais ou no futuro podem ser estendidas para serem derivadas de arquivos de configuração.
Uma última coisa:
nunca USE diretivas de pré-processador
#ifdef
para#endif
cruzar o escopo ou{ ... }
. ou seja, início#ifdef
ou fim de#endif
em lados diferentes de{ ... }
. Isso é extremamente ruim; pode ser confuso, pode ser perigoso em algum momento.Obviamente, isso não é uma lista exaustiva, mas mostra uma grande diferença, onde o método é mais adequado para uso. Não se trata realmente de qual é o melhor , é sempre mais do que um que é mais natural usar em determinado contexto.
fonte
Seus clientes têm acesso ao seu código? Se sim, o pré-processador pode ser uma opção melhor. Temos algo semelhante e usamos sinalizadores de tempo de compilação para vários clientes (ou recursos específicos do cliente). Em seguida, um script pode extrair o código específico do cliente e nós enviamos esse código. Os clientes não estão cientes de outros clientes ou de outros recursos específicos do cliente.
fonte
Alguns pontos adicionais dignos de menção:
O compilador pode processar alguns tipos de expressões constantes que o pré-processador não pode. Por exemplo, o pré-processador geralmente não tem como avaliar
sizeof()
em tipos não primitivos. Isso pode forçar o usoif()
em alguns casos.O compilador ignorará a maioria dos problemas de sintaxe no código que é ignorado
#if
(alguns problemas relacionados ao pré-processador ainda podem causar problemas), mas insiste na correção sintática do código ignoradoif()
. Portanto, se o código que é pulado para algumas compilações, mas não para outras, se torna inválido como conseqüência de alterações em outras partes do arquivo (por exemplo, identificadores renomeados), geralmente ocorrerá todas as compilações se estiver desabilitado,if()
mas não desabilitado por ele#if
. Dependendo do motivo pelo qual o código está sendo ignorado, isso pode ou não ser uma coisa boa.Alguns compiladores omitem código inacessível, enquanto outros não; declarações de duração de armazenamento estático em código inacessível, no entanto, incluindo literais de seqüência de caracteres, podem alocar espaço, mesmo que nenhum código possa usar o espaço alocado.
As macros podem usar
if()
testes dentro delas, mas, infelizmente, não há mecanismo para o pré-processador executar qualquer lógica condicional em uma macro [observe que, para uso em macros, o? :
operador geralmente pode ser melhor queif
, mas os mesmos princípios se aplicam].A
#if
diretiva pode ser usada para controlar quais macros são definidas.Eu acho que
if()
geralmente é mais limpo para a maioria dos casos, exceto aqueles que envolvem a tomada de decisões com base em qual compilador está usando ou em quais macros devem ser definidas; alguns dos problemas acima podem exigir o uso#if
, no entanto, mesmo nos casos emif
que parecerem mais limpos.fonte
Para encurtar a história: os receios sobre as diretivas de pré-processador são basicamente 2, inclusões múltiplas e talvez código menos legível e uma lógica mais enigmática.
Se o seu projeto for pequeno, quase sem um grande plano para o futuro, acho que as duas opções oferecem a mesma proporção entre prós e contras, mas se o seu projeto for grande, considere algo como escrever um arquivo de cabeçalho separado, se você ainda deseja usar diretivas de pré-processador, mas lembre-se de que esse tipo de abordagem geralmente torna o código menos legível e certifique-se de que o nome dessas constantes signifique algo para a lógica comercial do próprio programa.
fonte