Si quelque chose peut être généré, alors ce sont des données, pas du code.
Dans ces conditions, l’idée même de la génération de code source n’est-elle pas un malentendu? Autrement dit, s'il existe un générateur de code pour quelque chose, pourquoi ne pas en faire une fonction appropriée pouvant recevoir les paramètres requis et effectuer l'action correcte que le code "aurait généré" aurait fait?
Si cela est fait pour des raisons de performances, cela ressemble à une lacune du compilateur.
Si cela est fait pour relier deux langues, cela ressemble à un manque de bibliothèque d’interface.
Est-ce que j'ai râté quelque chose?
Je sais que ce code est aussi une donnée. Ce que je ne comprends pas, c'est pourquoi générer du code source ? Pourquoi ne pas en faire une fonction capable d’accepter des paramètres et d’agir sur ceux-ci?
flex
ou un analyseur généré parbison
sera presque certainement plus prévisible, plus correct et souvent plus rapide à exécuter que les équivalents écrits à la main en C; et construit à partir de beaucoup moins de code (donc aussi moins de travail à maintenir).Réponses:
Techniquement, si nous générons du code, ce n'est pas la source même s'il s'agit d'un texte lisible par l'homme. Le code source est un code original, généré par une intelligence humaine ou autre, non traduit mécaniquement et non immédiatement reproductible à partir de (véritable) source (directement ou indirectement).
Je dirais que tout est des données de toute façon. Même le code source. Surtout le code source! Le code source n'est que des données dans un langage conçu pour accomplir des tâches de programmation. Ces données doivent être traduites, interprétées, compilées, générées selon les besoins sous d'autres formes - de données - dont certaines sont exécutables.
Le processeur exécute les instructions en dehors de la mémoire. La même mémoire utilisée pour les données. Avant que le processeur n'exécute des instructions, le programme est chargé en mémoire sous forme de données .
Donc, tout est données , même le code .
Il est parfaitement correct d’avoir plusieurs étapes de compilation, dont l’une peut être la génération de code intermédiaire sous forme de texte.
C'est une façon, mais il y en a d'autres.
Toutes les formes de texte ne sont pas destinées à la consommation humaine. En particulier, le code généré (sous forme de texte) est généralement destiné à la consommation du compilateur et non à la consommation humaine.
Le code source est considéré comme l’original: le maître - ce que nous éditons et développons; ce que nous archivons en utilisant le contrôle de code source. Le code généré, même lorsqu'un texte lisible par l'homme, est généralement régénéré à partir du code source d' origine . En règle générale, le code généré ne doit pas nécessairement être sous contrôle de code source car il est régénéré pendant la construction.
la source
Raisonnement pratique
À partir de cette édition, je suppose que vous posez une question plutôt pratique que informatique théorique.
La raison classique pour générer du code source dans des langages statiques tels que Java était que ces langages ne venaient tout simplement pas avec des outils faciles à utiliser en langage pour faire des choses très dynamiques. Par exemple, à l'époque formatrice de Java, il n'était tout simplement pas possible de créer facilement une classe avec un nom dynamique (correspondant à un nom de table depuis une base de données) et des méthodes dynamiques (attributs correspondants de cette table) avec des types de données dynamiques les types desdits attributs). D'autant que Java attache beaucoup d'importance, voire garantit, à pouvoir détecter les erreurs de type au moment de la compilation.
Ainsi, dans un tel contexte, un programmeur ne peut créer que du code Java et écrire beaucoup de lignes de code manuellement. Souvent, le programmeur s'aperçoit que chaque fois qu'une table est modifiée, il doit revenir en arrière et modifier le code afin qu'il corresponde; et s'il oublie ça, de mauvaises choses arrivent. Par conséquent, le programmeur en arrivera au point où il rédigera des outils qui le font pour lui. Et par conséquent, la route commence pour une génération de code de plus en plus intelligente.
(Oui, vous pouvez générer le bytecode à la volée, mais programmer un tel programme en Java ne serait pas quelque chose qu'un programmeur aléatoire ferait entre deux lignes d'écriture de code de domaine.)
Comparez cela à des langages très dynamiques, par exemple Ruby, que je considérerais comme l'antithèse de Java à presque tous les égards (notez que je le dis sans valoriser l'une ou l'autre approche; ils sont simplement différents). Ici, il est 100% normal et standard de générer dynamiquement des classes, des méthodes, etc. au moment de l’exécution, et surtout, le programmeur peut le faire trivialement dans le code, sans passer au niveau "méta". Oui, Ruby on Rails est associé à la génération de code, mais nous avons constaté dans notre travail que nous l'utilisions comme une sorte de "mode tutoriel" avancé pour les nouveaux programmeurs, mais au bout d'un moment cela devient superflu (car il y a si peu de code). pour écrire dans cet écosystème que, lorsque vous savez ce que vous faites, l'écrire manuellement devient plus rapide que le nettoyage du code généré).
Ce ne sont que deux exemples pratiques du "monde réel". Ensuite, vous avez des langues comme LISP où le code est littéralement des données. Par contre, dans les langages compilés (sans moteur d’exécution tel que Java ou Ruby), il n’existe (ou n’ai pas suivi les fonctionnalités modernes du C ++ ...) tout simplement aucun concept de définition de noms de classe ou de méthode au moment de l’exécution, La génération de code est donc l’outil de choix pour la plupart des choses (d’autres exemples plus spécifiques à C / C ++ seraient des choses comme flex, yacc, etc.).
la source
Parce que la programmation avec des cartes perforées (ou des codes alt dans le bloc-notes ) est une douleur.
Vrai. Je me fiche de la performance à moins d'y être forcé.
Hmm, je ne sais pas de quoi vous parlez.
Voici ce qui se passe: le code source généré et conservé est toujours et toujours pénible. Il existe pour une seule raison. Quelqu'un veut travailler dans une langue alors que quelqu'un d'autre insiste pour travailler dans une autre et aucun d'entre eux ne peut s'embarrasser pour savoir comment interopérer entre eux afin que l'un d'eux sache comment transformer sa langue préférée en langue imposée afin de pouvoir faire quoi Ils veulent.
Ce qui est bien jusqu'à ce que je dois le maintenir. À quel point vous pouvez tous aller mourir.
Est-ce un modèle anti? Soupir, non. De nombreuses langues n'existeraient même pas si nous n'étions pas disposés à dire au revoir aux faiblesses des langues précédentes et générer le code des anciennes langues correspond au nombre de nouvelles langues qui commencent.
C'est une base de code qui reste dans un patchwork de monstres Frankenstein à moitié converti que je ne supporte pas. Le code généré est un code intouchable. Je déteste regarder du code intouchable. Pourtant, les gens continuent à l'enregistrer. POURQUOI? Vous pourriez aussi bien vérifier l'exécutable.
Eh bien maintenant je peste. Mon point est que nous "générons tous du code". C'est lorsque vous traitez le code généré comme un code source que vous me rendez fou. Parce qu'il semble que le code source ne le rend pas le code source.
la source
/etc/
fichiers sous Unix, etc.Les cas d’utilisation les plus fréquents des générateurs de code avec lesquels je devais travailler étaient des générateurs qui
a pris une méta-description de haut niveau pour un type de modèle de données ou un schéma de base de données en entrée (peut-être un schéma relationnel ou un type de schéma XML)
et produit le code CRUD de la plaque de la chaudière pour les classes d’accès aux données en sortie, et peut-être des éléments supplémentaires comme des instructions SQL ou une documentation correspondante.
L'avantage ici est que d'une ligne d'une spécification d'entrée courte, vous obtenez 5 à 10 lignes de code débogable, sûr pour le type et sans bug (en supposant que la sortie du générateur de code soit mature) que vous auriez autrement dû implémenter et gérer manuellement. Vous pouvez imaginer à quel point cela réduit les efforts de maintenance et d'évolution.
Permettez-moi également de répondre à votre question initiale
Non, pas de génération de code source en soi, mais il y a en effet des pièges. Comme indiqué dans The Pragmatic Programmer , il convient d'éviter l'utilisation d'un générateur de code lorsqu'il génère un code difficile à comprendre . Sinon, les efforts accrus d'utilisation ou de débogage de ce code peuvent facilement compenser les efforts économisés en n'écrivant pas le code manuellement.
J'aimerais également ajouter qu'il est généralement utile de séparer physiquement les parties de code générées du code écrit manuellement de manière à ce que la régénération ne remplace pas les modifications manuelles. Cependant, j’ai aussi traité plus d’une fois de la situation dans laquelle la tâche consistait à migrer du code écrit dans l’ancien langage X vers un autre, un langage plus moderne, avec l’intention de le maintenir ensuite dans le langage Y. C’est un usage valable cas pour la génération de code ponctuel.
la source
J'ai rencontré deux cas d'utilisation pour le code généré (au moment de la construction et qui n'a jamais été archivé):
la source
Sussmann avait beaucoup à dire sur ce sujet dans son ouvrage classique "Structure et interprétation des programmes informatiques", principalement sur la dualité code-données.
Pour moi, la principale utilisation de la génération de code ad hoc est d'utiliser un compilateur disponible pour convertir un petit langage spécifique à un domaine en quelque chose que je peux lier à mes programmes. Pensez BNF, pensez ASN1 (en fait, non, c'est moche), pensez aux tableurs du dictionnaire de données.
Les langages spécifiques à un domaine trivial peuvent représenter un gain de temps considérable, et la sortie de quelque chose qui peut être compilé par des outils de langage standard est la voie à suivre lors de la création de telles choses, que préféreriez-vous éditer, un analyseur syntaxique piraté à la main non trivial dans la langue maternelle de votre choix? écriture, ou la BNF pour une auto généré?
En envoyant du texte qui est ensuite envoyé à un compilateur système, je reçois toute cette optimisation des compilateurs et la configuration spécifique du système sans avoir à y penser.
J'utilise efficacement le langage de saisie du compilateur comme une simple représentation intermédiaire. Quel est le problème? Les fichiers texte ne sont pas intrinsèquement du code source, ils peuvent être un IR pour un compilateur , et s’ils ressemblent à C, C ++, Java ou autre chose, peu importe.
Maintenant, si vous avez du mal à penser que vous pourriez éditer le résultat de l’analyseur de langage jouet, ce qui décevra manifestement la prochaine fois que quelqu'un éditera les fichiers de langue d'entrée et les reconstruira, la solution consiste à ne pas valider l'IR généré automatiquement dans le référentiel. (et évitez d’avoir de telles personnes dans votre groupe de développeurs, elles sont généralement plus heureuses dans le marketing).
Ce n'est pas vraiment un manque d'expressivité dans nos langues, mais une expression du fait que vous pouvez parfois obtenir (ou masquer) des parties de la spécification sous une forme qui peut être automatiquement convertie en code, et qui en engendrera généralement beaucoup moins. bugs et être beaucoup plus facile à entretenir. Si je peux donner à nos équipes de test et de configuration une feuille de calcul, ils peuvent la modifier et un outil qu'ils utilisent ensuite, qui extrait ces données et crée un fichier hexadécimal complet pour la mémoire flash de mon calculateur, ce qui représente un gain de temps considérable par rapport à la traduction manuelle la dernière configuration en un ensemble de constantes dans la langue du jour (complète avec des fautes de frappe).
Même chose avec la construction de modèles dans Simulink, puis la génération de C avec RTW, puis la compilation pour cibler avec l’outil qui convient, l’intermédiaire C est illisible, et alors? Le matériel de haut niveau Matlab RTW doit uniquement connaître un sous-ensemble de C, et le compilateur C prend en charge les détails de la plate-forme. Le seul moment où un humain doit se frayer un chemin à travers le C généré est lorsque les scripts RTW ont un bogue, et ce genre de choses est beaucoup plus facile à déboguer avec un IR lisible par un humain nominalement qu'avec un arbre d'analyse binaire.
Vous pouvez bien sûr écrire ce genre de choses pour sortir du bytecode ou même du code exécutable, mais pourquoi voudriez-vous le faire? Nous avons des outils pour convertir un IR à ces choses.
la source
Réponse pragmatique: la génération de code est-elle nécessaire et utile? Fournit-il quelque chose de vraiment très utile et nécessaire pour la base de code propriétaire, ou semble-t-il simplement créer une autre façon de faire les choses d'une manière qui contribue davantage à la surcharge intellectuelle pour des résultats sous-optimaux?
Si vous devez poser cette question et qu'il n'y a pas de réponse claire, la génération de code est probablement superflue et ne fait que contribuer à l'exotisme et à une charge intellectuelle considérable pour votre base de code.
Pendant ce temps , si vous prenez quelque chose comme OpenShadingLanguage: https://github.com/imageworks/OpenShadingLanguage
... alors ces questions ne doivent pas être soulevées car elles sont immédiatement répondues par les résultats impressionnants.
Dans un tel cas, vous n'avez pas besoin de mettre en doute l'existence du générateur de code. Si vous travaillez dans ce type de domaine VFX, votre réponse immédiate est généralement plus orientée: "tais-toi et prends mon argent!" ou "wow, nous devons aussi faire quelque chose comme ça."
la source
Non, la génération de code intermédiaire n'est pas un anti-motif. La réponse à l’autre partie de votre question, "Pourquoi le faire?", Est une question très large (et séparée), bien que je donnerai quand même quelques raisons.
Ramifications historiques de ne jamais avoir de code intermédiaire lisible par l'homme
Prenons C et C ++ comme exemples, car ils font partie des langages les plus célèbres.
Vous devez prendre note que la procédure logique de compilation du code C ne génère pas de code machine, mais un code assembleur lisible par l’homme. De même, les anciens compilateurs C ++ compilaient physiquement le code C ++ en code C. Dans cette chaîne d'événements, vous pouvez compiler du code 1 lisible par l'homme au code 2 lisible par l'homme au code 3 lisible par l'homme au code machine. "Pourquoi?" Pourquoi pas?
Si du code intermédiaire lisible par l'homme n'a jamais été généré, il est possible que nous n'ayons même pas du tout C ou C ++. C'est certainement une possibilité. les gens prennent le chemin de la moindre résistance à leurs objectifs, et si une autre langue gagnait du terrain en raison de la stagnation du développement du C, celui-ci aurait pu mourir alors qu'il était encore jeune. Bien sûr, vous pourriez dire "Mais alors, nous utiliserions peut-être un autre langage et ce serait peut-être mieux." Peut-être, ou peut-être que ce serait pire. Ou peut-être aurions-nous tous encore écrit en assemblée.
Pourquoi utiliser du code intermédiaire lisible par l'homme?
Exemple
J'ai déjà travaillé sur des projets où le code devait être généré à partir de données ou d'informations contenues dans un autre document. Par exemple, un projet avait tous ses messages réseau et ses données constantes définis dans un tableur et un outil qui parcourrait le tableur et générerait beaucoup de code C ++ et Java qui nous permettait de travailler avec ces messages.
Je ne dis pas que c'était la meilleure façon de mettre en place ce projet (je ne faisais pas partie de son démarrage), mais c'était ce que nous avions, et c'étaient des centaines (peut-être même des milliers, pas sûr) de structures, d'objets et de constantes qui étaient générés; à ce stade, il est probablement trop tard pour essayer de le refaire dans quelque chose comme Rhapsody. Mais même si cela avait été refait dans quelque chose comme Rhapsody, nous aurions quand même du code généré à partir de Rhapsody .
De plus, avoir toutes ces données dans un tableur était une bonne chose: cela nous permettait de représenter les données d'une manière que nous ne pourrions pas avoir si elles n'étaient que des fichiers de code source.
Exemple 2
Quand j'ai travaillé sur la construction du compilateur, j'ai utilisé l'outil Antlr pour faire mon lexing et mon analyse. J'ai spécifié une grammaire linguistique, puis j'ai utilisé l'outil pour cracher une tonne de code en C ++ ou en Java, puis ce code généré à côté de mon propre code et inclus dans la construction.
Sinon, comment cela aurait-il dû être fait? Peut-être pourriez-vous trouver un autre moyen; il y a probablement d'autres moyens. Mais pour ce travail, les autres méthodes n'auraient pas été meilleures que le code généré lex / parse que j'avais.
la source
Ce qui vous manque, c'est la réutilisation .
Nous avons un outil extraordinaire pour transformer le texte du code source en binaire, appelé compilateur. Ses entrées sont bien définies (généralement!) Et il a fallu beaucoup de travail pour affiner son optimisation. Si vous voulez réellement utiliser le compilateur pour effectuer certaines opérations, vous voulez utiliser un compilateur existant et non écrire le vôtre.
Beaucoup de gens inventent de nouveaux langages de programmation et écrivent leurs propres compilateurs. Quasiment sans exception, ils le font tous parce qu'ils aiment le défi, non pas parce qu'ils ont besoin des fonctionnalités fournies par ce langage. Tout ce qu'ils font pourrait être fait dans une autre langue; ils créent simplement une nouvelle langue parce qu'ils aiment ces fonctionnalités. Cependant, ce qui ne les obtiendra pas, c'est un compilateur optimiseur bien réglé, rapide, efficace. Cela leur donnera quelque chose qui peut transformer du texte en binaire, bien sûr, mais ce ne sera pas aussi bon que tous les compilateurs existants .
Le texte n'est pas simplement quelque chose que les humains lisent et écrivent. Les ordinateurs sont parfaitement à la maison avec le texte aussi. En fait, les formats tels que XML (et autres formats associés) ont du succès car ils utilisent du texte brut. Les formats de fichiers binaires sont souvent obscurs et mal documentés, et un lecteur ne peut pas facilement savoir comment ils fonctionnent. XML est relativement auto-documenté, ce qui facilite la rédaction de code utilisant des fichiers au format XML. Et tous les langages de programmation sont configurés pour lire et écrire des fichiers texte.
Supposons donc que vous souhaitiez ajouter de nouvelles installations pour vous simplifier la vie. Peut-être que c'est un outil de présentation graphique. Peut-être que Qt fournit les interfaces de signaux et de slots . C'est peut-être ainsi que Code Composer Studio de TI vous permet de configurer le périphérique avec lequel vous travaillez et d'extraire les bonnes bibliothèques dans la construction. Peut-être prend-il un dictionnaire de données et génère-t-il des définitions de type et des variables globales auto-générées (oui, c’est toujours un problème dans les logiciels embarqués). Quoi qu'il en soit, le moyen le plus efficace d'exploiter votre compilateur existant consiste à créer un outil prenant en compte votre configuration de ce qu'il est et produisant automatiquement du code dans la langue de votre choix.
Il est facile à développer et à tester, car vous savez ce qui se passe et vous pouvez lire le code source qu'il a généré. Vous n'avez pas besoin de passer des années sur la construction d'un compilateur pour rivaliser avec GCC. Vous n'avez pas besoin d'apprendre une nouvelle langue complète ni de demander à d'autres personnes de le faire. Tout ce que vous avez à faire est d’automatiser ce petit domaine et tout le reste reste le même. Travail accompli.
la source
Une réponse un peu plus pragmatique, centrée sur le pourquoi et non sur le code source. Notez que la génération de code source fait partie du processus de construction dans tous les cas. Par conséquent, les fichiers générés ne doivent pas se retrouver dans le contrôle de source.
Interoprabilité / simplicité
Prenons l'exemple des tampons de protocole de Google: vous écrivez une seule description de protocole de haut niveau qui peut ensuite être utilisée pour générer l'implémentation dans plusieurs langues - souvent différentes parties du système sont écrites dans différentes langues.
Mise en œuvre / raisons techniques
Prenez TypeScript - les navigateurs ne peuvent pas l’interpréter. Le processus de génération utilise donc un transpiler (traducteur de code) pour générer du JavaScript. En fait, de nombreux langages compilés nouveaux ou ésotériques commencent par transpiler en C avant d'obtenir un compilateur approprié.
Facilité d'utilisation
Pour les projets intégrés (think IoT) écrits en C et utilisant un seul binaire (RTOS ou aucun système d'exploitation), il est assez facile de générer un tableau C avec les données à compiler comme si c'était du code source normal, par opposition à les relier directement comme ressources.
Modifier
Développer sur protobuf: la génération de code permet aux objets générés d'être des classes de première classe dans n'importe quel langage. Dans un langage compilé, un analyseur générique renverrait nécessairement une structure clé-valeur - ce qui signifie que vous manquiez beaucoup de code standard, que vous manquiez certaines vérifications au moment de la compilation (sur les clés et les types de valeurs en particulier), pas de code complet. Imaginez tous ceux
void*
en C ou ceux qui sont énormesstd::variant
en C ++ (si vous avez C ++ 17), certaines langues peuvent ne pas avoir une telle fonctionnalité du tout.la source
C'est un moyen de contourner un langage de programmation insuffisamment expressif. Il n'est pas nécessaire de générer du code dans un langage contenant une méta-programmation intégrée adéquate.
la source
La génération de code source n'est pas toujours un anti-modèle. Par exemple, j'écris actuellement un framework qui, par spécification donnée, génère du code dans deux langages différents (Javascript et Java). Le framework utilise le Javascript généré pour enregistrer les actions du navigateur de l'utilisateur et utilise le code Java de Selenium pour exécuter l'action lorsque le framework est en mode de relecture. Si je n'utilisais pas la génération de code, je devrais m'assurer manuellement que les deux sont toujours synchronisés, ce qui est fastidieux et constitue en quelque sorte une duplication logique.
Si toutefois on utilise la génération de code source pour remplacer des fonctionnalités telles que les génériques, alors c'est anti-pattern.
la source
Peut-être un bon exemple où le code intermédiaire s'est avéré être la raison du succès? Je peux vous offrir du HTML.
Je pense qu'il était important que le HTML soit simple et statique: il facilitait la création de navigateurs, permettait de démarrer les navigateurs mobiles plus tôt, etc. . Il s'avère que les utilisateurs sont réellement menacés par les applets Java et la visite de tels sites Web était aussi sûre que d'essayer des jeux téléchargés via DC ++. Le langage HTML simple, en revanche, est suffisamment inoffensif pour nous permettre de consulter tout site ayant une confiance raisonnable en la sécurité de notre appareil.
Cependant, HTML ne serait nulle part où il est maintenant s'il n'était pas généré par ordinateur. Ma réponse n'apparaît même pas sur cette page tant que quelqu'un n'a pas réécrit manuellement la base de données dans un fichier HTML. Heureusement, vous pouvez créer du HTML utilisable dans presque tous les langages de programmation :)
Pouvez-vous imaginer un meilleur moyen d’afficher la question, ainsi que toutes les réponses et commentaires à l’utilisateur, en utilisant HTML comme code intermédiaire généré?
la source
Parce que c'est plus rapide et plus facile (et moins sujet aux erreurs) que d'écrire le code manuellement, en particulier pour les tâches fastidieuses et répétitives. Vous pouvez également utiliser l'outil de haut niveau pour vérifier et valider votre conception avant d'écrire une seule ligne de code.
Cas d'utilisation courants:
En ce qui concerne votre "pourquoi ne pas simplement en faire une fonction et lui passer directement les paramètres", notez qu’aucun de ces éléments n’est un environnement d’exécution en soi. Il n'y a aucun moyen de lier votre code avec eux.
la source
Parfois, votre langage de programmation ne dispose tout simplement pas des installations souhaitées, ce qui rend impossible l’écriture de fonctions ou de macros pour faire ce que vous voulez. Ou peut-être que vous pourriez faire ce que vous voulez, mais le code pour l'écrire serait moche. Un simple script Python (ou similaire) peut ensuite générer le code requis dans le cadre de votre processus de construction, que vous intégrez ensuite
#include
dans le fichier source réel.Comment je sais ça? Parce que c’est une solution que j’ai choisie plusieurs fois lorsque je travaille avec différents systèmes, notamment SourcePawn. Un simple script Python qui analyse une simple ligne de code source et génère deux ou trois lignes de code généré est nettement préférable à la création manuelle du code généré, lorsque vous vous retrouvez avec deux douzaines de lignes de ce type (créant tous mes cvars).
Code source démonstratif / exemple disponible si les gens le souhaitent.
la source
La forme du texte est requise pour une consommation facile par les humains. Les ordinateurs traitent également le code sous forme de texte assez facilement. Par conséquent, le code généré doit être généré sous la forme la plus facile à générer et à utiliser par les ordinateurs, ce qui est très souvent un texte lisible.
Et lorsque vous générez du code, le processus de génération de code lui-même doit souvent être débogué - par des humains. C'est très, très utile si le code généré est lisible par l'homme afin que les humains puissent détecter les problèmes dans le processus de génération de code. Quelqu'un doit écrire le code pour générer du code, après tout. Cela ne vient pas de nulle part.
la source
Générer du code, juste une fois
Toute la génération de code source ne consiste pas à générer du code et à ne jamais le toucher; puis le régénérer à partir de la source originale quand il a besoin de mise à jour.
Parfois, vous générez du code une seule fois, puis vous supprimez la source d'origine et vous conservez ensuite la nouvelle source.
Cela se produit parfois lors du transfert de code d'une langue à une autre. En particulier si on ne souhaite pas reporter ultérieurement les modifications apportées à l'original (par exemple, le code de l'ancienne langue ne sera pas conservé ou il est en fait complet (par exemple, dans le cas de certaines fonctionnalités mathématiques)).
Un cas courant est que l’écriture d’un générateur de code à cette fin pourrait ne traduire en réalité que 90% du code correctement. et ensuite, les 10% restants doivent être réparés à la main. Ce qui est beaucoup plus rapide que de traduire 100% à la main.
De tels générateurs de code sont souvent très différents du type de générateurs de code produits par les traducteurs en langage intégral (comme Cython ou
f2c
). Depuis l'objectif est de faire maintenir le code une fois. Ils sont souvent fabriqués en tant que 1, pour faire exactement ce qu'ils doivent faire. À bien des égards, il s’agit de la version de niveau supérieur de l’utilisation d’un code regex / find-replace pour le port. "Portage assisté par un outil" pourrait-on dire.Générer du code, juste une fois, à partir, par exemple, d'un site web à gratter.
En relation étroite, si vous générez le code à partir d'une source à laquelle vous ne souhaitez plus avoir accès. Par exemple, si les actions nécessaires pour générer le code ne sont pas reproductibles, cohérentes ou si leur exécution coûte cher. Je travaille actuellement sur deux projets: DataDeps.jl et DataDepsGenerators.jl .
DataDeps.jl aide les utilisateurs à télécharger des données (comme des jeux de données ML standard). Pour ce faire, il faut ce que nous appelons RegistrationBlock. Il s’agit d’un code spécifiant des métadonnées, telles que le lieu de téléchargement des fichiers, une somme de contrôle et un message expliquant à l’utilisateur les termes / coditions / l’état de la licence des données.
Écrire ces blocs peut être ennuyant. Et cette information est souvent disponible dans (structurée ou non) à partir de sur les sites Web où les données sont hébergées. Donc, DataDepsGenerators.jl, utilise un webscraper pour générer le RegistrationBlockCode, pour certains sites hébergeant beaucoup de données.
Cela pourrait ne pas les générer correctement. Donc, le développeur utilisant le code généré peut et doit le vérifier et le corriger. Il y a de fortes chances qu'ils veuillent s'assurer qu'ils n'ont pas raté les informations de licence, par exemple.
Il est important de noter que les utilisateurs / devs travaillant avec DataDeps.jl n’ont pas besoin d’installer ou d’utiliser le navigateur Web pour utiliser le code RegistrationBlock généré. (Et ne pas avoir besoin de télécharger et d'installer un Web-Scraper permet d'économiser un peu de temps. En particulier pour les courses CI)
Générer du code source une fois n'est pas un anti-modèle. et il ne peut normalement pas être remplacé par une métaprogrammation.
la source
f2c
+cc
), mais le code résultant n’était pas vraiment un bon point de départ pour une version C du programme, AFAIK.f2c
retour du jour.)sed
va en effet très loin, mais il faut parfois un peu plus de pouvoir expressif. La ligne entre la logique du programme et les données est souvent fine. Parfois, la distinction n'est pas utile. JSON est (/ était) juste le code constructeur de l'objet javascript. Dans mon exemple, je génère également du code de constructeur d'objet (s'agit-il de données? Peut-être (peut-être pas, car il comporte parfois des appels de fonction). Est-il préférable de le traiter comme un code? Oui.)La génération de code "source" est une indication d'une lacune du langage généré. Est-ce que l'utilisation d'outils pour surmonter ceci est un anti-modèle? Absolument pas, laissez-moi vous expliquer.
Généralement, la génération de code est utilisée car il existe une définition de niveau supérieur qui peut décrire le code résultant beaucoup moins verbeusement que le langage de niveau inférieur. La génération de code facilite donc l’efficacité et la concision.
Quand j'écris c ++, je le fais parce que cela me permet d'écrire du code plus efficace que d'utiliser du code assembleur ou machine. Le code machine est encore généré par le compilateur. Au début, c ++ était simplement un préprocesseur qui générait du code C. Les langages à usage général sont parfaits pour générer un comportement à usage général.
De la même manière, en utilisant un DSL (langage spécifique à un domaine), il est possible d'écrire des mots, mais peut-être que le code est restreint à une tâche spécifique. Cela rendra moins compliqué de générer le comportement correct du code. Rappelez-vous que le code est un moyen de se terminer . Ce qu’un développeur recherche, c’est un moyen efficace de générer un comportement.
Idéalement, le générateur peut créer un code rapide à partir d’une entrée plus simple à manipuler et à comprendre. Si cela est accompli, ne pas utiliser de générateur est un anti-motif . Cet anti-motif provient généralement de la notion que le code "pur" est "plus propre", de la même manière qu'un ouvrier du bois ou un autre artisan pourrait envisager l'utilisation d'outils électriques, ou l'utilisation de la CNC pour "générer" des pièces (think golden marteau ).
D'autre part, si la source du code généré est plus difficile à gérer ou à générer du code pas assez efficace, l'utilisateur tombe dans le piège qui consiste à utiliser les mauvais outils (parfois à cause du même marteau d'or ).
la source
La génération de code source signifie absolument que le code généré est une donnée. Mais ce sont des données de première classe, des données que le reste du programme peut manipuler.
Les deux types de données les plus courants que je connaisse et qui sont intégrés au code source sont les informations graphiques sur les fenêtres (nombre et emplacement des divers contrôles) et les ORM. Dans les deux cas, l'intégration via la génération de code facilite la manipulation des données, car vous n'avez pas à passer par des étapes "spéciales" supplémentaires pour les utiliser.
Lorsque vous travaillez avec les Macs d'origine (1984), les définitions de dialogue et de fenêtre ont été créées à l'aide d'un éditeur de ressources dans lequel les données sont conservées au format binaire. L'utilisation de ces ressources dans votre application était plus difficile que si le "format binaire" avait été Pascal.
Donc, non, la génération de code source n'est pas un anti-motif, elle permet de rendre la partie données de l'application, ce qui facilite son utilisation.
la source
La génération de code est un anti-modèle quand il en coûte plus que ce qu'il accomplit. Cette situation se produit lorsque la génération a lieu de A à B, où A est presque le même langage que B, mais avec quelques extensions mineures qui pourraient être effectuées simplement en codant en A avec moins d’effort que tous les outils personnalisés et l’établissement de la compilation de A à B. .
Le compromis est plus prohibitif vis-à-vis de la génération de code dans des langues dépourvues de fonctions de méta-programmation (macros structurelles) en raison des difficultés et des insuffisances liées à la réalisation de la métaprogrammation via la mise en scène du traitement de texte externe.
Le compromis médiocre pourrait également être lié à la quantité d'utilisation. La langue A peut être substantiellement différente de B, mais le projet dans son ensemble avec son générateur de code personnalisé n’utilise A que dans un ou deux petits endroits, de sorte que la complexité totale (petits bits de A, plus le générateur de code A -> B, plus la mise en scène environnante) dépasse la complexité d’une solution qui vient d’être réalisée en B.
Fondamentalement, si nous nous engageons à générer du code, nous devrions probablement «aller gros ou aller chez nous»: lui donner une sémantique substantielle, et l’utiliser beaucoup, ou ne pas déranger.
la source
Je n'ai pas vu cela énoncé clairement (je l'ai vu évoqué par une ou deux réponses, mais cela ne semblait pas très clair)
La génération de code (comme vous l'avez dit, comme s'il s'agissait de données) n'est pas un problème, c'est un moyen de réutiliser un compilateur à des fins secondaires.
L’édition de code généré est l’un des anti-patterns les plus insidieux, pervers et horribles que vous rencontrerez jamais. Ne faites pas cela.
Au mieux, la modification du code généré génère un lot de codes de mauvaise qualité dans votre projet (l'ensemble ENTIER de code est désormais véritablement CODE SOURCE - plus de données). Dans le pire des cas, le code introduit dans votre programme est hautement redondant, avec des ordures mal nommées et presque impossibles à maintenir.
Je suppose qu'une troisième catégorie est le code que vous utilisez une fois (générateur d'interface graphique?) Puis que vous modifiez pour vous aider à démarrer / à apprendre. C’est un peu de chaque - cela peut être un bon moyen de commencer, mais votre générateur d’IUG sera destiné à utiliser du code "Generatable" qui ne sera pas un bon début pour vous en tant que programmeur - En plus, vous pourriez être tenté de le réutiliser pour une seconde interface graphique, ce qui signifie qu’il faut extraire du code SOURCE redondant dans votre système.
Si vos outils sont suffisamment intelligents pour interdire toute modification du code généré, lancez-vous. Sinon, je dirais que c'est l'un des pires anti-modèles du marché.
la source
Le code et les données sont les suivants: Information.
Les données sont les informations exactement sous la forme dont vous avez besoin (et valeur). Le code est aussi une information, mais sous une forme indirecte ou intermédiaire. En substance, le code est également une forme de données.
Plus spécifiquement, le code est une information permettant aux machines de décharger des êtres humains du traitement d’informations par eux-mêmes.
Décharger des êtres humains du traitement de l'information est le motif le plus important. Les étapes intermédiaires sont acceptables dans la mesure où elles facilitent la vie. C'est pourquoi des outils de mappage d'informations intermédiaires existent. Comme les générateurs de code, les compilateurs, les transpileurs, etc.
Supposons que quelqu'un vous propose une telle fonction de cartographie, dont l'implémentation vous est obscure. Tant que la fonction fonctionne comme promis, vous soucieriez-vous de générer du code source en interne ou non?
la source
Dans la mesure où vous stipulez plus tard que le code est une donnée, votre proposition se réduit à "Si quelque chose peut être généré, alors ce n'est pas du code". Diriez-vous alors que le code assembleur généré par un compilateur C n'est pas un code? Et si cela coïncide exactement avec le code d'assemblage que j'écris à la main? Vous pouvez y aller si vous le souhaitez, mais je ne viendrai pas avec vous.
Commençons plutôt avec une définition de "code". Sans être trop technique, une définition plutôt bonne pour les besoins de cette discussion serait "des instructions exploitables par une machine pour effectuer un calcul".
Eh bien oui, votre proposition de départ est que le code ne peut pas être généré, mais je rejette cette proposition. Si vous acceptez ma définition de «code», la génération de code en général ne devrait poser aucun problème conceptuel.
Eh bien, c’est une question tout à fait différente, sur la raison d’utiliser la génération de code, plutôt que sur sa nature. Vous proposez comme alternative que, au lieu d'écrire ou d'utiliser un générateur de code, on écrit une fonction qui calcule directement le résultat. Mais dans quelle langue? Il est révolu le temps où quiconque écrivait directement dans le code machine et si vous écrivez votre code dans une autre langue, vous dépendez d'un générateur de code sous la forme d'un compilateur et / ou d'un assembleur pour générer un programme qui s'exécute réellement.
Pourquoi, alors, préférez-vous écrire en Java, en C, en Lisp ou ailleurs? Même assembleur? J'affirme que c'est au moins en partie parce que ces langages fournissent des abstractions pour les données et les opérations qui facilitent l'expression des détails du calcul que vous voulez effectuer.
Il en va de même pour la plupart des générateurs de code de niveau supérieur. Les cas prototypiques sont probablement des générateurs de scanner et d’analyseur tels que
lex
etyacc
. Oui, vous pouvez écrire un scanner et un analyseur directement en C ou dans un autre langage de programmation de votre choix (même du code machine brut), et parfois un. Mais pour un problème de complexité significative, l'utilisation d'un langage de niveau supérieur, tel que lex ou le yacc, facilite l'écriture, la lecture et la maintenance du code manuscrit. Habituellement beaucoup plus petit aussi.Vous devriez également considérer ce que vous entendez exactement par "générateur de code". Je considérerais le prétraitement C et l’instanciation de modèles C ++ comme des exercices de génération de code; vous opposez-vous à cela? Sinon, je pense que vous devrez effectuer une gymnastique mentale pour rationaliser l'acceptation de celles-ci, mais en rejetant d'autres versions de la génération de code.
Pourquoi? Vous pensez en principe qu’il faut un programme universel vers lequel l’utilisateur insère des données, certaines classées comme "instructions" et d’autres comme "entrée", et qui effectue le calcul et émet plus de données que nous appelons "sortie". (D'un certain point de vue, on pourrait appeler ce programme universel "système d'exploitation".) Mais pourquoi supposez-vous qu'un compilateur devrait être aussi efficace pour optimiser un programme aussi polyvalent que pour optimiser un logiciel plus spécialisé? programme? Les deux programmes ont des caractéristiques et des capacités différentes.
Vous dites que comme si une bibliothèque d'interface universelle à un certain degré serait nécessairement une bonne chose. Peut-être que ce serait le cas, mais dans de nombreux cas, une telle bibliothèque serait volumineuse et difficile à écrire et à gérer, voire lente. Et si une telle bête n’existe pas en réalité pour résoudre le problème particulier qui se pose, alors qui souhaitez-vous qu’elle soit créée, quand une approche de génération de code peut résoudre le problème beaucoup plus rapidement et facilement?
Plusieurs choses, je pense.
Les générateurs de code transforment le code écrit dans une langue en code dans une langue différente, généralement de niveau inférieur. Vous vous demandez alors pourquoi les gens voudraient écrire des programmes dans plusieurs langues et surtout pourquoi ils pourraient vouloir mélanger des langues de niveaux subjectivement différents.
Mais j'ai déjà abordé ce sujet. On choisit un langage pour une tâche particulière, basé en partie sur sa clarté et son expressivité pour cette tâche. Dans la mesure où le code plus petit contient en moyenne moins de bogues et est plus facile à gérer, il existe également un biais en faveur des langages de niveau supérieur, du moins pour les travaux à grande échelle. Cependant, un programme complexe implique de nombreuses tâches. Souvent, certaines d'entre elles peuvent être traitées plus efficacement dans une langue, alors que d'autres sont traitées de manière plus efficace ou plus concise dans une autre langue. Utiliser le bon outil pour le travail signifie parfois employer de la génération de code.
la source
Répondre à la question dans le contexte de votre commentaire:
Un compilateur ne sera jamais optimisé pour votre tâche. La raison en est simple: elle est optimisée pour effectuer de nombreuses tâches. C'est un outil à usage général utilisé par de nombreuses personnes pour différentes tâches. Une fois que vous savez quelle est votre tâche, vous pouvez aborder le code de manière spécifique à un domaine, en faisant des compromis que les compilateurs ne pourraient pas.
Par exemple, j'ai travaillé sur des logiciels pour lesquels un analyste peut avoir besoin d'écrire du code. Ils pourraient écrire leur algorithme en C ++ et ajouter toutes les astuces de vérification des liens et de mémorisation dont ils dépendent, mais cela nécessite de connaître beaucoup le fonctionnement interne du code. Ils préfèrent écrire quelque chose de simple et me laisser jeter un algorithme pour générer le code C ++ final. Je peux ensuite réaliser des astuces exotiques pour optimiser les performances, telles que l'analyse statique, que je ne m'attendrais jamais de la part de mes analystes. La génération de code leur permet d'écrire d'une manière spécifique à un domaine, ce qui leur permet d'obtenir le produit plus facilement que n'importe quel outil à usage général.
J'ai aussi fait exactement le contraire. J'ai un autre travail que j'ai fait qui avait un mandat "pas de génération de code". Nous voulions toujours faciliter la vie des utilisateurs du logiciel. Nous avons donc utilisé une quantité considérable de métaprogrammation de modèles pour que le compilateur génère le code à la volée. Ainsi, je n'avais besoin que du langage généraliste C ++ pour faire mon travail.
Cependant, il y a un piège. Il était extrêmement difficile de garantir la lisibilité des erreurs. Si vous avez déjà utilisé un code métaprogrammé de modèle auparavant, vous savez qu'une seule erreur innocente peut générer une erreur qui prend 100 lignes de noms de classe incompréhensibles et d'arguments de modèle pour comprendre ce qui s'est mal passé. Cet effet était tellement prononcé que le processus de débogage recommandé pour les erreurs de syntaxe était "Faites défiler le journal des erreurs jusqu'à ce que vous voyiez la première fois qu'un de vos propres fichiers comporte une erreur. Allez à cette ligne, a eu tort. "
Si nous avions utilisé la génération de code, nous aurions pu disposer de capacités de traitement d'erreur beaucoup plus puissantes, avec des erreurs lisibles par l'homme. C'est la vie.
la source
Il y a différentes manières d'utiliser la génération de code. Ils pourraient être divisés en trois groupes principaux:
J'imagine que vous parlez du troisième type de code généré, puisqu'il s'agit de la forme la plus controversée. Dans les deux premières formes, le code généré est une étape intermédiaire qui est très nettement séparée du code source. Mais dans la troisième forme, il n'y a pas de séparation formelle entre le code source et le code généré, sauf que le code généré a probablement un commentaire qui dit "ne modifie pas ce code". Cela laisse quand même le risque aux développeurs d’éditer le code généré, ce qui serait vraiment moche. Du point de vue du compilateur, le code généré est le code source.
Néanmoins, de telles formes de code généré peuvent être vraiment utiles dans un langage à typage statique. Par exemple, lors de l'intégration avec des entités ORM, il est vraiment utile de disposer de wrappers fortement typés pour les tables de la base de données. Bien sûr, vous pouvez gérer l'intégration de manière dynamique au moment de l'exécution, mais vous perdriez la sécurité du type et le support des outils (complétion de code). Un avantage majeur du langage de type statique est la prise en charge du système de type au type d’écriture plutôt qu’au moment de l’exécution. (Inversement, ce type de génération de code n’est pas très répandu dans les langages à typage dynamique car, dans un tel langage, il n’offre aucun avantage par rapport aux conversions d’exécution.)
Étant donné que la sécurité de type et l'achèvement de code sont des fonctionnalités souhaitées au moment de la compilation (et lors de l'écriture de code dans un IDE), les fonctions standard ne sont exécutées qu'au moment de l'exécution.
Cependant, il peut y avoir un terrain d’entente: F # supporte le concept de fournisseurs de types qui est essentiellement des interfaces fortement typées générées par programme lors de la compilation. Ce concept pourrait probablement remplacer de nombreuses utilisations de la génération de code et offrir une séparation plus nette des problèmes.
la source
Les jeux d'instructions du processeur sont fondamentalement impératifs , mais les langages de programmation peuvent être déclaratifs . L'exécution d'un programme écrit dans un langage déclaratif nécessite inévitablement un certain type de génération de code. Comme mentionné dans cette réponse et d'autres, l'une des principales raisons de générer du code source dans un langage lisible par l'homme est de tirer parti des optimisations sophistiquées effectuées par les compilateurs.
la source
Vous l'avez mal compris. Il faut lire
Si quelque chose peut être introduit dans un générateur pour les interprétables , alors c'est du code, pas des données.
C'est le format source de cette étape de compilation, et le format du récepteur est toujours du code.
la source
gcc -fverbose-asm -O -S
n'est pas un code source (et il ne s'agit pas uniquement de données), même s'il s'agit d'une forme textuelle toujours alimentée par GNUas
et parfois lue par des humains.