É uma boa prática usar o #ifdef durante o desenvolvimento para alternar entre diferentes tipos de comportamento? Por exemplo, eu quero mudar o comportamento do código existente, tenho várias idéias de como mudar o comportamento e é necessário alternar entre diferentes implementações para testar e comparar diferentes abordagens. Normalmente, as alterações no código são complexas e influenciam métodos diferentes em arquivos diferentes.
Eu costumo introduzir vários identificadores e fazer algo assim
void foo()
{
doSomething1();
#ifdef APPROACH1
foo_approach1();
#endif
doSomething2();
#ifdef APPROACH2
foo_approach2();
#endif
}
void bar()
{
doSomething3();
#ifndef APPROACH3
doSomething4();
#endif
doSomething5();
#ifdef APPROACH2
bar_approach2();
#endif
}
int main()
{
foo();
bar();
return 0;
}
Isso permite alternar entre diferentes abordagens rapidamente e fazer tudo em apenas uma cópia do código-fonte. É uma boa abordagem para o desenvolvimento ou existe uma prática melhor?
c++
programming-practices
development-process
Yury Shkliaryk
fonte
fonte
#ifdef
blocos se o bloco estiver desativado. Encontramos casos em que o código pode facilmente ficar obsoleto e não compilar se você não criar rotineiramente todos os caminhos.#ifdefs
menos complicado.Respostas:
Eu preferiria usar ramificações de controle de versão para este caso de uso. Isso permite que você diferencie as implementações, mantenha um histórico separado para cada uma e, quando tiver tomado sua decisão e precisar remover uma das versões, você apenas descartará esse ramo em vez de passar por uma edição propensa a erros.
fonte
git
é especialmente especialista nesse tipo de coisa. Talvez não seja o casosvn
,hg
ou outros, mas ainda pode ser feito.git branch
!Quando você está segurando um martelo, tudo parece um prego. É tentador quando você sabe como
#ifdef
funciona usá-lo como uma forma de obter um comportamento personalizado no seu programa. Eu sei porque cometi o mesmo erro.Eu herdei um programa legado escrito em MFC C ++ que já era usado
#ifdef
para definir valores específicos da plataforma. Isso significava que eu poderia compilar meu programa para ser usado em uma plataforma de 32 bits ou de 64 bits simplesmente definindo (ou em alguns casos não definindo) valores de macro específicos.Surgiu o problema de que eu precisava escrever um comportamento personalizado para um cliente. Eu poderia ter criado uma filial e criado uma base de código separada para o cliente, mas isso teria feito um inferno na manutenção. Eu também poderia ter definido valores de configuração para serem lidos pelo programa na inicialização e usado esses valores para determinar o comportamento, mas precisaria criar configurações personalizadas para adicionar os valores de configuração adequados aos arquivos de configuração de cada cliente.
Fiquei tentado e cedi. Escrevi
#ifdef
seções no meu código para diferenciar os vários comportamentos. Não se engane, não foi nada demais no início. Foram feitas pequenas alterações de comportamento que me permitiram redistribuir versões do programa para nossos clientes e não preciso ter mais de uma versão da base de código.Com o tempo isso se tornou o inferno manutenção de qualquer maneira porque o programa não se comportou de forma consistente em toda a linha. Se eu quisesse testar uma versão do programa, precisava necessariamente saber quem era o cliente. O código, embora eu tentasse reduzi-lo a um ou dois arquivos de cabeçalho, estava muito confuso e a abordagem de correção rápida
#ifdef
fornecida forneceu que essas soluções se espalhassem pelo programa como um câncer maligno.Desde então, aprendi minha lição, e você também deveria. Use-o se for absolutamente necessário e use-o estritamente para alterações na plataforma. A melhor maneira de abordar as diferenças de comportamento entre programas (e, portanto, clientes) é alterar apenas a configuração carregada na inicialização. O programa permanece consistente e torna-se mais fácil de ler e de depurar.
fonte
Temporariamente, não há nada errado com o que você está fazendo (por exemplo, antes do check-in): é uma ótima maneira de testar diferentes combinações de técnicas ou ignorar uma seção do código (embora isso fale de problemas por si só).
Mas uma palavra de advertência: não mantenha #ifdef branches , é um pouco mais frustrante do que perder meu tempo lendo a mesma coisa implementada de quatro maneiras diferentes, apenas para descobrir qual eu deveria estar lendo .
Ler sobre um #ifdef exige esforço, pois você precisa se lembrar de ignorá-lo! Não faça isso mais difícil do que absolutamente deve ser.
Use #ifdefs com a menor moderação possível. Geralmente, há maneiras de fazer isso em seu ambiente de desenvolvimento para diferenças permanentes , como compilações de depuração / versão ou arquiteturas diferentes.
Eu escrevi recursos de biblioteca que dependiam das versões incluídas da biblioteca, que exigiam #ifdef splits. Portanto, às vezes, pode ser o único caminho, ou o mais fácil, mas mesmo assim você deve ficar chateado por mantê-los.
fonte
Usar #ifdefs como esse dificulta a leitura do código.
Portanto, não, não use #ifdefs assim.
Pode haver toneladas de argumentos sobre por que não usar ifdefs, para mim este é o suficiente.
Pode fazer muitas coisas:
Tudo dependendo de quais abordagens são definidas ou não. O que ele faz não é absolutamente claro à primeira vista.
fonte