7
votes

Les préprocesseurs sont-ils obsolètes dans les langues modernes?

Je fais un simple compilateur pour une simple langue d'animal de compagnie que je crée et viennent d'un contexte C (bien que je l'écris dans Ruby) Je me demandais si un prétraiteur est nécessaire.

Que pensez-vous? Un préprocesseur "muet" est-il toujours nécessaire dans les langues modernes? Les capacités de compilation conditionnelle C # seraient-elles considérées comme un «préprocesseur»? Est-ce que chaque langue moderne n'inclut pas un préprocesseur a-t-elle des utilitaires nécessaires pour le remplacer correctement? (Par exemple, le préprocesseur C ++ est maintenant principalement obsolète (bien que tenté) à cause de modèles.)


3 commentaires

Je voudrais pas dire que le pré-processeur est obsolète en C ++. Beaucoup de choses qu'elle a été utilisée dans C peut être faite à l'aide de modèles, mais il y a encore beaucoup d'utilisations.


C ++ a toujours besoin du préprocesseur sur #include les fichiers à tout le moins - si j'espère que nous pourrons tous convenir qu'il existe des moyens de gérer plusieurs fichiers que d'utiliser un préprocesseur.


@sepp @james oui c'est ce dont je parle. Toute personne qui fait #define foo (x) ... en C ++ ne connaît pas les modèles C ++ ou travaille sur le code hérité.


9 Réponses :


7
votes

Le préprocesseur est une méthode bon marché pour fournir des installations de métaprogramming incomplètes à une langue de manière laid.

Préférez la véritable métaprogrammation ou les macros de style LISP à la place.


1 commentaires

Bien dit. Toute personne qui conçoit une langue aujourd'hui sans macros ni quelque chose d'équivalent devrait avoir sa licence de conception linguistique enlevée. Voir Converge ( CONVERGEPL.org ) pour savoir comment faire un puissant système de métaprogramming de compilation dans une langue non homoiconique avec Beaucoup de syntaxe.



2
votes

Je pense que les préprocesseurs sont une béquille pour garder une langue avec une mauvaise marche de puissance expressive.

J'ai vu tant d'abus de préprocesseurs que je déteste avec une passion.


0 commentaires

9
votes

Le prétraitement de C peut faire des choses vraiment soignées, mais si vous regardez les choses que vous avez utilisées pour vous réalisez que c'est souvent pour simplement ajouter un autre niveau d'abstraction.

  • Prétraitement pour différentes opérations sur différentes plates-formes? Il s'agit essentiellement d'une couche d'abstraction pour l'indépendance de la plate-forme.
  • Prétraitement pour ajouter facilement du code complexe? Abstraction parce que la langue n'est pas assez générique.
  • Prétraitement pour ajouter des extensions dans votre code? Abstraction parce que votre code / votre langue n'est pas suffisamment flexible.

    Donc, ma réponse est la suivante: Vous n'avez pas besoin de préprocesseur si votre langue est suffisamment de haut niveau *. Je n'appellerais pas le prétraitement du mal ou inutile, je dis simplement que plus le langage est abstrait, moins la raison pour laquelle je peux penser pour avoir besoin de prétraitement.

    * Quel est suffisamment de haut niveau? C'est bien sûr, entièrement subjectif.

    edit: Bien sûr, je ne fais que référence vraiment à macros . Utilisation des préprocesseurs pour interfacer avec d'autres fichiers de code ou pour définir les constantes est mal.


2 commentaires

+1 pour C'est fondamentalement une couche d'abstraction pour l'indépendance de la plate-forme.


Les macros ne sont que du mal si vous ne les laissez pas non révéler. Les développeurs C devraient être forcés de ne utiliser que des macros dans des champs spécifiques et non définis lorsqu'ils sont effectués. par exemple, #define Options, #include en-tête, l'en-tête fait un travail et indéfique les options et laisse la structure brute derrière, ou s'il s'agit d'un en-tête utilitaire, vous # y compris, alors vous #define désinstallez et #incluez à nouveau l'en-tête. Et il devrait nettoyer toutes ses macros. Sinon, la portée mondiale devient assez rapide de FUBARD, voir API Windows pour la voir à une extrême.



0
votes

Un préprocesseur est une phase séparée de la compilation. Bien que le prétraitement puisse être utile dans certains cas, les maux de tête et les bugs qu'il peut causer un problème.

en C, le préprocesseur est utilisé principalement pour:

  1. y compris les données - tandis que les cas d'utilisation les plus courants n'ont pas besoin de telle puissance et "Importer" / "L'utilisation" (comme dans Java / C #) est beaucoup plus propre à utiliser, et peu de personnes ont besoin des cas restants. ;
  2. Définition des constantes - pourquoi pas seulement fournir une déclaration "const"
  3. macros - tandis que les macros de style C sont très puissants (ils peuvent inclure des déclarations telles que des retours), ils font également mal de lisibilité. Les génériques / modèles sont plus propres et, bien que moins puissants de quelques égards, ils sont plus faciles à comprendre.
  4. Compilation conditionnelle - Ceci est peut-être le cas d'utilisation le plus légitime pour les préprocesseurs, mais une fois de plus, il est douloureux pour la lisibilité. Séparer le code spécifique à la plate-forme dans le code source spécifique à la plate-forme et utiliser des déclarations communes si les déclarations finissent par être mieux pour la lisibilité.

    Donc, ma réponse est tandis que le préprocesseur nuit à la lisibilité et / ou n'est pas le meilleur moyen de traiter certains problèmes . Les plus récentes langues ont tendance à considérer la maintenance du code très important et pour les raisons pour lesquelles le prétraiteur semble être obsolète.


4 commentaires

Je pense que vous rendant le problème plus grand que cela. Oui, obtenir le droit de transition de phase est un problème difficile. Mais c'est un résolu problème difficile: la communauté du régime a compris il y a une décennie il y a des décennies. Vous avez juste besoin de voler leur travail.


Dans le schéma, il ne semble pas y avoir de préprocesseur. Cela signifie que s'il existe, il semble avoir été fait correctement.


Pourquoi la compilation conditionnelle serait-elle le cas d'utilisation le plus légitime pour un préprocesseur? Delphi gère la compilation conditionnelle très bien sans préprocesseur ...


@Marjan Venema: précisément. Donc, même que l'utilisation de l'utilisation a de meilleures alternatives, ce qui ne réduit que même la nécessité de préprocesseurs.



0
votes

C'est votre langue afin que vous puissiez construire toutes les capacités que vous souhaitez entrer dans la langue elle-même, sans avoir besoin d'un préprocesseur. Je ne pense pas qu'un prétraiteur soit nécessaire, et il ajoute une couche de complexité et d'obscurité au-dessus d'une langue. La plupart des langues modernes n'ont pas de préprocesseurs et, en C ++, vous ne l'utilisez que lorsque vous n'avez pas d'autre choix.

Au fait, je crois que D traite la compilation conditionnelle sans préprocesseur.


2 commentaires

Pour une raison quelconque, d ressemble à une extension de fiction C ++ qui se trouve avoir un compilateur réel


D a statique si qui est une instruction si est évaluée au moment de la compilation au lieu d'exécution.



0
votes

Cela dépend exactement de quelles autres fonctionnalités que vous proposez. Par exemple, si j'ai un const int n, offrez-vous pour moi de prendre n variables? Avoir n variables membres, prendre un argument pour les construire tous? Créer n fonctions? Effectuer n Opérations qui ne fonctionnent pas nécessairement dans des boucles (par exemple, Pass N arguments)? N Arguments de modèle? Compilation conditionnelle? Constantes qui ne sont pas intégrales?

Le préprocesseur C est tellement absurdement puissant dans les mains appropriées, vous auriez besoin de faire une langue sérieusement puissante pour ne pas en garantir une.


0 commentaires

5
votes

Un préprocesssor est pas nécessaire . Pour de vrai métaprogramming, vous devriez avoir quelque chose comme Metaml ou Modèle Haskell ou Macros Hygiénique à la schéma. Pour des trucs rapides et sales, si vos utilisateurs doivent absolument l'avoir, il y a toujours m4 .

Cependant, une langue moderne devrait prendre en charge l'équivalent des directives #line de C. de telles directives permettent au compilateur de localiser des erreurs dans la source d'origine, même lorsque cette source est intégré dans un générateur d'analyseur ou un générateur LXER ou un programme d'alphabétisation. En d'autres termes,

  • Concevez votre langue afin de ne pas avoir besoin d'un préprocesseur.
  • Ne faites pas votre langue avec un préprocesseur béni.
  • Mais si d'autres ont leurs propres raisons d'utiliser un préprocesseur (la génération d'analyseurs est une populaire), fournissez une prise en charge des messages d'erreur précis.

0 commentaires

0
votes

Je dirais que, même si vous éviteriez le pré-processeur pour la plupart de tout ce que vous faites normalement, il est toujours nécessaire.

Par exemple, en C ++, afin d'écrire une bibliothèque de test unitaire comme attraper , un pré -Processeur est absolument nécessaire. Ils l'utilisent de deux manières différentes: une pour une expansion d'affirmation 1 et une pour les sections de nidification dans des cas de test 2 . .

Mais, le pré-processeur ne doit pas être abusé de calculer des calculs de compilation en C ++, où la méta-programmation de const-expressions et de modèles peut être utilisée.


Désolé, je n'ai pas assez de réputation pour poster plus de deux liens, alors je le mettais ici ici:

  1. github.com/philsquared/catch/blob/master/docs/assertions.md
  2. github.com/philsquared/catch/blob/master/docs/test-cases-et-sections.md

0 commentaires

0
votes

Les autres ont souligné, une grande partie de la fonctionnalité fournie par le prétraitement C existe pour compenser les limitations du langage C. Par exemple, #include et les gardes d'inclusion existent en raison de l'absence d'une instruction d'importation et existent largement en raison du manque de fonctions en ligne et de déclarations constantes.

Cependant, la seule caractéristique du préprocesseur C qui serait toujours bénéfique dans des langues plus modernes est la directive #line , car elle supporte l'utilisation de préprocesseurs / compilateurs riches en semi-sémantique. Un exemple, envisagez YACC , qui est une langue spécifique à un domaine (DSL) pour écrire un analyseur comme collection de règles de grammaire BNF. Une caractéristique centrale de YACC est que les morceaux de code C appelé actions peuvent être intégrés dans les règles BNF. Lorsqu'une règle BNF est utilisée pour analyser un élément d'entrée, une action intégrée dans cette règle sera exécutée. Le compilateur YACC génère un fichier C qui implémente l'analyseur basé sur BNF spécifié dans le fichier d'entrée et toutes les actions figurant dans le fichier d'entrée YACC sont copiées dans le fichier C généré, mais chaque action est entourée. par #line directives. Cette utilisation de directives #line fournit deux avantages importants.

Premièrement, s'il existe une erreur de syntaxe dans une action, le message d'erreur généré par le Compiler C peut spécifier que l'erreur s'est produite dans, disons, , ligne 42 < / Code> plutôt que dans .c, ligne 3967 .

Deuxièmement, les informations de localisation fournies par #line sont copiées dans des fichiers de code d'objet généré créés par le compilateur C. Donc, si vous utilisez un débogueur pour rechercher un crash de programme, si le bogue qui a provoqué l'origine du crash est originaire d'une action intégrée dans un fichier d'entrée de YACC, le débogueur signalera l'emplacement de cette gamme de buggy de code source comme étant dans , Ligne 42 plutôt que dans .c, ligne 3967 . .

Les concepteurs de C # et Perl ont judicieusement fourni une directive #line . Malheureusement, les concepteurs de nombreuses autres langues (Java étant une source à l'esprit) négligé de fournir une directive #line . Pour cette raison, des générateurs d'analyseurs semblables à ceux de YACC pour de nombreuses langues ne sont pas en mesure de communiquer l'emplacement source des actions intégrées aux compilateurs (et donc de débuggers).


0 commentaires