CMake est très utilise dans le monde C++ ; malheureusement, il est assez complexe de développer autour, car il fonctionne comme une sorte de boite noire. On donne un CMakeLists.txt en entrée et on obtient un de Makefile ou fichier projet en sortie. Par contre, il est difficile d’accéder à la représentation en interne du projet, notamment pour les EDI.
Le but est de développer une sorte de serveur, à qui on pourrait envoyer des requêtes sur le projet en cours. Par exemple, demander la liste de fichiers pour la cible « mabibliotheque » ou encore « monbinaire ».
La communication se fait via un protocole JSON, lisible avec le module du même nom de Qt 5. Le projet est plus ou moins fonctionnel à l’heure actuelle. Une intégration upstream est en discussion mais risque de durer.
Des développeurs d’autres EDI, notamment Milian Wolff et Kevin Funk, tous les deux de KDevelop, étaient aussi là pour présenter leurs problématique, et s’assurer que ce serveur soit le plus générique possible.
« Multithreading with Qt », par Giuseppe D'Angelo
Cette présentation avait pour thème la parallélisation en C++ avec Qt :
- QThread ;
- synchronsation ;
- thread safety avec Qt ;
- Qt et STL concernant le multi-threading.
QThread hérite de QObject : il n’est donc ni copiable ni déplaçable, possède des signaux et des lots et est conçu pour gérer un fil d’exécution.
On peut créer un fil d’exécution avec ou sans boucle évènementielle. Si l'on ne souhaite pas de boucle événementielle, on peut simplement hériter de QThread et réimplémenter la méthode run(), on démarrera alors le fil d’exécution avec QThread::start(). Pour un QThread avec boucle événementielle, on appellera la methode QThread::exec(). On utilisera alors quit() ou exit() pour sortir du fil d’exécution. On peut modifier la priorité du fil d’exécution par la méthode setPriority(). Il est aussi possible d'utiliser une QEventLoop ou d'appeler directement QCoreApplication::processEvent().
C’est une mauvaise idée d'appeler sleep() au sein d'un fil d’exécution. Il vaut mieux tirer parti des signaux et slots (ou alors utiliser le polling).
Il n’est pas possible d’effectuer des opérations affectant l’IHM depuis un autre fil d’exécution que le principal. Impossible d’utiliser QWidget/Qt Quick/QPixmap hors du fil d’exécution principal. Par contre, ce n'est pas gênant d'utiliser des QImage/QPainter.
Concernant OpenGL, cela dépend de la configuration. La méthode QopenGLContext::supportThreadedOpenGL() permet de savoir si le multithread est géré.
Il faut faire attention à ne jamais bloquer le fil d’exécution principal, qui se trouve être responsable de l’IHM : ne pas appeler sleep(). Faire attention à supprimer tous les QObject d'un fil d’exécution avant de le supprimer.
Pour s’assurer de la suppression des QObject à la fin du fil d’exécution, on peut :
- allouer les objets sur la pile ;
- connecter deleteLater au signal QThread::finished() ;
- déplacer les objets encore nécessaires dans le fil d’exécution principal (qApp->thread()).
Quand on parle de synchronisation, le mot-clé est : « data race ». Nécessite deux choses : au moins un des deux accès est une écriture et les accès ne sont pas atomiques, aucun accès n’ayant lieu avant l’autre.
Qt propose différentes classes pour synchroniser : QMutex, QSemaphore, QWaitCondition,, QReadWriteLock, QAtomicInt, QAtomicPointer<T>. Il y a aussi des classes RAII pour la gestion des verrous, comme QMutexLocker.
Une fonction est dite thread-safe si on peut appeler cette fonction au même moment, depuis différents fil d'exécution, sur les mêmes données, sans devoir synchroniser du côté développeur.
Réentrant : pareil que thread-safe mais sur des données différentes.
Non-réentrant (thread-unsafe) : fonction qui ne peut être appelée que depuis seul fil d’exécution à la fois.
Selon la documentation, QObject est réentrant mais, en pratique, cela est faux.
Alors, comment communiquer avec un QObject vivant dans une autre fil d’exécution ? Avec les connections multi-thread : QueuedConnection. Cela postera l'événement dans la boucle d’événement du fil d’exécution dans lequel vit le QObject receveur. L’objet receveur doit absolument vivre dans un fil d’exécution possédant une boucle d’événement. Les types passés en argument doivent être enregistrés via qRegisterMetaType.
Le type de la connexion est décidé uniquement au moment de l'émission du signal, sauf si le type est précisé lors de l'appel à QObject::connect().
Ce n'est pas un problème d’ajouter des signaux dans une sous-classe de QThread, mais ajouter des slots est le signe d'une mauvaise conception.
On peut sans souci combiner Qt et la bibliothèque standard (C++11) au niveau du multi-threading. Il est à noter que beaucoup d’outils peuvent vérifier le bon usage de l’API STL concernant le multithreading, mais ils ne fonctionnent pas avec Qt, à moins que l’implémentation de Qt ne repose sur la STL. QThread est plus pratique d’utilisation lorsque l'on travaille avec des QObjects.
À la fin de la session, les différents types venant de la STL et de Qt furent comparés.
« Making QML optional », par Andrew Knight
Problématique : les applications complexes reposant sur Qt Quick/QML rencontrent des problèmes de performances, de la lenteur, notamment au démarrage. En effet, tout le code QML doit être lu et compilé. L'idée de base est de prendre les classes de Qt Quick et de les exposer via une interface C++.
Le temps de création de la version C++ de l'interface est beaucoup plus rapide. Les temps de rendu sont les mêmes. Avec Qt Quick 1, la création de chaque bouton entraîne la création de nombreux QObject. Qt Quick 2 est bien plus efficace. L’implémentation maison ne nécessite qu'un seul QObject par bouton.
Discussion sur des classes permettant d'exposer des fonctions de dessin de bas niveau avec Qt Quick.