CAMP 0.7.0 : bibliothèque de réflexion en C++ sous LGPL

Posté par  (site web personnel, Mastodon) . Modéré par Nÿco.
Étiquettes :
13
16
juin
2010
Technologie
La réflexion (ou introspection) est un mécanisme qui permet à un programme d'examiner, voire de modifier ses structures internes. En d'autres termes, c'est la capacité d'un programme à examiner son propre état. Cela permet par exemple d'effectuer des mesures de performance, d'inspecter des modules, de déboguer un programme, ou encore de le faire d'évoluer automatiquement en fonction des besoins et de l'environnement.

Certains langages de programmation offrent ce type de fonctionnalité, notamment Smalltalk, Java et C#, qui fournissent des outils pour connaître et utiliser la classe d'un objet, ses propriétés et ses méthodes (on parle en général de méta-classe, de méta-propriété et de méta-fonction).

L'introspection peut être utilisée pour fournir des bindings vers des langages de script, pour écrire des éditeurs de propriétés, ou faire de la sérialisation. L'intérêt étant que le code de ces outils est écrit une fois pour fonctionner sur la structure abstraite (méta-classe, méta-propriété, méta-fonction), et peut ensuite être utilisé dans n'importe quel programme.

Malheureusement, le langage C++ ne fournit aucun moyen de faire de la réflexion. Il existe cependant un certain nombre de bibliothèques fournissant ce type de fonctionnalité, la plus connue étant probablement Qt avec ses QObjects.

Cependant, pour générer toutes les « méta-informations » nécessaires à la réflexion, la majeure partie de ces bibliothèques requiert soit d'utiliser un pré-compilateur (moc pour Qt), soit de déclarer ses informations en ajoutant tout un tas de macros dans l'en-tête de chaque classe ; C++ Mirror faisant exception. De plus, il est souvent nécessaire d'hériter d'une classe de base (QObject pour Qt).

Ceci a amené Tegesoft à développer CAMP, une bibliothèque de réflexion généraliste pour C++. CAMP utilise intensivement les templates C++, ainsi que boost (ensemble de bibliothèques C++ permettant d'étendre les fonctionnalités du langage). Il est non-intrusif, à l'exception de la gestion du polymorphisme pour les méta-classes ; une alternative basée sur le RTTI (Run-Time Type Information : fait de déterminer, à l'exécution du programme, le type d'une variable, dans les langages orienté-objet) du langage devrait être ajoutée dans la prochaine version.

CAMP ressemble à Luabind ou à boost::python, mais est généraliste. De ce fait, il est possible d'écrire un module pour CAMP permettant d'embarquer un interpréteur Python, un autre pour embarquer un interpréteur Lua, ou encore un pour faire de la sérialisation XML, l'intérêt étant que le binding des classes n'est fait ici qu'une seule fois. Lorsqu'un nouveau module pour CAMP est disponible, vous n'avez aucun binding supplémentaire à écrire.

Le mieux dans tout ça ? C'est sous LGPL ! La version 0.6.0 de CAMP était déjà distribuée sous GPL, mais une licence plus permissive a été choisie afin de favoriser l'utilisation de la bibliothèque.

De plus, un nouveau site internet (basé sur Redmine) est disponible, avec bug tracker, wiki, et documentation de l'API. Le code est quant à lui hébergé sur GitHub. Un forum est également disponible.

Aller plus loin

  • # Intéressant !

    Posté par  . Évalué à 8.

    Ça fait réfléchir.

    DLFP >> PCInpact > Numerama >> LinuxFr.org

    • [^] # Re: Intéressant !

      Posté par  (site web personnel) . Évalué à 4.

      C++ est probablement encore trop simple, même avec boost… il lui manquait bien la réflexion…
      • [^] # Re: Intéressant !

        Posté par  . Évalué à 2.

        ça fait 15-20 ans que les RTTI sont là.

        ce qui manque à C++ c'est pouvoir créer une nouvelle classe de toutes pièces, en créant une classe Class qui s'appellera prout, qui aura un attribut (membre) public qui s'appellera totoz qui sera un entier ou un Troll, une méthode qui s'appellera coin et prendra Pan comme argument...

        Java fait ça très simplement et dynamiquement (à l'execution, tu peux donner les noms de classes ou d'attributs à ce moment et les classes puis objets créés seront absolument comme les autres)
        • [^] # Re: Intéressant !

          Posté par  (site web personnel) . Évalué à -1.

          Il y a pas mal de bibliothèques qui permettent de faire ça, en C ou en C++
          • [^] # Re: Intéressant !

            Posté par  (site web personnel) . Évalué à 6.

            Pas mal ? On connait :
            - xcpprefl : utilise un pré-compilateur, mais semble pas mal
            - agm::libreflection : grosses macros qui tachent, directement dans la classe en plus
            - boost::mirror : grosses macros qui tachent (un petit appercu), pas directement dans la classe cependant
            - xrtti : utillise un pré-compilateur
            - open-c++ : utilise un pré-compilateur

            (Si tu connais d'autres bibliothèques, merci de me le indiquer, ça m'intéresse!)

            Le problème du pré-compilateur, en plus de complexifier la chaîne de compilation, c'est qu'on ne peut pas choisir ce qu'on veut exporter et ce qu'on ne veut pas (ce qui est une nécessité pour nous) sans ajouter des informations à la main. Du coup, quitte à déclarer quelque chose, autant que ce soit le binding plutôt que d'utiliser un outil supplémentaire.

            En ce qui concerne l'avantage des templates par rapport aux macros, c'est qu'on peut déterminer plus de chose à la compilation (genre les types, les nombres d'arguments, les const/non const...). Du coup, il y a beaucoup moins de chose à déclarer lors du binding.
        • [^] # Re: Intéressant !

          Posté par  (site web personnel) . Évalué à 3.

          La nature statique de C++ empêche évidemment de créer une classe de toute pièce.

          Ceci dit je suis curieux de savoir à quoi ça pourrait bien servir concrètement. C'est juste histoire de me coucher moins bête ce soir :)
          • [^] # Re: Intéressant !

            Posté par  (site web personnel) . Évalué à 1.

            Mettre en place un binding, par exemple vers un webservice ( c'est le 1er exemple qui me vient )
            • [^] # Re: Intéressant !

              Posté par  (site web personnel) . Évalué à 3.

              Dans ce cas là, on utilise des générateurs de code.
              • [^] # Re: Intéressant !

                Posté par  . Évalué à 2.

                Je ne suis pas un fan des générateurs de code.

                L'intérêt de ne pas générer de code permet de garder la base de code petite et aussi de ne pas ralentir la compilation (Ce qui fait perdre du temps aux devs). Ceci dit, on en paye inévitablement le prix lors de l'exécution.
                Est ce que ce prix est acceptable? Aux utilisateurs de trancher.

                Ça me fait penser au bon vieux troll langages dynamiques contre langages compilés.
          • [^] # Re: Intéressant !

            Posté par  . Évalué à 2.

            C’est du Ruby, mais j’ai eu besoin de faire un conteneur « typé », c’est à dire qui puisse dire dès l’insertion « hé, tu me donnes le mauvais truc » (mauvais truc étant, dans mon cas, la nécessité d’avoir une méthode, mais on peut imaginer d’autres trucs). Voila le code simplifié:

            class TypedArray < Array
            class << self
            attr_accessor :method
            end

            def <<(item)
            if !item.responds_to? self.class.method
            raise "#{item} does not respond to #{self.class.method}"
            end
            super
            end

            Qui s’utilise comme ça
            ArrayDelegate = Class.new TypedArray
            ArrayDelegate.method = :delegate=
            my_array = ArrayDelegate.new
            my_array.is_a? ArrayDelegate # Et ne doit pas répondre oui pour une autre sous-classe de TypedArray, comme un ToStringArray
            • [^] # Re: Intéressant !

              Posté par  (site web personnel) . Évalué à 1.

              Ah ok, merci pour l'exemple concret !
            • [^] # Re: Intéressant !

              Posté par  . Évalué à 2.

              Tu as conscience qu'en C++ ce genre de choses se détecte dès la compilation, et ce sans tous ces artifices ?

              La réflexion répond à certains besoins bien particuliers (le plus généralement : génération dynamique d'interfaces graphiques, plugins). Camp (de ce que j'en ai vi, je n'ai jamais pu la mettre en œuvre) est une excellente bibliothèque pour faire de la réflexion. Ca ne veut pas dire qu'il faut l'utiliser là où ça ne sert à rien :).

              Mes commentaires sont en wtfpl. Une licence sur les commentaires, sérieux ? o_0

              • [^] # Re: Intéressant !

                Posté par  . Évalué à 2.

                Oui mais si le monsieur ne code pas en C++ et qu'il a besoin de détecter qu'un objet du mauvais type est ajouté a son tableau?

                Rien ne te dit qu'il a eu le choix du langage. Et même s'il l'a eu, peut être qu'il préfère Ruby quand même.
              • [^] # Re: Intéressant !

                Posté par  . Évalué à 2.

                En pratique, ce n’est pas un raise que je fais derrière, mais un traitement bien plus compliqué. Simplement, je ne vois pas l’intérêt de donner 500 lignes de code pour illustrer quelque chose qui pourrait l’être en 10, d’où cette simplification.

Suivre le flux des commentaires

Note : les commentaires appartiennent à celles et ceux qui les ont postés. Nous n’en sommes pas responsables.