¿Es una buena práctica usar #ifdef durante el desarrollo para cambiar entre diferentes tipos de comportamiento? Por ejemplo, quiero cambiar el comportamiento del código existente, tengo varias ideas sobre cómo cambiar el comportamiento y es necesario cambiar entre diferentes implementaciones para probar y comparar diferentes enfoques. Por lo general, los cambios en el código son complejos y tienen influencia en diferentes métodos en diferentes archivos.
Normalmente presento varios identificadores y hago algo así
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;
}
Esto permite cambiar rápidamente entre diferentes enfoques y hacer todo en una sola copia del código fuente. ¿Es un buen enfoque para el desarrollo o hay una mejor práctica?
c++
programming-practices
development-process
Yury Shkliaryk
fuente
fuente
#ifdef
bloques si el bloque está desactivado. Nos encontramos con casos en los que el código puede quedar obsoleto fácilmente y no compilarse si no construye rutinariamente todas las rutas.#ifdefs
menos engorroso.Respuestas:
Preferiría usar ramas de control de versiones para este caso de uso. Eso le permite diferenciar entre las implementaciones, mantener un historial separado para cada una, y cuando haya tomado una decisión y necesite eliminar una de las versiones, simplemente descarta esa rama en lugar de pasar por una edición propensa a errores.
fuente
git
es especialmente experto en este tipo de cosas. Tal vez no tanto en el caso desvn
,hg
o de otros, pero todavía se puede hacer.git branch
!Cuando sostienes un martillo, todo parece un clavo. Es tentador una vez que sabes cómo
#ifdef
funciona usarlo como una forma de obtener un comportamiento personalizado en tu programa. Lo sé porque cometí el mismo error.Heredé un programa heredado escrito en MFC C ++ que ya se usaba
#ifdef
para definir valores específicos de la plataforma. Esto significaba que podía compilar mi programa para usarlo en una plataforma de 32 bits o en una plataforma de 64 bits simplemente definiendo (o en algunos casos no definiendo) valores macro específicos.Entonces surgió el problema de que necesitaba escribir un comportamiento personalizado para un cliente. Podría haber creado una sucursal y crear una base de código separada para el cliente, pero eso habría hecho un infierno de mantenimiento. También podría haber definido los valores de configuración para que el programa los lea al inicio y utilizar estos valores para determinar el comportamiento, pero luego tendría que crear configuraciones personalizadas para agregar los valores de configuración adecuados a los archivos de configuración para cada cliente.
Me sentí tentado y cedí. Escribí
#ifdef
secciones en mi código para diferenciar los diferentes comportamientos. No se equivoquen, no fue nada exagerado al principio. Se realizaron cambios de comportamiento muy menores que me permitieron redistribuir versiones del programa a nuestros clientes y no necesito tener más de una versión de la base de código.Con el tiempo, esto se convirtió en un infierno de mantenimiento de todos modos porque el programa ya no se comportó de manera consistente en todos los ámbitos. Si quería probar una versión del programa, necesariamente tenía que saber quién era el cliente. El código, aunque traté de reducirlo a uno o dos archivos de encabezado, estaba muy abarrotado y el enfoque de solución rápida que
#ifdef
proporcionaba significaba que tales soluciones se extendieron por todo el programa como un cáncer maligno.Desde entonces aprendí mi lección, y tú también deberías. Úselo si es absolutamente necesario, y úselo estrictamente para cambios de plataforma. La mejor manera de abordar las diferencias de comportamiento entre los programas (y, por lo tanto, los clientes) es cambiar solo la configuración cargada al inicio. El programa se mantiene constante y se vuelve más fácil de leer y de depurar.
fuente
Temporalmente no hay nada de malo en lo que está haciendo (por ejemplo, antes del check-in): es una excelente manera de probar diferentes combinaciones de técnicas o de ignorar una sección de código (aunque eso habla de problemas en sí mismo).
Pero una palabra de advertencia: no guarde las ramas #ifdef, es un poco más frustrante que perder el tiempo leyendo lo mismo implementado de cuatro maneras diferentes, solo para descubrir cuál debería estar leyendo .
¡Leer sobre un #ifdef requiere esfuerzo, ya que debes recordar saltearlo! No lo hagas más difícil de lo que tiene que ser.
Use #ifdefs con la mayor moderación posible. En general, puede hacer esto dentro de su entorno de desarrollo para las diferencias permanentes , como las compilaciones Debug / Release, o para diferentes arquitecturas.
He escrito funciones de biblioteca que dependían de las versiones de biblioteca incluidas, que requerían divisiones #ifdef. Por lo tanto, a veces puede ser la única forma, o la más fácil, pero incluso así debería estar molesto por mantenerlos.
fuente
Usar #ifdefs así hace que el código sea muy difícil de leer.
Entonces, no, no uses #ifdefs así.
Puede haber toneladas de argumentos por qué no usar ifdefs, para mí este es suficiente.
Puede hacer muchas cosas que puede hacer:
Todo depende de qué enfoques se definan o no. Lo que hace no está absolutamente claro a primera vista.
fuente