Sé que se implementan de manera extremadamente insegura en C / C ++. ¿No se pueden implementar de una manera más segura? ¿Son las desventajas de las macros realmente lo suficientemente malas como para superar la potencia masiva que proporcionan?
programming-languages
macros
Casebash
fuente
fuente
WithEvents
modificador de Visual Basic ? Necesitarías algo como macros semánticas.Respuestas:
Creo que la razón principal es que las macros son léxicas . Esto tiene varias consecuencias:
El compilador no tiene forma de verificar que una macro está semánticamente cerrada, es decir, que representa una "unidad de significado" como lo hace una función. (Considere
#define TWO 1+1
: ¿qué significaTWO*TWO
igual? 3.)Las macros no se escriben como las funciones. El compilador no puede verificar que los parámetros y el tipo de retorno tengan sentido. Solo puede verificar la expresión expandida que usa la macro.
Si el código no se compila, el compilador no tiene forma de saber si el error está en la macro o en el lugar donde se usa la macro. El compilador informará el lugar equivocado la mitad del tiempo, o tendrá que informar ambos, aunque uno de ellos probablemente esté bien. (Considere
#define min(x,y) (((x)<(y))?(x):(y))
: ¿Qué debe hacer el compilador si los tipos dex
yy
no coinciden o no se implementanoperator<
?)Las herramientas automatizadas no pueden trabajar con ellas de formas semánticamente útiles. En particular, no puede tener cosas como IntelliSense para macros que funcionan como funciones pero se expanden a una expresión. (De nuevo, el
min
ejemplo).Los efectos secundarios de una macro no son tan explícitos como lo son con las funciones, causando una posible confusión para el programador. (Considere nuevamente el
min
ejemplo: en una llamada de función, sabe que la expresión parax
se evalúa solo una vez, pero aquí no se puede saber sin mirar la macro).Como dije, todas estas son consecuencias del hecho de que las macros son léxicas. Cuando intentas convertirlos en algo más apropiado, terminas con funciones y constantes.
fuente
Pero sí, las macros se pueden diseñar e implementar mejor que en C / C ++.
El problema con las macros es que son efectivamente un mecanismo de extensión de sintaxis de lenguaje que reescribe su código en otra cosa.
En el caso de C / C ++, no existe una comprobación de cordura fundamental. Si tienes cuidado, las cosas están bien. Si comete un error, o si usa en exceso las macros, puede tener grandes problemas.
Agregue a esto que muchas cosas simples que puede hacer con macros (estilo C / C ++) se pueden hacer de otras maneras en otros idiomas.
En otros idiomas, como varios dialectos de Lisp, las macros están mejor integradas con la sintaxis del lenguaje principal, pero aún puede tener problemas con las declaraciones en una macro "fuga". Esto se aborda mediante macros higiénicas .
Breve contexto histórico
Las macros (abreviatura de macroinstrucciones) aparecieron por primera vez en el contexto del lenguaje ensamblador. Según Wikipedia , las macros estaban disponibles en algunos ensambladores de IBM en la década de 1950.
El LISP original no tenía macros, pero se introdujeron por primera vez en MacLisp a mediados de la década de 1960: https://stackoverflow.com/questions/3065606/when-did-the-idea-of-macros-user-defined-code -transformación-aparecen . http://www.csee.umbc.edu/courses/331/resources/papers/Evolution-of-Lisp.pdf . Antes de eso, "fexprs" proporcionaba una funcionalidad tipo macro.
Las primeras versiones de C no tenían macros ( http://cm.bell-labs.com/cm/cs/who/dmr/chist.html ). Estos se agregaron alrededor de 1972-73 a través de un preprocesador. Antes de eso, C solo soportaba
#include
y#define
.El macroprocesador M4 se originó en circa 1977.
Los lenguajes más recientes aparentemente implementan macros donde el modelo de operación es sintáctico en lugar de textual.
Entonces, cuando alguien habla de la primacía de una definición particular del término "macro", es importante tener en cuenta que el significado ha evolucionado con el tiempo.
fuente
Las macros pueden, como señala Scott , permitirle ocultar la lógica. Por supuesto, también lo hacen funciones, clases, bibliotecas y muchos otros dispositivos comunes.
Pero un poderoso sistema macro puede ir más allá, permitiéndole diseñar y utilizar sintaxis y estructuras que normalmente no se encuentran en el lenguaje. De hecho, esta puede ser una herramienta maravillosa: lenguajes específicos de dominio, generadores de código y más, todo dentro de la comodidad de un entorno de lenguaje único ...
Sin embargo, puede ser abusado. Puede hacer que el código sea más difícil de leer, comprender y depurar, aumentar el tiempo necesario para que los nuevos programadores se familiaricen con una base de código y generar errores y demoras costosas.
Entonces, para los lenguajes destinados a simplificar la programación (como Java o Python), dicho sistema es un anatema.
fuente
Las macros se pueden implementar de manera muy segura en algunas circunstancias: en Lisp, por ejemplo, las macros son solo funciones que devuelven código transformado como una estructura de datos (expresión-s). Por supuesto, Lisp se beneficia significativamente del hecho de que es homoicónico y del hecho de que "el código es información".
Un ejemplo de lo fácil que pueden ser las macros es este ejemplo de Clojure que especifica un valor predeterminado que se utilizará en el caso de una excepción:
Sin embargo, incluso en Lisps, el consejo general es "no use macros a menos que sea necesario".
Si no está utilizando un lenguaje homoicónico, las macros se vuelven mucho más complicadas y las otras opciones tienen algunas dificultades:
Además, todo lo que una macro puede hacer se puede lograr de alguna otra manera en un lenguaje completo (incluso si esto significa escribir muchas repeticiones). Como resultado de todo este engaño, no es sorprendente que muchos lenguajes decidan que las macros realmente no valen la pena.
fuente
Para responder a sus preguntas, piense para qué macros se usan predominantemente (Advertencia: código compilado por el cerebro).
#define X 100
Esto se puede reemplazar fácilmente con:
const int X = 100;
#define max(X,Y) (X>Y?X:Y)
En cualquier lenguaje que admita la sobrecarga de funciones, esto se puede emular de una manera mucho más segura mediante la sobrecarga de funciones del tipo correcto o, en un lenguaje que admita genéricos, mediante una función genérica. La macro felizmente intentará comparar cualquier cosa, incluidos punteros o cadenas, que podrían compilarse, pero es casi seguro que no es lo que quería. Por otro lado, si hizo que las macros sean seguras, no ofrecen beneficios ni conveniencia sobre las funciones sobrecargadas.
#define p printf
Esto se reemplaza fácilmente por una función
p()
que hace lo mismo. Esto está bastante involucrado en C (que requiere que use lava_arg()
familia de funciones), pero en muchos otros lenguajes que admiten números variables de argumentos de función, es mucho más simple.El soporte de estas características dentro de un lenguaje en lugar de en un lenguaje macro especial es más simple, menos propenso a errores y mucho menos confuso para otros que leen el código. De hecho, no puedo pensar en un solo caso de uso para macros que no se pueda duplicar fácilmente de otra manera. El único lugar donde las macros son realmente útiles es cuando están vinculadas a construcciones de compilación condicional como
#if
(etc.).En ese punto, no discutiré con usted, ya que creo que las soluciones no preprocesadoras para la compilación condicional en lenguajes populares son extremadamente engorrosas (como la inyección de código de bytes en Java). Pero lenguajes como D han creado soluciones que no requieren un preprocesador y no son más engorrosos que el uso de condicionales de preprocesador, aunque son mucho menos propensos a errores.
fuente
In fact, I can't think of a single use-case for macros that can't easily be duplicated in another way
: en C al menos, no puede formar identificadores (nombres de variables) usando la concatenación de tokens sin usar macros.enum
para definir las condiciones y un conjunto de cadenas constante para definir los mensajes podría generar problemas si el enum y la matriz se desincronizan. El uso de una macro para definir todos los pares (enum, string) y luego incluir esa macro dos veces con otras definiciones apropiadas en el alcance cada vez permitiría colocar cada valor de enum junto a su cadena.El mayor problema que he visto con las macros es que, cuando se usan mucho, pueden hacer que el código sea muy difícil de leer y mantener, ya que le permiten ocultar la lógica en la macro que puede o no ser fácil de encontrar (y puede o no ser trivial )
fuente
Para empezar, tenga en cuenta que los MACRO en C / C ++ son muy limitados, propensos a errores y no son realmente tan útiles.
Los MACRO implementados en, por ejemplo, LISP, o el lenguaje ensamblador de z / OS pueden ser confiables e increíblemente útiles.
Pero debido al abuso de la funcionalidad limitada en C, tienen una mala reputación. Entonces, ya nadie implementa macros, en su lugar obtienes cosas como Plantillas que hacen algunas de las cosas simples que solían hacer las macros y cosas como las anotaciones de Java que hacen algunas de las cosas más complejas que solían hacer las macros.
fuente