Je sais qu'ils sont mis en œuvre de manière extrêmement dangereuse en C / C ++. Ne peuvent-ils pas être mis en œuvre de manière plus sûre? Les inconvénients des macros sont-ils vraiment assez importants pour l'emporter sur la puissance massive qu'ils fournissent?
programming-languages
macros
Casebash
la source
la source
WithEvents
modificateur de Visual Basic ? Vous auriez besoin de quelque chose comme des macros sémantiques.Réponses:
Je pense que la raison principale est que les macros sont lexicales . Cela a plusieurs conséquences:
Le compilateur n'a aucun moyen de vérifier qu'une macro est sémantiquement fermée, c'est-à-dire qu'elle représente une «unité de sens» comme le fait une fonction. (Considérez
#define TWO 1+1
- qu'est-ce quiTWO*TWO
équivaut? 3.)Les macros ne sont pas saisies comme les fonctions. Le compilateur ne peut pas vérifier que les paramètres et le type de retour ont un sens. Il ne peut que vérifier l'expression développée qui utilise la macro.
Si le code ne compile pas, le compilateur n'a aucun moyen de savoir si l'erreur se trouve dans la macro elle-même ou à l'endroit où la macro est utilisée. Le compilateur signalera soit le mauvais endroit la moitié du temps, soit il devra signaler les deux même si l'un d'eux est probablement correct. (Réfléchissez
#define min(x,y) (((x)<(y))?(x):(y))
: que doit faire le compilateur si les types dex
ety
ne correspondent pas ou ne sont pas implémentésoperator<
?)Les outils automatisés ne peuvent pas fonctionner avec eux de manière sémantiquement utile. En particulier, vous ne pouvez pas avoir des choses comme IntelliSense pour les macros qui fonctionnent comme des fonctions mais se développent en une expression. (Encore une fois, l'
min
exemple.)Les effets secondaires d'une macro ne sont pas aussi explicites qu'ils le sont avec les fonctions, provoquant une confusion potentielle pour le programmeur. (Reprenons l'
min
exemple: dans un appel de fonction, vous savez que l'expression dex
n'est évaluée qu'une seule fois, mais ici vous ne pouvez pas le savoir sans regarder la macro.)Comme je l'ai dit, ce sont toutes des conséquences du fait que les macros sont lexicales. Lorsque vous essayez de les transformer en quelque chose de plus approprié, vous vous retrouvez avec des fonctions et des constantes.
la source
Mais oui, les macros peuvent être conçues et implémentées mieux qu'en C / C ++.
Le problème avec les macros est qu'elles sont en fait un mécanisme d' extension de syntaxe de langage qui réécrit votre code en quelque chose d'autre.
Dans le cas C / C ++, il n'y a pas de vérification d'intégrité fondamentale. Si vous faites attention, les choses vont bien. Si vous faites une erreur ou si vous abusez des macros, vous pouvez rencontrer de gros problèmes.
Ajoutez à cela que beaucoup de choses simples que vous pouvez faire avec des macros (style C / C ++) peuvent être faites d'autres manières dans d'autres langues.
Dans d'autres langues telles que divers dialectes Lisp, les macros sont mieux intégrées à la syntaxe du langage principal, mais vous pouvez toujours rencontrer des problèmes avec les déclarations dans une macro "qui fuit". Ceci est résolu par des macros hygiéniques .
Bref contexte historique
Les macros (abréviation de macro-instructions) sont apparues pour la première fois dans le contexte du langage d'assemblage. Selon Wikipedia , les macros étaient disponibles dans certains assembleurs IBM dans les années 1950.
Le LISP d'origine n'avait pas de macros, mais elles ont été introduites pour la première fois dans MacLisp au milieu des années 1960: https://stackoverflow.com/questions/3065606/when-did-the-idea-of-macros-user-defined-code -transformation-apparaître . http://www.csee.umbc.edu/courses/331/resources/papers/Evolution-of-Lisp.pdf . Avant cela, "fexprs" offrait une fonctionnalité de type macro.
Les premières versions de C n'avaient pas de macros ( http://cm.bell-labs.com/cm/cs/who/dmr/chist.html ). Ceux-ci ont été ajoutés vers 1972-73 via un préprocesseur. Avant cela, C ne supportait que
#include
et#define
.Le macro-préprocesseur M4 est né vers 1977.
Des langages plus récents implémentent apparemment des macros où le modèle de fonctionnement est syntaxique plutôt que textuel.
Ainsi, lorsque quelqu'un parle de la primauté d'une définition particulière du terme «macro», il est important de noter que le sens a évolué au fil du temps.
la source
Comme le note Scott , les macros peuvent vous permettre de masquer la logique. Bien sûr, il en va de même des fonctions, des classes, des bibliothèques et de nombreux autres périphériques courants.
Mais un puissant système de macro peut aller plus loin, vous permettant de concevoir et d'utiliser une syntaxe et des structures que l'on ne trouve pas normalement dans le langage. Cela peut être un merveilleux outil en effet: des langages spécifiques à un domaine, des générateurs de code et bien plus, le tout dans le confort d'un environnement linguistique unique ...
Cependant, il peut être abusé. Cela peut rendre le code plus difficile à lire, à comprendre et à déboguer, augmenter le temps nécessaire aux nouveaux programmeurs pour se familiariser avec une base de code et entraîner des erreurs et des retards coûteux.
Ainsi pour les langages destinés à simplifier la programmation (comme Java ou Python), un tel système est un anathème.
la source
Les macros peuvent être implémentées en toute sécurité dans certaines circonstances - en Lisp par exemple, les macros ne sont que des fonctions qui renvoient du code transformé sous forme de structure de données (expression s). Bien sûr, Lisp profite de manière significative du fait qu'il est homoiconique et du fait que "le code est une donnée".
Un exemple de la simplicité des macros est cet exemple Clojure qui spécifie une valeur par défaut à utiliser dans le cas d'une exception:
Même en Lisps, le conseil général est de "ne pas utiliser de macros sauf si vous devez".
Si vous n'utilisez pas un langage homoiconique, les macros deviennent beaucoup plus délicates et les diverses autres options présentent toutes des pièges:
De plus, tout ce qu'une macro peut faire peut finalement être réalisé d'une autre manière dans un langage complet (même si cela signifie écrire beaucoup de passe-partout). À la suite de toute cette ruse, il n'est pas surprenant que de nombreuses langues décident que les macros ne valent pas vraiment tous les efforts à mettre en œuvre.
la source
Pour répondre à vos questions, pensez aux macros utilisées principalement (Avertissement: code compilé par le cerveau).
#define X 100
Cela peut facilement être remplacé par:
const int X = 100;
#define max(X,Y) (X>Y?X:Y)
Dans n'importe quel langage qui prend en charge la surcharge de fonctions, cela peut être émulé d'une manière beaucoup plus sûre pour les types en ayant des fonctions surchargées du type correct, ou, dans un langage qui prend en charge les génériques, par une fonction générique. La macro tentera avec plaisir de comparer tout ce qui inclut les pointeurs ou les chaînes, qui pourraient être compilés, mais ce n'est certainement pas ce que vous vouliez. D'un autre côté, si vous avez rendu les macros sécuritaires, elles n'offrent aucun avantage ni aucune commodité par rapport aux fonctions surchargées.
#define p printf
Ceci est facilement remplacé par une fonction
p()
qui fait la même chose. Ceci est assez impliqué dans C (vous obligeant à utiliser lava_arg()
famille de fonctions) mais dans de nombreux autres langages qui prennent en charge un nombre variable d'arguments de fonction, c'est beaucoup plus simple.La prise en charge de ces fonctionnalités dans un langage plutôt que dans un langage macro spécial est plus simple, moins sujette aux erreurs et beaucoup moins déroutante pour les autres qui lisent le code. En fait, je ne peux pas penser à un cas d'utilisation unique pour les macros qui ne peuvent pas être facilement dupliquées d'une autre manière. Le seul endroit où les macros sont vraiment utiles est lorsqu'elles sont liées à des constructions de compilation conditionnelle comme
#if
(etc.).Sur ce point, je ne discuterai pas avec vous, car je pense que les solutions non préprocesseurs pour la compilation conditionnelle dans les langages populaires sont extrêmement lourdes (comme l'injection de bytecode en Java). Mais des langages comme D ont proposé des solutions qui ne nécessitent pas de préprocesseur et ne sont pas plus encombrantes que l'utilisation de conditions de préprocesseur, tout en étant beaucoup moins sujettes aux erreurs.
la source
In fact, I can't think of a single use-case for macros that can't easily be duplicated in another way
: en C au moins, vous ne pouvez pas former d'identifiants (noms de variables) en utilisant la concaténation de jetons sans utiliser de macros.enum
pour définir les conditions et un tableau de chaînes constant pour définir les messages pourrait entraîner des problèmes si le enum et le tableau se désynchronisent. L'utilisation d'une macro pour définir toutes les paires (énumération, chaîne), puis l'inclusion de cette macro deux fois avec d'autres définitions appropriées dans la portée à chaque fois permettrait de placer chaque valeur énumérée à côté de sa chaîne.Le plus gros problème que j'ai vu avec les macros est que, lorsqu'elles sont fortement utilisées, elles peuvent rendre le code très difficile à lire et à maintenir car elles vous permettent de masquer la logique dans la macro qui peut ou peut ne pas être facile à trouver (et peut être triviale ou non). ).
la source
Commençons par noter que les macros en C / C ++ sont très limitées, sujettes aux erreurs et pas vraiment utiles.
Les MACRO telles qu'implémentées dans LISP ou le langage assembleur z / OS peuvent être fiables et incroyablement utiles.
Mais à cause de l'abus des fonctionnalités limitées de C, ils ont acquis une mauvaise réputation. Donc, personne n'implémente plus de macros, au lieu de cela, vous obtenez des choses comme les modèles qui font certaines des choses simples que les macros faisaient et des choses comme les annotations de Java qui font certaines des macro de choses les plus complexes utilisées auparavant.
la source