Anglais

MFC contre Qt

par Pascal Audoux

Introduction

J'ai eu l'occasion de développer successivement avec Qt et avec les MFC. J'aimerqi partager mon expérience à propos des différences entre les deux boites à outils.

Je ne suis pas un auteur professionnel. Cet article n'est donc pas aussi bien écrit que ce que vous pourrez trouver sur des sites web professionels ou dans des magazines. Il s'agit simplement de mon expérience, que je partage avec mes propres mots.

Cet article ne prétend pas être objectif. Je ne prétend pas détailler toutes les qualités et les défauts de Qt ou des MFC. Cet article est juste le reflet de mes impressions.

L'article est écrit d'un point de vue pragmatique: mon chef me donne les spécifications des applications qu'il veut et je les développe. J'en ai développées certaines avec Qt, et d'autres avec les MFC.

Les MFC (Microsoft Foundation Class) sont une boite à outils graphique pour le système Windows. C'est une surcouche plus ou moins objet d'une partie de l'API Win32 développée par Microsoft. L'API est tantot en C, tantot en C++, et en général un mélange pas très clair.

Qt est une boite à outils graphique C++ démarrée en 1994 par Trolltech ( www.trolltech.com). Qt fonctionne sous toutes les versions de Windows, sous MacOs X, sous tous les Unix et sur des équipements embarqués comme le Sharp Zaurus. C'est une bibliothèque totalement orientée objet.

Le modèle document-vue

La programmation en MFC impose l'emploi du Document-Vue et des templates (il est très difficile de ne pas les utiliser, mais rien ne garantit qu'ils soient bien utilisé). Les templates sont cependant figés et il est très difficile de faire quelque chose de non prévu au départ (par exemple splitter une zone pour y afficher deux vues ayant des documents différents, etc...). Un autre problème est que c'est souvent le template qui crée les vues et il n'est pas forcément prévu d'y accéder ensuite. Tout doit être fait par le document, ce qui peut parfois poser quelques problèmes.

Qt n'impose pas de modèle de développement (mais il ne les interdit pas non plus). Il est ainsi possible d'utiliser un modèle document-vue lorsque cela s'avère judicieux, et ceci sans problème d'implémentation.

Pseudo-objet contre pur object

La différence fondamentale entre les MFC et Qt est leur conception.

Les MFC sont une sorte de surcouche objet permettant d'accéder à l'API windows qui est en C. L'architecture n'est pas très objet. Il est nécessaire dans plein de situations de passer une structure 'C' avec une quinzaine de champs (dont seulement un ou deux vous intéressent), appeler des méthodes dont certains paramètres sont obsolètes, etc...

Et il y a plein de trucs sans aucune logique. Par exemple, si on créé en MFC un objet graphique il ne sera en réalité pas créé tant que la méthode Create() n'aura pas été appelée. Pour les dialogues, il faut attendre OnInitDialog(), pour les vues OnInitialUpdate(), etc. Ainsi, si on créé un objet (manuellement) et qu'ensuite on appelle certaines de ses méthodes, le programme pourra planter !!

Prenons l'exemple d'un dialogue où on a un champ d'edition Cedit, l'appel depuis le dialogue de la méthode GetWindowText() sur ce champ plantera tant qu'on ne sera pas dans le DoModal() (soit avant soit après !!). On s'étonne qu'ensuite le C++ ait la réputation de planter bizarrement.

Qt est tout le contraire. C'est basé sur une architecture purement objet, réellement pensée (par des gens ayant eu l'expérience d'autres bibliothèques comme les MFC, Java, Motif...). Il en résulte une bibliothèque extrêmement cohérente à tous points de vue (nommage, objets, héritage...), complète (classes proposées, méthodes des classes) tout en restant simple d'utilisation (arguments et valeurs retournées des méthodes sont logiques...).

Sous Qt, si vous avez un champ d'édition (QlineEdit), vous le créer avec l'opérateur new, comme n'importe quelle classe et vous pouvez appeler TOUTES ses méthodes, n'importe quand à partir du moment où l'objet existe. Manipuler cet objet revient exactement à manipuler l'objet affiché. Il n'y a pas de trucs à connaitre, ca fonctionne de la façon la plus simple qu'on peut imaginer.

La boucle de messages

Les MFC sont construits autour d'une boucle d'évenements (appelés aussi messages). Toute action se rapporte à la réception et à l'émission de messages. Il existe des milliers de messages renvoyés par Windows au programme... Malheureusement, il n'est du tout évident de savoir lequel utiliser, quelles infos il contient, etc... Certains sont redondants, certains ne sont pas toujours émis, etc... la documentation est d'ailleurs assez déficiente à ce sujet (pas facile de savoir quel objet émet quel message, ni quand). Il est aussi parfois difficile de savoir qui recevra un message émis et qui ne le recevra pas, etc... Certaines fonctionnalités qui pourraient être exécutées par appel de méthodes sont remplacées par des envois de messages, ce qui ne facilite en rien le debuggage ni la relecture du code.

Sous Qt, il existe un système de callback basé sur l'envoi de signaux et leur réception dans des slots qui permet de communiquer entre des classes. Ce système est très souple (on dit explicitement à l'objet d'écouter un signal donné). Il est aussi plus simple car le nombre de signaux différents pouvant être émis par une classe est restreint (rarement plus de 4-5). Ce système est assez proche des 'listener' de Java, mais bien plus léger à utiliser en pratique.

Création graphique

Les MFC ne gèrent pas les réarrangements (layout) dans les fenêtres : cela pose d'énormes problèmes si on veut qu'une fenêtre soit de taille variable. Il faut repositionner soi-même tous les contrôles (cela explique pourquoi tant de dialogues sous Windows ont une taille fixe). Le problème devient plus grave quand on doit traduire une application, car beaucoup de langues ont des expressions plus longues que l'anglais. Vous devez retravailler vos fenêtres pour chaque langue.

L'éditeur de ressources de Visual Studio est fortement limité : il permet de placer des contrôles à l'écran, et c'est à peu près tout. Quelques propriétés sont disponibles via l'onglet 'properties' et le 'class wizard' permet de créer des variables et méthodes relativement facilement. Il faut cependant remarquer qu'il serait quasiment impossible de le faire à la main (gérer la boucle de message à la main, les DDX, IMPLEMENT_DYNCREATE, etc...).

Sous Qt, tout peut être codé à la main, parce que le code est simple. Pour créer un bouton, il suffit de faire:

button = new QPushButton( "texte de mon bouton", MaFenetre );

Pour appeler une methode 'action()' si le bouton est clické, écrivez :

connect( button, SIGNAL( clicked() ), SLOT( action() ) );

Qt a un système d'arrangement automatique qui est tellement simple à utiliser que c'est une perte de temps que de vouloir s'en passer.

Il existe de plus un outil, 'Qt Designer', qui permet une programmation proche d'un RAD (vous pouvez associer facilement du code aux actions, l'éditeur de properties est complet, associer des signaux et des slots, etc...). Il gère le réarrangement automatique et génère du code lisible et compréhensible. Le code est généré dans un fichier séparé, de sorte que vous pouvez modifier votre interface alors que vous l'utiliser déjà dans votre code.

Qt Designer permet de faire des choses qui sont pratiquement impossibles en MFC. Essayez par exemple d'afficher une listview avec des champs préremplis en MFC, ou de faire un 'tab-control' avec différentes vues suivant les onglets...

La documentation

La documentation est un aspect primordial à considérer lorsqu'on veut utiliser une bibliothèque graphique riche en fonctionnalités. Celle de Visual, MSDN semble pléthorique, tient sur 10 CDROM. On y trouve des articles pour faire toutes sorte de choses. Cependant, elle laisse l'impression que ce qui est documenté provient d'une mauvaise architecture. La navigation à l'intérieur est de piètre qualité : impossible d'accéder facilement depuis une classe a ses classes mères ou filles, présentations des méthodes sans la signature, accès difficiles aux méthodes héritées, etc. Par ailleurs, en recherchant de l'aide sur un mot-clé, on tombe sur l'aide de tous les langages sur ce mot-clé: Java, Visual Basic, C++, InterDev, ...

La documentation de Qt est excellente. Le mieux est que vous la regardiez vous-meme: doc.trolltech.com

Elle est à la fois copieuse et compète, puisqu'elle couvre tout Qt, et pourtant, elle ne prend que 18 Mo. Toutes les classes et toutes les méthodes sont documentées correctement, avec une foule de details et des exemples. Il est facile de naviguer d'une classe à une autre, en utilisant soit la version html, soit l'outil fourni par Trolltech (Qt Assistant). La documentation comprend bien sur un tutoriel et des exemples illustrants des utilisations typiques. Il existe aussi une FAQ et une mailing-list, dont les archives sont accessibles via le web ou un newsgroup. Si vous possedez une licence, vous pouvez contacter le support qui répond dans la journée.

Le fait que la conception objet de Qt soit bien pensée est un facteur expliquant la clarté de l'aide, un des objectifs de Trolltech étant La documentation doit être tellement bonne qu'il ne doit pas y avoir besoin de faire du support.

L'Unicode

Sous Qt, toutes les Qstring sont nativement en unicode. A noter qu'il existe des méthodes permettant de convertir les Qstring en char * (lorsque cela est possible, bien sur). Du code Qt est donc automatiquement unicode et il n'y a donc aucun problème.

En MFC, ce n'est pas le cas. Tout d'abord il faut compiler/linker avec des options spéciales (changer le point d'entrée de l'exécutable, etc...). Ensuite, il faut rajouter des _T autour de toutes les chaînes (simple et double quote), etc... Il ne faut plus utiliser de char, mais des TCHAR, toutes les fonctions C de manipulation de chaîne sont remplacées par d'autres (autre nom) etc... De plus un programme compilé avec les options 'unicode' ne fonctionnera pas avec une DLL non compilée avec ces options.

Remarque : techniquement la grande différence vient de la conception de la classe Cstring en MFC à comparer à celle de la class Qstring. Cstring est basiquement un char * auquel on a rajoute quelques méthodes. Cela a l'avantage que partout ou on a le droit d'utiliser des char *, on peut utiliser une Cstring. Ca paraît bien a première vue mais cela entraîne une série impressionnante d'inconvénients, en particulier parce qu'on peut alors modifier directement le char * sans réellement se soucier de la classe (dangereux) et on a vu les problèmes posés lorsqu'on est en unicode.

La Qstring au contraire stocke en interne un char *, en effet et même une version unicode de la chaîne, ce qui présente l'inconvénient d'être plus lourd en effet. Il résulte cependant une puissance lors de l'utilisation, d'où l'absence de problèmes lors de l'unicode par exemple. Les QString possêdent par ailleurs des fonctionnalités bien pratique, comme la copie paresseuse (économie de mémoire et de temps) C'est un exemple typique d'une conception objet correcte de la part de Qt et d'une bidouille tirée du C pour les MFC.

L'internationalisation

Il est possible d'internationaliser un programme MFC. Il 'suffit' de mettre toutes les chaînes que l'on veut voir traduites dans une String Table, (et mettre des LoadString( identifieur ) dans le code, ce qui n'est quand même pas pratique si on veut la modifier rapidement ou connaître simplement le texte). Ensuite il faut transformer les ressources en DLL, traduire les chaînes de la string table (en utilisant Visual) dans la langue désirée, traduire aussi les ressources graphiques (qui ont du texte qui ne se trouve pas dans la String Table) et demander au programme d'utiliser cette DLL.

Sous Qt, il faut rajouter la macro " tr " devant les chaînes que l'on veut traduire. Ensuite il suffit d'utiliser un programme fourni qui permet d'extraire ces chaînes et surtout les affiche dans une interface permettant la traduction aisée (accès à un dictionnaire, rappel du contexte d'utilisation, gestion des modifications des phrases à traduire, etc...). Il est à noter que c'est une interface à destination d'un traducteur ce qui est bien différent d'une édition de DLL sous Visual.

Cet éditeur permet aussi de traduire les raccourcis clavier (pratique). Il est sous licence GPL (et donc gratuit, améliorable...). Le fichier sur lequel travaille l'éditeur est en xml, les spécifications et le code étant à disposition de tout le monde, c'est une garantie de plus de ne pas être bridé ni limité par Qt en cas de changement de format de leur part, etc...

Les problèmes des ressources

Une partie du développement doit être faite via les " ressources ". Il est indispensable de les utiliser pour de nombreux cas de développement en MFC.

Sous Qt, il n'existe pas de système de ressources, ce qui résout tous les problèmes cités précédemment.

Prix

Quand vous avez acheté Visual Studio, les MFC viennent avec gratuitement.

Qt est gratuit et libre pour sa version Unix (disponible sous licence GPL). Une version gratuite est disponible sous Windows pour utilisation non commerciale. En revanche, pour du développement commercial classique, à sources fermées, vous devez acheter une licence. Cette licence est valable pour une plate-forme, pour un seul développeur, à vie. Elle inclut le support pendant une année. Il n'y a pas de coût de redistribution associé. Le prix est assez cher pour une petite société: 1500 $ (avec des réductions pour l'achat de plusieurs licences).

Cependant, il faut noter que ce prix, c'est moins de la moitié du coùt d'un programmeur pendant un mois. Pour le même développement en Qt et en MFC, on gagne bien plus qu'un demi mois en terme de vitesse de développement et de richesse de l'application résultante. C'est un investissement qui vaut le coup.

Distribution

Pour distribuer votre application MFC, vous dépendez de la présence de la DLL MFC42.dll sur le système d'exploitation cible. Mais pour le même nom, MFC42.dll, il y a en fait trois versions de cette DLL. Il est donc nécessaire de vérifier que l'utilisateur a la version correcte, et sinon, de la mettre à jour. Le mise à jour va changer le comportement de tout plein d'applications sur son système. C'est plutot inconfortable. Que dire si ses applications ne marchent plus après la mise à jour ?

Qt nomme ses DLL explicitement (Qt-mt303.dll). Ainsi, il n'y a aucun risque de modifier le comportement d'une application dépendant d'une autre version de Qt quand on en installe une nouvelle. C'est plus rassurant.

Autres avantages de Qt

Qt n'est pas un concurrent de MFC. C'est un toolkit C++ à part entière. Il fournit ainsi de nombreuses fonctionnalités non présentes dans les MFC, ou bien présentes mais pénibles à utiliser.

Et Codejock ?

Les nombreux inconvénients des MFC ont fait qu'il existe un certain nombre de surcouches des MFC qui permettent de les utiliser plus facilement ou mieux. J'ai ainsi utiliser la bibliothèque CodeJock et la question peut se poser de comparer MFC+Codejock à Qt. Voici quelques points à noter :

Conclusion

La conclusion qui ressort de mon expérience est évidente : Qt est bien mieux que les MFC, tant en terme de rapidité de développement que de fonctionnalités. Qt permet de faire des programmes de meilleure qualité, avec moins de peine.

Certains se sont plaint que cet article était biaisé vers Qt et n'était pas assez objectif vis à vis des MFC. Il s'agit simplement de notre expérience: programmer avec Qt était simple et rapide, alors que plein de problèmes sont apparus avec les MFC. Si les MFC ont des points positifs, nous ne les avons pas encore rencontré, en dehors du fait d'etre livré gratuitement avec Visual Studio. Le fait que Microsoft ai décidé de les laisser complètement tomber en faveur de .NET vient corroborer notre point de vue.

Nous sommes bien évidemment avide de toute remarque supplémentaire: pour des suggestions, des améliorations, des remarques ou des critiques, contactez-nous!.

Je souhaite inclure des citations de personne ayant utilisé Qt et les MFC. Si vous êtes dans ce cas, écrivez-nous!

Liens

D'autres personne ont écrit (en anglais) des comparaisons avec Qt:


Copyright Philippe Fremy 2002 Last modification : $Date: 2005/07/02 17:45:27 $ - $Author: philippe $