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.
Certains langages de programmation offrent ce type de fonctionnalité, notamment Java, Smalltalk ou C# qui fournissent des outils pour connaître et utiliser la classe d'un objet, ses propriétés et ses méthodes (ou 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 des 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 le header de chaque classe (à l'exception de C++ Mirror). De plus, il est souvent nécessaire d'hériter d'une classe de base (QObject pour Qt).
Ceci nous a amené à développer CAMP, une bibliothèque de réflexion généraliste pour C++. CAMP utilise intensivement les templates C++ (et boost) et est non-intrusif (à l'exception de la gestion du polymorphisme pour les méta-classes ; une alternative basée sur le RTTI 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, documentation de l'API. Le code est quand à lui hébergé sur GitHub. Un forum est également disponible.
# Comparaisons
Posté par Gof (site web personnel) . Évalué à 5.
Le journal donne quelques avantages par rapport a Qt, par example
- Pas besoin du préprocesseur comme Moc. (Mais est-ce que un préprocesseur est vraiment un problème ?)
- Pas besoin d'hériter de QObject. (Ça c'est cool car QObject a un overhead de ~100 octets par objects sur une arch 64bit)
Par contre, un inconvénient de taille est que il faut enregistrer sois même toutes les fonctions manuellement.
Mais qu'en est-il des performances et de l'utilisation de la mémoire ?
Qt ajoute toutes les chaînes de caractère l'une a la suite des autres de manière assez compacte. Et sans presque aucun impact lors du lancement.
CAMP, va éparpier les chaînes un peu partout dans le programme, plus avoir plein de structures sur le tas. (Sans compter aussi
toutes les tables virtueles (mémoire non-partagable entre proccesses))
Et le coût lors du démarrage est il ou non négligable ? (Enregistrements de toutes les classes dans les structures internes + rélocations)
Comptez vous a long terme avoir une compatibilité source ou binaire ?
[^] # Re: Comparaisons
Posté par Laurent . Évalué à 2.
Soyons clairs : CAMP fournit un service d'abstraction haut niveau pour programmes plus ou moins conséquents, pas un truc dont le but est d'être embarqué sur des systèmes avec 32 Ko de mémoire vive. Si tu cherches quelque chose qui économise 100 octets ou qui ne fragmente pas la mémoire, alors clairement CAMP ne t'intéressera pas, ce n'est pas la voie que l'on poursuit.
En ce qui concerne les avantages, utiliser un préprocesseur n'est pas un luxe que l'on peut toujours se permettre car cela complexifie la chaîne de compilation. Par exemple, avec Visual Studio sans le plugin d'intégration Qt (qui n'est pas disponible librement), c'est quasiment impossible de développer, les règles d'invocation des différents outils de précompilation de Qt sont bien trop laborieuses à écrire.
L'avantage de ne pas hériter de QObject n'est bien sûr pas d'économiser 100 octets, mais de se débarasser d'une dépendance sur les classes et de rendre le système non-intrusif. C'est un gage de maintenabilité et de d'adaptabilité très important, et ça évite les petites surprises du genre "on ne peut pas hériter de 2 bases QObject". En clair, avec CAMP tu peux binder std::string ; pas avec Qt.
Quant à l'enregistrement manuel des metaclasses, rien n'empêche d'utiliser un précompilateur du genre gccxml ou open-c++ (désolé si les noms sont approximatifs) pour générer le code d'enregistrement. L'important est que l'on n'impose pas d'outil externe à l'utilisateur, c'est lui qui choisit.
Nous sommes soucieux des performances, de sorte que CAMP ne soit pas un frein quelque soit l'utilisation que l'on en fait ; concrètement il faut qu'on puisse utiliser CAMP pour binder des classes en Python ou en Lua et garder le maximum de performances que ces langages fournissent.
Enfin, oui nous comptons bien entendu préserver une compatibilité binaire et source. A partir de la version 1.0, les choses seront faites dans les règles.
[^] # Re: Comparaisons
Posté par alberthier (site web personnel) . Évalué à 1.
Je bosse depuis plusieurs années avec Qt et je l'utilisation de moc est complètement transparente pour moi:
sous unix les systèmes de build qmake/cmake font le boulot tout seuls.
sous visual studio, pas besoin de plugin, il suffit d'écrire une "Build Rule" une fois pour toutes pour faire passer moc sur les .h et après on l'oublie. Pas besoin de version pro pour ça, c'est possible avec la version "express" de visual.
Je trouve donc que ces critiques sur moc ne sont pas vraiment justifiées vu que c'est assez facile de l'oublier si ton système de build tient la route.
Par contre, je suis d'accord avec toi sur les autres remarques (obligation d'hériter de QObject et la macro Q_OBJECT à ajouter)
[^] # Re: Comparaisons
Posté par Laurent . Évalué à 1.
En plus, utiliser un précompilateur implique que l'utilisateur n'a aucun degré de personnalisation sur les infos qui sont exportées :
- choisir ce qui est exporté et ce qui ne l'est pas
- ajouter des informations non présentes dans le code C++ (tags, visibilité, informations d'ownership, ajouter de l'aide, ...)
- changer le nom des symboles exportés
- ...
Pour pallier à ça il faudrait ajouter des informations spécifiques dans le code (intrusives !), et dans ce cas on perd tout l'intérêt du précompilateur, autant écrire le binding directement.
Ceci-dit, idéalement nous pourrions choisir un précompilateur parmi les outils libres existant, et l'adapter pour proposer directement aux utilisateurs notre propre précompilateur pour CAMP. Je pense que ce serait un très bon ajout pour CAMP.
[^] # Re: Comparaisons
Posté par Gof (site web personnel) . Évalué à 1.
(on dit "public slots:" pour exporter les slots)
On peux aussi ajouter des attributs and le code source pour "tagger" des fonctions (fonction dépréciée, internes, ...)
Je ne vois pas en quoi ses informations sont intrusive. au contraire, elle sont là où elle doivent être, à coté de la déclaration de la fonction.
Tu dis que vous pouvez utilisé un précompilateur, mais quels sont les précompilateur qui marche "out-of-the-box" pour le moment.
Car dit comme ça, Qt ne force pas l'utilisation du moc non plus, n'importe quel préprocesseur qui peux générer le code qui va bien convient, (il pourrait même être écrit à la main pour les mazo). C'est juste que le moc fonctionne bien et que donc il n'y a besoin de rien d'autre.
[^] # Re: Comparaisons
Posté par Laurent . Évalué à 1.
Ces informations sont intrusives car elles se trouvent à l'intérieur de la définition de la classe. Qu'elles soient bien placé ou non n'y change rien : ta classe sera forcément fortement couplée au système de meta-objet et dépendante de celui-ci. Or je ne t'apprendrai rien en te disant que le découplage des fonctionnalités orthogonales est une bonne chose en C++.
Par exemple, limitation très simple : avec un tel système (intrusif) il est impossible de binder une classe qui n'a pas été prévue dès le départ pour cette utilisation (et on ne bosse pas toujours avec des classes qu'on a écrites soi-même).
En ce qui concerne les précompilateurs, on ne s'est pas encore penchés sur le problème, mais il y a des projets connus qui devraient répondre à ces attentes. C'est un projet à creuser un peu plus.
[^] # Re: Comparaisons
Posté par windu.2b . Évalué à 2.
N'étant pas un développeur C++ (et donc encore moins développeur Qt), pourrait-on en savoir d'avantage à ce sujet ? Quelles sont les contraintes que cela impose d'hériter de QObject et d'utiliser la macro Q_OBJECT ?
Merci.
[^] # Re: Comparaisons
Posté par alberthier (site web personnel) . Évalué à 2.
class MaClasseAMoi : public QObject
{
Q_OBJECT
public:
MaClasseAMoi() : QObject()
{
}
virtual ~MaClasseAMoi()
{
}
}
La macro Q_OBJECT, on s'en fout, c'est juste un placeholder pour des methodes que Qt va ajouter.
Par contre le fait de devoir hériter de QObject peut être ennuyeux parfois: si tu dois hériter d'une classe de base d'un framework que tu utilise et que tu aimerai en faire un QObject aussi => tu es obligé de faire de l'héritage multiple, c'est pas terrible et surtout QObject doit être la première classe de base, dans la déclaration d'héritage sinon, ça marche pas (je sais pas si c'est toujours le cas).
Après pour l'intrusivité des macros Qt, je trouve pas ça gènant non plus, c'est comme les annotation en Java ou Python et comme dit plus haut: les information sont dans le contexte où elles doivent être: dans la declaration de la classe et pas éparpillées dans d'autres endroits du code.
Bon, j'ai pas une vision très objective : j'ai toujours considéré que le C++/Qt était un langage différent du C++.
[^] # Re: Comparaisons
Posté par Alex . Évalué à 2.
[^] # Re: Comparaisons
Posté par Laurent . Évalué à 1.
- en cas d'héritage multiple, QObject doit être la première base
- on ne peut pas hériter de plusieurs classes dérivant de QObject
- un objet dérivé de QObject n'est pas copiable
Ensuite, ce système est intrusif. Il faut en effet taper directement dans la déclaration de la classe pour modifier ses meta-informations.
Conséquence directe : on ne peut pas appliquer le système sur des classes figées et non prévues pour fonctionner avec Qt (par exemple je ne peux pas créer une meta-classe Qt pour la classe Toto de la bibliothèque SuperLib que je viens d'installer -- dommage).
Dans une moindre mesure, le côté intrusif est également pénalisant au niveau des dépendances entre les fichiers (temps de compilation accrus) et de la maintenance (le code relatif aux meta-informations est imbriqué dans la déclaration de la classe et mélangé au reste).
Enfin, on ne pourrait pas non plus utiliser les meta-informations uniquement de manière interne, avec un tel système on est obligé d'exposer les meta-informations à toute personne extérieure qui utilise la classe.
De manière générale, on apprend toujours que découpler les fonctionnalités orthogonales est meilleur que de tout imbriquer ensemble.
Suivre le flux des commentaires
Note : les commentaires appartiennent à celles et ceux qui les ont postés. Nous n’en sommes pas responsables.