La fin du moc ?

Qt est un framework C++ relativement ancien. Pour supporter des fonctionnalités intéressantes (gestion des signaux et slots, métaobjets, introspection, etc.) sur un grand nombre de compilateurs pas forcément très au courant des standards C++, il a dû développer un outil de génération de code, le moc (metaobject compiler).

Il donc n’est pas suffisant de compiler les fichiers source d’une application et de les lier avec le framework, puisqu’il faut générer du code. Ce simple oubli a déjà causé bien des pertes de temps, même pour des développeurs plus chevronnés. Les outils de compilation s’y sont habitués (QMake, CMake et d’autres), les EDI également… mais pas forcément les utilisateurs : il est la cause d’un certain nombre de grandes incompréhensions de la part de débutants avec le framework. Les plus puristes n’apprécient pas non plus cette partie hors standard de la chaîne de compilation.

Plus techniquement, moc est un outil en ligne de commande qui prend en entrée un fichier source de l’application avec des macros particulières (la plus connue étant Q_OBJECT) et génère un autre fichier source à ajouter à la compilation. Ce dernier implémente les métaobjets grâce aux informations récoltées par ces macros particulières. Pour cette étape de génération, moc utilise un analyseur assez naïf qui se limite à l’extraction des informations nécessaires à son bon fonctionnement, il achoppe donc sur du code C++ plus avancé ou sur les nouveautés de C++11/14.

Clang

Une première expérience était de remplacer l’étape du moc externe au compilateur par une extension de ce compilateur, afin de générer le code nécessaire à la volée. moc-ng s’occupe de cette partie, avec un plug-in pour Clang (un compilateur C++), mais aussi un outil en ligne de commande équivalent au moc mais utilisant Clang pour l’analyse du code source de l’application (et évite donc les écueils de la solution actuelle).

Clang est bien connu pour ses messages d’erreur, bien plus explicites que ceux d’autres compilateurs. Proposer le moc en tant qu’extension permet de générer des messages d’erreur en cas de mauvaise utilisation des commandes du générateur, notamment en cas de typo. Le message généré par le moc actuel est très suboptimal :

Grâce aux fonctionnalités plus avancées de Clang pour la recherche dans les fichiers source, l’extension propose même la correction de la typo à l’origine de l’erreur :

Bien qu’il ne s’agisse que d’une expérience, d’une preuve de faisabilité, cette réimplémentation fonctionne bien : elle peut remplacer le moc pour la compilation d’une grande partie de Qt, les tests n’échouant pas plus souvent qu’avec le moc officiel.

Se passer du moc

Boost implémente déjà les concepts de signaux et slots sans utiliser de précompilateur dans Boost.Signals2. Le moc propose également des fonctionnalités d’introspection et de réflexion, c’est à ce moment que le comité de standardisation du C++ entre en jeu : le document N3814 est une demande de propositions pour la réflexion à la compilation. Cette fonctionnalité sera intégrée au plus tôt dans C++17 et aucune version n’est actuellement implémentée dans un compilateur.

La proposition N3951 donne de nouvelles significations aux mots clés typedef et typename, de telle sorte que le code

class SomeClass {
public:
  int foo();
  void bar(int x);
};

vector names = { typename... };
auto members = std::make_tuple(typedef...);

deviendrait équivalent à

 vector names =  { "SomeClass",  "foo", "bar" };
auto members = std::make_tuple(static_cast(nullptr), &SomeClass::foo, &SomeClass::bar);

Ainsi, un compilateur implémentant cette fonctionnalité pourrait générer l’équivalent du QMetaObject actuel à l’aide de quelques traits, sans outil extérieur. Pour le moment, il est possible d’expérimenter cette fonctionnalité en réécrivant soi-même les typedef… et typename… dans le prototype, avec le grand avantage de garder la compatibilité binaire avec le code généré par le moc : les structures générées donnent tous les slots, les signaux et les propriétés requises pour la construction du métaobjet à l’exécution. Pour distinguer les signaux et slots, C++11 propose des attributs, mais il serait plus agréable de les placer au niveau des spécifications d’accès de groupe (soit une extension du document N3955) :

class MyObject : public QObject {
    Q_OBJECT
public [[qt::slot]]:
    void fooBar();
    void otherSlot(int);
public [[qt::signal]]:
    void mySignal(int param);
public:
   enum [[qt::enum]] Foobar { Value1, Value2  };
};

Sources : Proof Of Concept: Re-implementing Qt moc using libclang et Can Qt’s moc be replaced by C++ reflection?

Voir aussi :

Qt Creator 3.1 beta

Qt Creator 3.1 est désormais disponible en préversion, peu après Qt 5.2. Parmi les nouveautés, le moteur d’analyse du C++ interne peut être remplacé par Clang : l’avantage principal est que Clang est bien plus précis sur la sémantique du C++ que le moteur interne, mais au prix de performances bien moindres (ce qui a d’ailleurs ralenti fortement le développement de cette fonctionnalité, en cours de développement depuis 2011).

Pour rappel, Clang est un compilateur C++ basé sur LLVM, une architecture de compilateurs libre notamment développée par Apple et désormais utilisée sur un certain nombre de BSD. Il supporte une série de langages proches de C++, comme C ou Objective-C. Il s’agit également du premier compilateur libre à supporter complètement le standard C++14, notamment avant GCC.

Outils de développement

Cette fonctionnalité est activable à la demande, comme un plug-in (ClangCodeModel). Les binaires proposent cette fonctionnalité, mais elle n’est pas (encore) activée par défaut. La différence principale à ce niveau sera pour ceux qui compilent eux-mêmes Qt Creator : il faudra disposer de libclang (détails dans les sources).

Autre changement au niveau des outils de développement, toute version de GDB sans script Python est désormais dépréciée (la dernière plateforme pour laquelle une telle version de GDB n’est pas disponible est OS X, mais LLDB le remplace avantageusement, surtout avec le support de fonctionnalités autrefois réservées à GDB). Le profileur QML a été aux petits oignons, avec un grand nombre de bogues corrigés.

Un autre plug-in expérimental fait son apparition : Beautifier, pour reformater du code. Il peut utiliser les outils externes Artistic Style, Clang-format et Uncrustify.

Support de Windows RT

La dernière préversion de Qt 5 propose un support expérimental de Windows RT, Qt Creator se met au diapason avec un nouveau plug-in (encore expérimental, comme le support de WinRT dans Qt) pour supporter cette plateforme. Tout ne fonctionne pas encore, mais il est déjà utilisable. Parmi les fonctionnalités qui fonctionnent déjà bien :

  • enregistrement du Windows Runtime et de Qt pour Windows Phone ;
  • support de Windows Runtime et des Windows Phone Kit ;
  • compilation d’applications pour Windows Runtime et Windows Phone ;
  • lancement d’applications par un bouton depuis l’EDI.

Pour le moment, cependant, le débogage n’est pas encore fonctionnel, le bouton est d’ailleurs grisé dans l’interface.

Plus en détail, pour gérer le déploiement et le lancement d’applications, le plug-in utilise deux nouveaux outils en ligne de commande, windeployqt et winrtrunner, proposés par Qt 5.3. En tant que plug-in expérimental, le support de Windows RT n’est pas activé par défaut.

Sources : Qt Creator 3.1 beta released et Experimental Version of Qt Creator’s WinRT Plugin