¿Hay alguna opinión sobre si usar #define para definir líneas completas de código para simplificar la codificación es una buena o mala práctica de programación? Por ejemplo, si tuviera que imprimir un montón de palabras juntas, me molestaría escribir
<< " " <<
Para insertar un espacio entre palabras en una declaración cout. Solo podría hacer
#define pSpace << " " <<
y escribe
cout << word1 pSpace word2 << endl;
Para mí, esto no agrega ni resta de la claridad del código y hace que escribir sea un poco más fácil. Hay otros casos en los que puedo pensar que escribir será mucho más fácil, generalmente para la depuración.
Tiene alguna idea sobre esto?
EDITAR: ¡Gracias por todas las excelentes respuestas! Esta pregunta me llegó después de escribir mucho de forma repetitiva, pero nunca pensé que habría otras macros menos confusas para usar. Para aquellos que no quieren leer todas las respuestas, la mejor alternativa es usar las macros de su IDE para reducir la escritura repetitiva.
fuente
Respuestas:
Escribir código es fácil. Leer el código es difícil.
Escribes código una vez. Vive durante años, la gente lo lee cien veces.
Optimice el código para leer, no para escribir.
fuente
Personalmente, lo detesto. Hay varias razones por las que desaliento a las personas de esta técnica:
En tiempo de compilación, los cambios reales en el código pueden ser significativos. El siguiente tipo aparece e incluso incluye un corchete de cierre en su #define o una llamada de función. Lo que está escrito en un cierto punto de código está lejos de lo que va a estar allí después del preprocesamiento.
Es ilegible. Puede ser claro para usted ... por ahora ... si es solo esta una definición. Si se convierte en un hábito, pronto terminará con docenas de #defines y comenzará a perder el rastro usted mismo. Pero lo peor de todo es que nadie más podrá entender qué
word1 pSpace word2
significa exactamente (sin buscar el #definir).Puede convertirse en un problema para herramientas externas. Digamos que de alguna manera terminas con un #define que incluye un corchete de cierre, pero no un corchete de apertura. Todo puede funcionar bien, pero los editores y otras herramientas pueden ver algo
function(withSomeCoolDefine;
bastante peculiar (es decir, informarán errores y demás). (Ejemplo similar: una llamada a una función dentro de una definición: ¿podrán sus herramientas de análisis encontrar esta llamada?)El mantenimiento se vuelve mucho más difícil. Tiene todos los definidos además de los problemas habituales que conlleva el mantenimiento. Además del punto anterior, el soporte de herramientas para la refactorización también puede verse afectado negativamente.
fuente
Mi pensamiento principal sobre esto es que nunca uso "hacer que escribir sea más fácil" como regla cuando escribo código.
Mi regla principal cuando escribo código es hacerlo fácilmente legible. La razón detrás de esto es simplemente que el código se lee un orden de magnitud más veces que se escribe. Como tal, el tiempo que pierde al escribirlo con cuidado, de manera ordenada y correctamente distribuida se invierte en leer más y comprender mucho más rápido.
Como tal, el #define que usa simplemente rompe la forma habitual de alternar
<<
y otras cosas . Rompe la regla de la menor sorpresa y, en mi humilde opinión, no es algo bueno.fuente
Esta pregunta ofrece un claro ejemplo de cómo puede usar mal las macros. Para ver otros ejemplos (y entretenerse) vea esta pregunta .
Dicho esto, daré ejemplos del mundo real de lo que considero una buena incorporación de macros.
El primer ejemplo aparece en CppUnit , que es un marco de prueba de unidad. Al igual que cualquier otro marco de prueba estándar, crea una clase de prueba y luego tiene que especificar de alguna manera qué métodos deben ejecutarse como parte de la prueba.
Como puede ver, la clase tiene un bloque de macros como primer elemento. Si agregué un nuevo método
testSubtraction
, es obvio lo que debe hacer para incluirlo en la ejecución de la prueba.Estos macrobloques se expanden a algo como esto:
¿Cuál preferirías leer y mantener?
Otro ejemplo está en el marco Microsoft MFC, donde asigna funciones a mensajes:
Entonces, ¿cuáles son las cosas que distinguen a las "buenas macros" del horrible tipo maligno?
Realizan una tarea que no se puede simplificar de otra manera. Escribir una macro para determinar un máximo entre dos elementos es incorrecto, porque puede lograr lo mismo utilizando un método de plantilla. Pero hay algunas tareas complejas (por ejemplo, mapear códigos de mensajes a funciones miembro) que el lenguaje C ++ simplemente no maneja con elegancia.
Tienen un uso extremadamente estricto y formal. En ambos ejemplos, los macrobloques se anuncian al iniciar y finalizar las macros, y las macros intermedias solo aparecerán dentro de estos bloques. Tiene C ++ normal, se disculpa brevemente con un bloque de macros y luego vuelve a la normalidad nuevamente. En los ejemplos de "macros malvadas", las macros están dispersas por todo el código y el desafortunado lector no tiene forma de saber cuándo se aplican las reglas de C ++ y cuándo no.
fuente
Será, por supuesto, mejor si sintoniza su editor de texto / IDE favorito para insertar fragmentos de código que le resulta tedioso volver a escribir una y otra vez. Y mejor es el término "educado" para comparar. En realidad, no puedo pensar en ningún caso similar cuando el preprocesamiento supera las macros del editor. Bueno, puede ser uno, cuando por razones misteriosas e infelices está constantemente usando un conjunto diferente de herramientas para la codificación. Pero no es una justificación :)
También puede ser una mejor solución para escenarios más complejos, cuando el preprocesamiento de texto puede hacer cosas mucho más ilegibles y complicadas (piense en la entrada parametrizada).
fuente
<< " " <<
.Los otros ya han explicado por qué no debes hacerlo. Su ejemplo obviamente no merece ser implementado con una macro. Pero, hay una amplia gama de casos en los que tiene que usar macros por razones de legibilidad.
Un ejemplo notorio de una aplicación inteligente de tal técnica es el proyecto Clang : vea cómo
.def
se usan los archivos allí. Con las macros#include
, puede proporcionar una definición única, a veces totalmente declarativa, para una colección de cosas similares que se desenrollarán en declaraciones de tipos,case
declaraciones cuando corresponda, inicializadores predeterminados, etc. Aumenta significativamente la capacidad de mantenimiento: nunca olvidará agregar nuevoscase
declaraciones en todas partes cuando ha agregado una nuevaenum
, por ejemplo.Entonces, como con cualquier otra herramienta poderosa, debe usar el preprocesador C con cuidado. No hay reglas genéricas en el arte de la programación, como "nunca debe usar esto" o "siempre debe usar eso". Todas las reglas no son más que pautas.
fuente
Nunca es apropiado usar #defines así. En su caso, podría hacer esto:
fuente
No.
Para las macros destinadas a usarse en código, una buena guía para probar la idoneidad es rodear su expansión con paréntesis (para expresiones) o llaves (para código) y ver si aún se compilará:
Las macros utilizadas en las declaraciones (como el ejemplo en la respuesta de Andrew Shepherd) pueden salirse con la suya con un conjunto de reglas más flexibles siempre que no alteren el contexto circundante (por ejemplo, cambiar entre
public
yprivate
).fuente
Es una cosa razonablemente válida para hacer en un programa puro "C".
Es innecesario y confuso en un programa C ++.
Hay muchas formas de evitar la escritura repetitiva de código en C ++. Desde el uso de las facilidades proporcionadas por su IDE (incluso con vi, un simple "
%s/ pspace /<< " " <</g
" ahorraría tanto tipeo y aún produciría un código legible estándar). Podría definir métodos privados para implementar esto o, para casos más complejos, la plantilla C ++ sería más limpia y sencilla.fuente
En C ++ esto se puede resolver con sobrecarga del operador. O incluso algo tan simple como una función variadic:
lineWithSpaces(word1, word2, word3, ..., wordn)
es simple y te ahorra escribirpSpaces
una y otra vez.Entonces, aunque en su caso puede no parecer un gran problema, hay una solución que es más simple y más robusta.
En general, hay pocos casos en los que el uso de una macro es significativamente más corto sin introducir la ofuscación, y en su mayoría hay una solución lo suficientemente corta usando las características del lenguaje real (las macros son más que una mera sustitución de cadenas).
fuente
Si, es muy malo. Incluso he visto gente haciendo esto:
para guardar la escritura (lo que está tratando de lograr).
Tal código pertenece solo en lugares como este .
fuente
Las macros son malvadas y solo deben usarse cuando realmente sea necesario. Hay algunos casos en los que son aplicables las macros (principalmente depuración). Pero en C ++ en la mayoría de los casos, puede usar funciones en línea .
fuente
goto
, a todos los macro sistemas posibles, etc.No, no puedes usar macros para guardar la escritura .
Sin embargo, tiene permiso, incluso debe usarlos para separar parte del código que no cambia de los que cambian, y reducir la redundancia. Para esto último, debe pensar en alternativas y elegir macro solo si las mejores herramientas no funcionan. (Para la práctica, la macro está bastante al final de la línea, por lo que tenerla implica el último recurso ...)
Para reducir la escritura, la mayoría de los editores tienen macros, incluso fragmentos de código inteligentes.
fuente
switch
/ etc, puedes apostar que usaré macros para evitar volverme loco y aumentar la legibilidad. Usar macros para guardar la escritura en palabras clave, controlar el flujo, etc. es estúpido, pero decir que nadie puede usarlas para guardar las pulsaciones de teclas en contextos válidos es igualmente estúpido.