Pour répondre à vos questions, pensez aux macros utilisées principalement (Avertissement: code compilé par le cerveau).
- Macros utilisées pour définir les constantes symboliques
#define X 100
Cela peut facilement être remplacé par: const int X = 100;
- Macros utilisées pour définir (essentiellement) des fonctions agnostiques de type inline
#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.
- Macros utilisées pour spécifier des raccourcis vers des éléments souvent utilisés.
#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 la va_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.