web-dev-qa-db-fra.com

Quelle est l'utilité de la métaprogrammation?

J'ai lu:

et j'avoue une certaine confusion quant à l'objectif de la métaprogrammation/génération de code.

Quelqu'un at-il un exemple concret de l'utilisation de la métaprogrammation/génération de code? Encore mieux serait une explication d'accompagnement pour expliquer pourquoi c'était mieux qu'une alternative.

edit : Thistle serait-il considéré comme une méta-programmation?

70
Wayne Werner

Imaginez un gars qui construit des voitures. Disons que c'est la même chose que d'utiliser un ordinateur.
À un moment donné, il se rend compte qu'il fait toujours la même chose, plus ou moins.
Alors il construit des usines pour construire des voitures, et c'est beaucoup mieux. Il programme maintenant!
Néanmoins, encore une fois, à un moment donné, il se rend compte qu'il fait toujours la même chose, dans une certaine mesure.
Maintenant, il décide de construire des usines qui construisent des usines qui construisent des voitures. C'est de la métaprogrammation.

La métaprogrammation est extrêmement puissante, mais un petit problème dans le système fait que tous les avantages se transforment en difficultés monstres. Alors maîtrisez-le et utilisez-le ... Ou restez à l'écart!

112
Benoît

Je pense à la métaprogame comme "des programmes qui écrivent (ou modifient) d'autres programmes". (Une autre réponse disait "des usines qui font des usines", belle analogie).

Les gens y trouvent toutes sortes d'utilisations: personnalisation des applications, génération de code passe-partout, optimisation d'un programme pour des circonstances particulières, implémentation de DSL, insertion de code pour gérer les problèmes de conception orthogonaux ("aspects") ...

Ce qui est remarquable, c'est le nombre de mécanismes différents qui ont été inventés pour faire cela au coup par coup: modèles de texte, macros, conditions de préprocesseur, génériques, modèles C++ - aspects, aspects, réflexion, ... d'autres mécanismes dans d'autres langues, et la plupart des langues n'ont aucun support de métaprogrammation. Cette distribution dispersée des capacités signifie que vous pourriez être en mesure de faire certains types de métaprogrammation dans une langue, avec des limitations, et pourtant ne pas être en mesure de faire ces types dans une autre. C'est aggravant: -}

Une observation que j'ai suivie jusqu'au bout est que l'on peut construire des machines génériques de métaprogrammation qui fonctionnent avec n'importe quelle langue sous la forme programme transformations . Une transformation de programme est un modèle paramétré: "si vous voyez cette syntaxe , remplacez-la par que syntaxe ".

Une transformation en soi n'est généralement pas impressionnante, mais des dizaines ou des centaines peuvent apporter des modifications spectaculaires au code. Parce que les transformations de programme (sophistiquées) peuvent en fait simuler une machine de Turing, elles peuvent effectuer des modifications de code arbitraires, y compris toutes ces techniques ponctuelles que vous trouvez dispersées.

Un outil qui accepte les définitions de langage. transformations spécifiques au langage et génère une autre pour appliquer ces transformations est un méta - outil de métaprogrammation: un programme pour écrire des "programmes qui écrivent des programmes".

La valeur est que vous pouvez appliquer un tel outil pour effectuer de nombreuses variétés de modifications de code arbitraire. Et, vous n'avez pas besoin que le comité de conception linguistique se rende compte que vous voulez un type particulier de support de métaprogrammation, et dépêchez-vous de le fournir afin que vous puissiez continuer votre travail aujourd'hui.

Une leçon intéressante est que ces machines ont besoin d'une solide analyse de programme (tables de symboles, contrôle et analyse de flux de données, etc.) pour l'aider à se concentrer sur les problèmes dans le code, afin que les machines de métaprogrammation puissent faire quelque chose à ce stade un faible exemple de cela est les spécifications pointues dans certains aspects, qui disent "apportez des modifications à des endroits qui ressemblent à ceci").

Le PO a demandé des exemples précis de cas d'application de la métaprogrammation. Nous avons utilisé notre outil de métaprogrammation "meta" ( DMS Software Reengineering Toolkit ) pour effectuer les activités suivantes sur grand code base automatiquement:

  • Migration linguistique
  • Mise en œuvre de la couverture des tests et des profileurs
  • Implémentation de la détection des clones
  • Réingénierie architecturale massive
  • Génération de code pour le contrôle d'usine
  • SOAisation des contrôleurs de réseau embarqués
  • Extraction d'architecture pour logiciel mainframe
  • Génération d'instructions vectorielles SIMD à partir de calculs de tableaux
  • Rétro-ingénierie du code retour aux concepts

dans de nombreux langages, dont Java, C #, C++, PHP, ...

Le PO a également demandé: "Pourquoi était-ce mieux que l'alternative?" La réponse a à voir avec l'échelle, le temps et la précision.

Pour les grandes applications, la taille de la base de code signifie que vous n'avez pas les ressources ou le temps pour effectuer de telles analyses ou modifications à la main.

Pour les tâches de génération ou d'optimisation de code, vous pouvez peut-être le faire à la main, mais les outils peuvent le faire beaucoup plus rapidement et plus précisément.

Essentiellement, ces outils font ce que les êtres humains ne peuvent tout simplement pas.

Il convient de noter que les outils n'ont aucune créativité; vous avez toujours besoin d'humains pour déterminer ce qu'ils doivent faire, par exemple, pour décider quelle est la tâche (voir la liste ci-dessus pour des exemples) et déterminer comment définir les analyses/transformations pour obtenir l'effet. Vous avez toujours besoin de méta - programmeurs. Cependant, lorsqu'un méta-programmeur arme un tel outil avec les bonnes connaissances, le code résultant peut sembler être construit par un codeur expert incroyablement rapide et créatif.

20
Ira Baxter

J'ai tiré le meilleur parti de la métaprogrammation pour faire le pont entre différentes API.

Un exemple de travail serait FireBreaths JSAPIAuto1 qui facilite l'écriture des classes C++ qui sont exposées à JavaScript. En fournissant une fonction d'enregistrement pour les fonctions qui doivent être exposées, les types d'arguments peuvent être inspectés et à partir de ce code approprié généré au moment de la compilation qui convertit les types script-API en types C++ natifs et inversement, même en prenant directement en charge map, vector, etc.

À titre d'exemple simple, considérons une fonction add(a, b) exposée qui utilise certains types d'API de script:

ScriptVariant add(const std::vector<ScriptVariant>& values) {
    // have to check argument count
    if(values.size() != 2)
        throw script_error("wrong number of arguments");

    try {
        // have to convert from scripting-API types
        long a = values[0].convert_cast<long>();
        long b = values[0].convert_cast<long>();
        return a+b; // potentially need to convert back too
    } catch(ScriptVariant::bad_cast& e) {
        // need to handle conversion failure
        throw script_error("conversion failed :(");
    }
}

La logique réelle enfouie ne contient qu'une seule ligne, à savoir que les vérifications et les conversions sont ennuyeuses et redondantes. Avec l'installation d'enregistrement mentionnée précédemment (par exemple dans le constructeur):

registerMethod("add", make_method(this, &MyClass::add));

cela peut maintenant simplement s'écrire:

long add(long a, long b) {
    return a+b;
}

... et le framework se charge de générer pour vous le code nécessaire.

1: Bien que je ferais un peu d'implémentation ... plus propre ... si je devais recommencer

5
Georg Fritzsche

Mon exemple concret récent (6 derniers mois) de génération de code:

  1. J'ai un script SQL Plus qui génère puis exécute d'autres scripts SQL Plus. Le script génère exécute des requêtes sur certaines tables qui ont des champs d'horodatage, et lorsque j'ai conçu le script, il était impossible de savoir quelle fenêtre de temps sélectionner. Ainsi, le script principal fait son travail et détermine quelles plages de temps doivent être dans les sous-scripts. Il génère ensuite les indices en écrivant leur code dans un fichier (et en remplaçant les espaces réservés par les heures de début et de fin réelles). Enfin, il exécute le ou les indices. J'ai utilisé cette astuce pour quelques situations maintenant (bien que souvent plus compliquées que celle-ci) où la structure des sous-étapes dépend des résultats des étapes précédentes.

  2. Une fois, j'ai obtenu un tableur mappant des éléments d'un XSD à des colonnes de table dans une base de données. Il a été possible de générer des extraits de code XSL et des requêtes complètes à partir de la feuille de calcul à l'aide de macros et de VBA. Ces extraits et requêtes ont été copiés et collés (la plupart du temps tels quels, sans modifications nécessaires) dans le système qui les a exécutés et a traité les résultats. Ce n'était pas une jolie solution, mais cela rendait certainement un travail très fastidieux beaucoup moins fastidieux, et le code qui en résultait était probablement beaucoup plus cohérent que si j'avais passé une semaine ou deux à tout écrire à la main.

SO liste d'exemples de métaprogrammation: Quels sont les exemples les plus cool de métaprogrammation que vous avez vus en C++?

Je peux donner mon propre exemple: je développe ABSE , qui est une approche de méta-programmation. Avec ABSE, vous créez un modèle (en fait, un arbre) où chaque élément est un "atome". Ce Atom représente un "concept" et contient les métadonnées nécessaires à sa définition.

Dans l'ABSE, la mise en œuvre d'un concept est en fait un "mini-programme".

Ensuite, le modeleur hôte ( AtomWeaver , développé en parallèle avec ABSE) prend le modèle et "tisse" un programme générateur de tous ses atomes. Ce programme est ensuite exécuté, générant les artefacts souhaités (code source, données, etc.).

Ainsi, le workflow ABSE est:

  1. Créer un concept discret (une fraction du méta-métaprogramme)
  2. Réutiliser ce concept dans un modèle (construire efficacement le métaprogramme)
  3. Le modélisateur hôte tisse et exécute le métaprogramme
  4. Le métaprogramme génère votre programme final

À première vue, cela ressemble à beaucoup de travaux redondants et complexes, mais c'est en fait assez simple si vous comprenez le concept.

Avantages de la méta-programmation (non exclusive à ABSE)?:

  • Changer le modèle et régénérer un système complet (Imaginez des fonctionnalités de refactoring au lieu de lignes sources).
  • La modification de quelques définitions dans le modèle peut entraîner la création de programmes distincts (une famille de produits logiciels).
  • En réutilisant des modèles, vous pouvez changer le code du modèle, régénérer et faire changer votre code dans des dizaines, des centaines d'endroits.
  • Beaucoup d'autres, vraiment

La métaprogrammation, la génération de code, la transformation de programmes sont de nouveaux mondes passionnants dans le développement de logiciels, à mon humble avis. Cependant, la métaprogrammation nécessite une nouvelle compétence: la méta-pensée.

Nous pouvons définir la méta-pensée comme "penser à la façon dont vous pensez de votre propre développement". Une sorte de réflexion en classe, appliquée sur vous-même. En pratique, vous devez découvrir vos propres modèles de développement, les isoler, les rendre génériques, puis les transformer en métaprogrammes en utilisant votre technique préférée, qu'il s'agisse d'ABSE, de DSL, de DSM, etc.

3
Rui Curado

Les bibliothèques/codes basés sur la métaprogrammation aident à écrire du code directement explicite et simple qui générera pour vous le code des détails d'implémentation, en fonction des paramètres utilisés.

Boost est plein de bibliothèques (C++) qui démontrent ce qui peut être réalisé avec la métaprogrammation. Proto quelques bons exemples (et peut-être difficiles à comprendre) qui permettent la mise en œuvre de DSL , Spirit qui permettent d'écrire un compilateur en utilisant la grammaire EBNF directement à l'intérieur du code, et de nombreuses autres bibliothèques époustouflantes.

2
Klaim

Je vais essayer d'expliquer mon exemple concret d'utilisation de techniques de méta-programmation.

J'ai créé un outil de programme qui générera le code source de la page Web ASP.NET à partir de n'importe quel formulaire de saisie de données MS Access. La technique que j'ai utilisée était de créer mes propres modèles de texte ASP.NET pour chaque type de contrôle de formulaire. J'ai simplement branché les valeurs telles que TOP, LEFT, HEIGHT, WIDTH, CONTROLSOURCE à partir des métadonnées des objets du formulaire MS Access. Par exemple, mon modèle pour une zone de texte ASP.NET ressemble à ceci:

 <asp:TextBox ID="**ID**" runat="server" style="z-index: 1; left: **LL**px; top: **TOP**px; position: absolute"  Text='<%# Bind("[**CTLSOURCE**]") %>' />

après avoir obtenu les valeurs de métadonnées du contrôle de zone de texte, mon programme génère le code de la zone de texte

<asp:TextBox ID="txtCustomerID" runat="server" style="z-index: 1; left: 50px; top: 240px; position: absolute"  Text='<%# Bind("[CustomerID]") %>' />

Mon programme génère le code source de la page Web entière pour un formulaire MS Access en 2-3 secondes. L'alternative est de coder à la main la page Web ASP.NET à partir de zéro; une tâche qui pourrait potentiellement prendre des heures voire des jours.

Imaginez une base de données MS Access avec des formulaires 24-35. Le codage manuel de chaque formulaire en tant que code source d'une page Web ASP.NET peut prendre des semaines, voire des mois. L'utilisation d'un outil de conversion avec des techniques de méta-programmation, dans ce cas , réduit le temps de développement des pages Web de quelques semaines et mois à quelques heures.

2
Jeffrey Schaffner

Un exemple précis où cela pourrait être une approche utile.

Vous disposez d'un ensemble de classes tierces, auxquelles vous souhaitez ajouter un comportement générique - par exemple une sorte de contrôle de sécurité/d'accès, le mappage d'objets en JSON, etc.

Vous pouvez écrire ou générer des sous-classes pour tout, en ajoutant des méthodes wrapper pour ajouter au contrôle d'accès et appeler la superclasse. Avec la méta-programmation, vous pouvez le faire au moment de l'exécution, et vos modifications seront automatiquement appliquées à toutes les classes tierces supplémentaires/modifiées.

Avec l'exemple JSON, en utilisant l'introspection de la classe, vous devriez pouvoir générer le code pour sérialiser un objet, puis l'ajouter en tant que méthode à la classe. Les autres extrêmes seraient de générer ou d'écrire le code à l'avance (avant la compilation) et d'avoir un impact à chaque fois que la classe change, ou une approche complètement générique qui utilise l'introspection sur chaque objet individuel, chaque fois que vous souhaitez le mapper.

Selon la langue et le temps d'exécution en question, une approche de métaprogame est susceptible d'être plus rapide que celle entièrement générique/introspective, mais plus lente que le code initial, car vous avez réduit beaucoup de recherches de données en code.

Là où la méta-programmation n'existe pas directement dans un langage, il me semble également qu'elle est souvent réinventée à travers des frameworks (c'est-à-dire des conteneurs de style IoC comme Spring).

1
JulesLt

Démarrez votre Visual Studio (Eclipse, Netbeans, quoi que ce soit d'autre). Créez un nouveau projet. Surprise - vous venez d'utiliser une métaprogrammation, en créant un projet à partir d'un modèle. N'est-ce pas pratique?

1
SK-logic

Nous utilisons beaucoup la méta-programmation pour créer des propriétés dans VBA. Nous avons diverses feuilles de calcul Excel avec de nombreux en-têtes et nous voulons définir les propriétés getter/setter pour chaque en-tête, ce qui nous permet de manipuler les cellules sous cet en-tête. Faire cela manuellement serait un cauchemar.

Le cadre de méta-programmation de choix pour nous était Notepad ++ et ses capacités d'expressions régulières de recherche/remplacement. Voici comment nous avons méta-programmé nos propriétés:

  • Copiez une liste d'en-têtes d'Excel vers Notepad ++
  • Enregistrez une macro Notepad ++ pour nettoyer les données (supprimez les espaces blancs et les caractères spéciaux). À la fin de cela, nous avons une liste de chaînes séparées par des sauts de ligne.
  • Copiez manuellement la liste dans un autre fichier .CSV et utilisez Excel pour générer une liste de numéros de ligne. Copiez ensuite de nouveau dans Notepad ++.
  • Écrivez une expression régulière pour convertir un nom de propriété en une définition de propriété, en ajoutant tous les espaces, mots-clés, etc. Utilisez le numéro de ligne comme numéro de colonne dans notre définition de propriété.

À la fin de cela, nous avons un processus qui est un mélange d'étapes manuelles, de macros enregistrées et d'une expression régulière que nous pouvons réappliquer chaque fois que nous voulons des propriétés pour une feuille. Et nous l'avons fait! À grand effet.

C'est le pouvoir de la méta-programmation. Quand l'utiliser est une question d'expérience/d'intuition. Mais je recommande de répondre à cette question:

Est-ce que ce sera plus rapide pour moi de coder cela directement, ou puis-je automatiser une partie ou la totalité du processus et accélérer mon processus?

Cela vous donne une ligne à tracer au-delà de laquelle la méta-programmation n'est plus utile. Si vous pouvez simplement le coder plus rapidement, même si c'est 10 répétitions, faites-le! Seulement si c'est des centaines de répétitions, ou si c'est quelque chose que vous comptez réutiliser plusieurs fois à l'avenir, alors méta-programmez-le.

Un autre point est qu'il y a des degrés ici. J'ai écrit une fois un programme Java pour créer un tas de fichiers pour ajouter une nouvelle inspection IntelliJ à un projet de codage des inspections. C'était un peu de surcharge: créer le Java projetez-le et compilez-le, etc. D'autre part, Notepad ++ find/replace est juste une petite étape au-dessus de la saisie manuelle de trucs vous-même. le point où cela a du sens. Pas besoin d'un programme Java quand Notepad ++ fera l'affaire. Pas besoin de Notepad ++ lors de la saisie manuelle, cela fera l'affaire.

0
Colm Bhandal

Vous pouvez regarder les macros Common LISP ou les modèles C++ et voir comment ils sont utilisés. Les deux sont des métaprogrammations dans le sens que vous utilisez. Vous constaterez que les deux sont largement utilisés dans beaucoup de code.

Les macros LISP sont souvent utilisées pour redéfinir la langue. Par exemple, le dernier chapitre de Paul Graham On LISP crée une extension orientée objet de travail pour Common LISP. Un autre exemple est le désormais disparu Grenat .

L'ancienne bibliothèque de modèles standard pour C++ (principalement intégrée dans la bibliothèque standard) était un moyen d'introduire un grand nombre de conteneurs et d'algorithmes qui fonctionnaient comme s'ils étaient intégrés dans le langage, au moins en termes d'intégration et d'efficacité (pas syntaxiquement) .

0
David Thornley