Share via


Visual C++

Exploration des nouvelles fonctionnalités C++ et MFC dans Visual Studio 2010

Sumit Kumar

Visual Studio 2010 présente d'énormes avantages pour les développeurs C++. De la capacité à tirer parti des nouvelles fonctionnalités offertes par Windows 7 jusqu'aux nouvelles fonctionnalités de productivité pour l'utilisation de grandes bases de code, chaque développeur C++ y trouvera une nouveauté ou une amélioration digne d'intérêt.

Dans cet article, je vais expliquer la manière par laquelle Microsoft a résolu certains des problèmes de portée globale auxquels font face les développeurs C++. Plus spécifiquement, Visual Studio 2010 offre un modèle de programmation plus moderne en ajoutant des fonctionnalités de langage fondamentales issues de la norme à venir C++0x et en remaniant la bibliothèque standard de façon à tirer parti des nouvelles fonctionnalités de langage. Il existe de nouvelles bibliothèques de programmation parallèle et de nouveaux outils destinés à simplifier la création de programmes parallèles. Vous constaterez également une amélioration des performances globales et de la productivité de développement grâce à IntelliSense et aux fonctionnalités de compréhension de code qui s'adaptent aux grandes bases de code. Et vous bénéficierez également d'une amélioration des performances des bibliothèques et d'autres fonctionnalités au moment du design, de la génération, de la compilation et de la liaison.

Visual Studio 2010 migre le système de génération vers MSBuild afin de le rendre plus personnalisable et de prendre en charge le multi-ciblage natif. Les améliorations apportées à la bibliothèque MFC exploitent toute la puissance des nouvelles API de Windows 7 et vous permettent d'écrire de fantastiques applications Windows 7.

Examinons d'un peu plus près ces améliorations axées sur le langage C++ dans Visual Studio 2010.

Fonctionnalités de langage fondamentales de C++0x

La finalisation de la prochaine norme C++ ne devrait plus tarder. Pour vous aider à appréhender les extensions C++0x, le compilateur Visual C++ dans Visual Studio 2010 prend en charge six fonctionnalités de langage C++0x fondamentales : les expressions lambda, le mot clé auto, les références rvalue, static_assert, nullptr et decltype.

Les expressions lambda définissent et construisent de manière implicite des objets de fonction sans nom. Elles fournissent une syntaxe naturelle et légère pour définir des objets de fonction là où elles sont utilisées, sans encourir de surcharge de performances.

Les objets de fonction offrent un moyen très puissant de personnaliser le comportement des algorithmes STL (Standard Template Library) et peuvent encapsuler à la fois le code et les données (contrairement aux fonctions ordinaires). Mais leur définition est peu commode car elle nécessite d'écrire des classes entières. De plus, ils ne sont pas définis à l'emplacement dans votre code source où vous tentez de les utiliser, et la non-localité les rend plus difficile à utiliser. Les bibliothèques ont tenté d'atténuer certains des problèmes de commentaires et de non-localité, mais n'offrent que peu d'assistance car la syntaxe devient compliquée et les erreurs de compilateur ne sont pas très conviviales. L'utilisation d'objets de fonction à partir de bibliothèques est également moins efficace car les objets de fonction définis comme membres de données ne sont pas in-line.

Les expressions lambda permettent de résoudre ces problèmes. L'extrait de code suivant montre une expression lambda utilisée dans un programme pour supprimer des entiers entre des variables x et y à partir d'un vecteur d'entiers.

v.erase(remove_if(v.begin(),
   v.end(), [x, y](int n) { 
   return x < n && n < y; }),
   v.end());

La seconde ligne montre l'expression lambda. Des crochets, appelés introducteurs de lambdas, indiquent la définition d'une expression lambda. Cette lambda prend l'entier n comme paramètre, et l'objet de fonction généré par la lambda possède les membres de données x et y. Comparez cela à un objet de fonction équivalent écrit à la main afin de mieux évaluer la commodité et le gain de temps offerts par les lambdas :

class LambdaFunctor {
public:
  LambdaFunctor(int a, int b) : m_a(a), m_b(b) { }
  bool operator()(int n) const {
    return m_a < n && n < m_b; }
private:
  int m_a;
  int m_b;
};
v.erase(remove_if(v.begin(), v.end(),
  LambdaFunctor(x, y)), v.end());

Le mot clé auto a toujours existé en C++, mais il était rarement employé car ne fournissant aucune valeur supplémentaire. C++0x remanie ce mot clé de façon à déterminer automatiquement le type d'une variable à partir de son initialiseur. Le mot clé auto réduit les commentaires et aide à mettre en avant le code important. Il évite les incompatibilités de type et les erreurs de troncation. Il contribue également à rendre le code plus générique en autorisant l'écriture de modèles qui se soucient moins des types des expressions intermédiaires, et permet de gérer efficacement les types non documentés tels que les lambdas. Le code suivant montre dans quelle mesure auto vous évite d'avoir à taper le type de modèle dans la boucle for qui itère un vecteur :

vector<int> v;
for (auto i = v.begin(); i != v.end(); ++i) {
// code 
}

Les références rvalue sont un nouveau type de référence introduit dans C++0x qui aide à résoudre le problème lié à la copie inutile et facilite le transfert parfait. Lorsque le côté droit d'une assignation est une rvalue, l'objet côté gauche peut réutiliser les ressources du côté droit plutôt que d'effectuer une allocation distincte, ce qui permet de disposer d'une sémantique de déplacement.

Le transfert parfait vous permet d'écrire un modèle de fonction unique qui prend n arguments arbitraires et les transfère de manière transparente à une autre fonction arbitraire. La nature de l'argument (modifiable, const, lvalue ou rvalue) est préservée lors de ce processus de transfert.

template <typename T1, typename T2> void functionA(T1&& t1, T2&& t2) {
  functionB(std::forward<T1>(t1), std::forward<T2>(t2));
}

Une explication détaillée des références rvalue étant hors de la portée de cet article, je vous conseille de consulter la documentation MSDN à l'adresse msdn.microsoft.com/library/dd293668(VS.100) pour plus d'informations.

Static_assert permet d'effectuer des assertions de test au moment de la compilation plutôt qu'au moment de l'exécution. Il vous permet de déclencher des erreurs de compilateur avec des messages d'erreur personnalisés faciles à lire. Static_assert est particulièrement utile pour la validation de paramètres de modèles. Par exemple, la compilation du code suivant provoquera l'erreur « erreur C2338 : custom assert: n should be less than 5 » :

template <int n> struct StructA {
  static_assert(n < 5, "custom assert: n should be less than 5");
};

int _tmain(int argc, _TCHAR* argv[]) {
  StructA<4> s1;
  StructA<6> s2;
  return 0;
}

Nullptr ajoute la cohérence des types aux pointeurs null et est étroitement lié aux références rvalue. La macro NULL (définie comme 0) et le littéral 0 sont couramment utilisés comme pointeur null. Cela n'a pas posé de problème jusqu'à maintenant, mais ils ne fonctionnent pas très bien en C++0x en raison des problèmes potentiels au niveau du transfert parfait. Le mot clé nullptr a donc été introduit spécifiquement pour éviter les échecs mystérieux au niveau des fonctions de transfert parfait.

Nullptr est une constante de type nullptr_t, qui est convertible en tout type pointeur mais pas en d'autres types tels que int ou char. En plus des fonctions de transfert parfait, nullptr peut être utilisé partout où la macro NULL était utilisée comme pointeur null.

Une petite remarque tout de même : NULL est toujours pris en charge par le compilateur et n'a pas encore été remplacé par nullptr, de manière à éviter toute rupture de code existant due à un usage généralisé et souvent inapproprié de NULL. Mais à l'avenir, nullptr doit être utilisé partout où NULL était utilisé, et NULL doit être traité comme une fonctionnalité censée prendre en charge la compatibilité descendante.

Pour finir, decltype permet au compilateur de déduire le type de retour d'une fonction sur la base d'une expression arbitraire et rend le transfert parfait plus générique. Dans les versions passées, pour deux types arbitraires T1 et T2, il n'existait aucun moyen de déduire le type d'une expression utilisant ces deux types. La fonctionnalité decltype vous permet de déclarer par exemple une expression qui possède des arguments template, de telle sorte que sum<T1, T2>() ait le type T1+T2.

Améliorations apportées à la bibliothèque standard

Une partie significative de la bibliothèque C++ standard a été réécrite de façon à tirer parti des nouvelles fonctionnalités de langage C++0x et à améliorer les performances. De nombreux nouveaux algorithmes ont également été introduits.

La bibliothèque standard tire pleinement profit des références rvalue en vue d'accroître les performances. Des types tels que vecteur et liste possèdent désormais leurs propres constructeurs de déplacement et opérateurs d'assignation de déplacement. Les réallocations de vecteurs tirent parti de la sémantique de déplacement en sélectionnant des constructeurs de déplacement ; par conséquent, si vos types ont des constructeurs de déplacement et des opérateurs d'assignation de déplacement, la bibliothèque les sélectionne automatiquement.

Vous pouvez désormais créer un pointeur partagé vers un objet en même temps que vous construisez l'objet avec l'aide du nouveau modèle de fonction C++0x make_shared<T> :

auto sp = 
  make_shared<map<string,vector>>
  (args);

Dans Visual Studio 2008, il aurait fallu écrire ce qui suit pour obtenir la même fonctionnalité :

shared_ptr<map<string,vector>> 
  sp(new map<string,vector>(args));

L'utilisation de make_shared<T> est plus commode (vous devrez taper le nom de type moins de fois), plus robuste (elle permet d'éviter la classique fuite shared_ptr leak sans nom car le pointeur et l'objet sont créés simultanément) et plus efficace (elle effectue une seule allocation de mémoire dynamique au lieu de deux). 

La bibliothèque contient désormais un nouveau type pointeur intelligent et plus sécurisé, unique_ptr (qui a été activé par des références rvalue). En conséquence, auto_ptr est déconseillé ; unique_ptr évite les pièges présentés par auto_ptr car il est déplaçable mais non copiable. Il vous permet d'implémenter une sémantique de propriété stricte sans affecter la sécurité. Il fonctionne également très bien avec les conteneurs Visual C++ 2010 compatibles avec les références rvalue.

Les conteneurs possèdent maintenant de nouvelles fonctions membres (cbegin et cend) qui permettent d'utiliser un const_iterator pour l'inspection quel que soit le type de conteneur :

vector<int> v;
 
for (auto i = v.cbegin(); i != v.cend(); ++i) {
  // i is vector<int>::const_iterator
}

Visual Studio 2010 ajoute à la bibliothèque standard la plupart des algorithmes proposés dans divers articles relatifs à C++0x. Un sous-ensemble de la bibliothèque de conversions Dinkumware est désormais disponible dans la bibliothèque standard. Il est donc facile d'effecteur des conversions telles que UTF-8 vers UTF-16. La bibliothèque standard autorise la propagation d'exception par le biais d'exception_ptr. De nombreuses mises à jour ont été apportées à l'en-tête <random>. Il existe une liste à liaison unique nommée forward_list dans cette version. La bibliothèque possède un en-tête <system_error> afin d'améliorer les diagnostics. En outre, une grande partie des fonctionnalités TR1 qui existaient dans l'espace de noms std::tr1 dans la version précédente (telles que shared_ptr et regex) font désormais partie de la bibliothèque standard sous l'espace de noms std.

Améliorations en matière de programmation d'accès concurrentiel

Visual Studio 2010 introduit la Plateforme d'informatique parallèle, qui vous aide à écrire du code parallèle hautement performant, rapidement et tout en évitant les subtiles bogues d'accès concurrentiel. Ceci vous permet d'éliminer certains des problèmes classiques liés à l'accès concurrentiel.

La Plateforme d'informatique parallèle comporte quatre parties principales : Le Runtime d'accès concurrentiel (ConcRT), la Bibliothèque de modèles parallèles (PPL), la Bibliothèque d'agents asynchrones et le débogage et profilage parallèles.

ConcRT est la couche logicielle la plus basse qui communique avec le système d'exploitation et joue le rôle d'arbitre pour les différents composants qui luttent pour l'accès concurrentiel aux ressources. S'agissant d'un processus en mode utilisateur, il peut récupérer des ressources lorsque ses mécanismes de blocage coopératif sont utilisés. ConcRT est conscient de la localité et évite de faire basculer les tâches entre différents processeurs. Il emploie également la planification du mode utilisateur (UMS, User Mode Scheduling) de Windows 7 afin d'améliorer les performances même lorsque le mécanisme de blocage coopératif n'est pas utilisé.

La bibliothèque PPL fournit les modèles pour l'écriture de code parallèle. Si un calcul peut être décomposé en sous-calculs qui peuvent être représentés par des fonctions ou des objets de fonctions, chacun de ces sous-calculs peut être représenté par une tâche. Le concept de tâche se rapproche beaucoup plus du domaine du problème, contrairement aux threads qui vous éloignent du domaine du problème en vous faisant penser au matériel, au système d'exploitation, aux sections critiques, et ainsi de suite. Une tâche peut s'exécuter en même temps que d'autres tâches indépendamment de ce que font ces autres tâches. Par exemple, le tri de deux différentes moitiés d'un tableau peut être effectué par deux tâches différentes simultanément.

La bibliothèque PPL inclut des classes parallèles (task_handle, task_group et structured_task_group), des algorithmes parallèles (parallel_invoke, parallel_for et parallel_for_each), des conteneurs parallèles (combinable, concurrent_queue et concurrent_vector), ainsi que des primitives de synchronisation compatibles avec ConcRT (critical_section, event et reader_writer_lock), tous traitant les tâches comme un concept de première classe. Tous les composants de la bibliothèque PPL vivent dans l'espace de noms d'accès concurrentiel.

Les groupes de tâches vous permettent d'exécuter un ensemble de tâches et d'attendre qu'elles soient toutes terminées. Dans notre exemple de tri, les tâches qui gèrent les deux moitiés du tableau peuvent composer un groupe de tâches. Vous êtes assuré que ces deux tâches soient terminées à la fin de l'appel de fonction membre d'attente, comme illustré dans l'exemple de code de tri rapide récursif suivant, rédigé à l'aide de lambdas et de tâches parallèles :

void quicksort(vector<int>::iterator first,
vector<int>::iterator last) {
  if (last - first < 2) { return; }
  int pivot = *first;
  auto mid1 = partition(first, last, [=](int elem) { 
    return elem < pivot; });
  auto mid2 = partition( mid1, last, [=](int elem) { 
    return elem == pivot; });
  task_group g;
  g.run([=] { quicksort(first, mid1); });
  g.run([=] { quicksort(mid2, last); });
  g.wait();
}

Ce code peut être amélioré encore davantage à l'aide d'un groupe de tâches structuré activé par l'algorithme parallel_invoke. Il prend de deux à 10 objets de fonctions et les exécute tous en parallèle en utilisant autant de cœurs que ConcRT en fournit, et il attend que tous ces objets soient terminés :

parallel_invoke(
  [=] { quicksort(first, mid1); },
  [=] { quicksort(mid2, last); } );

parallel_invoke(
  [=] { quicksort(first, mid1); },
  [=] { quicksort(mid2, last); } );

Il pourrait y avoir plusieurs sous-tâches créées par chacune de ces tâches. Le mappage entre les tâches et les threads d'exécution (de même que l'utilisation optimale de tous les cœurs) est géré par ConcRT. Par conséquent, le fait de décomposer votre calcul en le plus de tâches possible aide à tirer parti de tous les cœurs disponibles.

parallel_for est un autre algorithme parallèle utile ; il permet d'itérer des index de manière simultanée :

parallel_for(first, last, functor);
parallel_for(first, last, step, functor);

Ce code appelle simultanément des objets de fonction avec chaque index, en commençant par le premier et en finissant par le dernier.

La Bibliothèque d'agents asynchrones procure un modèle de programmation basé sur les flux de données dans lequel les calculs dépendent de la disponibilité des données requises. La bibliothèque est fondée sur les concepts d'agents, de blocs de message et de fonctions de passage de message. Un agent est un composant d'une application qui effectue certains calculs et communique de façon asynchrone avec d'autres agents en vue de résoudre un problème de calcul plus important. Cette communication entre les agents s'effectue par le biais de fonctions de passage de message et de blocs de message.

Les agents ont un cycle de vie observable qui passe par différentes phases. Ils ne sont pas destinés à être utilisés pour le parallélisme affiné obtenu à l'aide de tâches PPL. Les agents reposent sur les composants de planification et de gestion des ressources de ConcRT et vous aident à éviter les problèmes dus à l'utilisation de la mémoire partagée dans les applications simultanées.

Il n'est pas nécessaire d'établir de liaison à des composants supplémentaires, ni d'en redistribuer, pour tirer parti de ces modèles. ConcRT, la Bibliothèque de modèles parallèles (PPL) et la Bibliothèque d'agents asynchrones ont été implémentés dans msvcr100.dll, msvcp100.dll et libcmt.lib/libcpmt.lib en plus de la bibliothèque standard. La Bibliothèque de modèles parallèles (PPL) et la Bibliothèque d'agents asynchrones sont principalement des implémentations en-tête-uniquement.

Le débogueur Visual Studio est désormais compatible avec ConcRT et simplifie le débogage des problèmes d'accès concurrentiel, contrairement à Visual Studio 2008 qui ne prenait pas en charge les concepts parallèles de haut niveau. Visual Studio 2010 possède un profileur d'accès concurrentiel qui vous permet de visualiser le comportement des applications parallèles. Le débogueur possède de nouvelles fenêtres qui affichent l'état de toutes les tâches dans une application et leurs piles d'appels. La figure 1 montre les fenêtres Tâches parallèles et Piles parallèles.

Figure 1 Fenêtres de débogage Piles parallèles et Tâches parallèles
Figure 1 Fenêtres de débogage Piles parallèles et Tâches parallèles

IntelliSense et productivité au moment du design

Une toute nouvelle infrastructure de navigation et IntelliSense est incluse dans Visual Studio 2010. Outre les améliorations en matière d'évolutivité et de réactivité des projets avec de grandes bases de code, l'infrastructure offre également de nouvelles fonctionnalités de productivité au moment du design.

Les fonctionnalités IntelliSense telles que les rapports d'erreur en direct et les info-bulles Info express sont basées sur une nouvelle application frontale de compilateur, qui analyse la totalité de l'unité de traduction de manière à fournir des informations riches et précises concernant la sémantique du code, même pendant la modification des fichiers de code.

Toutes les fonctionnalités de navigation du code, telles que l'affichage de classes et la hiérarchie de classes, utilisent désormais les informations de code source stockées dans une base de données SQL qui autorise l'indexation et possède une empreinte mémoire fixe. Contrairement aux versions précédentes, l'environnement de développement intégré (IDE) de Visual Studio 2010 est toujours réactif et il n'est plus nécessaire de patienter pendant que les unités de compilation sont réanalysées en réponse à des modifications dans un fichier d'en-tête.

La fonctionnalité de rapport d'erreurs en direct d'IntelliSense (les tildes rouges bien connus) affiche les erreurs de sémantique et de syntaxe durant la navigation et la modification du code. Lorsque vous placez le pointeur de la souris sur l'erreur, le message d'erreur s'affiche (voir la figure 2). La fenêtre de liste d'erreurs montre également l'erreur dans le fichier actuellement visualisé, ainsi que les erreurs IntelliSense présentes à d'autres emplacements dans l'unité de compilation. Toutes ces informations sont à votre disposition sans qu'il soit nécessaire de générer une build.

Figure 2 Affichage des erreurs à l'aide de la fonctionnalité de rapport d'erreurs en direct d'IntelliSense
Figure 2 Affichage des erreurs à l'aide de la fonctionnalité de rapport d'erreurs en direct d'IntelliSense

De plus, une liste de fichiers Include pertinents est affichée dans un menu déroulant lorsque vous tapez #include, et cette liste est affinée à mesure que vous tapez.

La nouvelle fonctionnalité Naviguer vers (Edition | Naviguer vers ou Ctrl+virgule) vous aidera à accroître votre productivité avec une recherche de symbole ou de fichier. Cette fonctionnalité procure des résultats de recherche en temps réel sur la base des sous-chaînes que vous tapez, en établissant une correspondance entre vos chaînes d'entrée et les symboles ou fichiers dans n'importe quel projet (voir la figure 3). Cette fonctionnalité s'applique également aux fichiers C# et Visual Basic, et elle est extensible.

Figure 3 Utilisation de la fonctionnalité Naviguer vers
Figure 3 Utilisation de la fonctionnalité Naviguer vers

La hiérarchie d'appels (affichée à l'aide de Ctrl+K, Ctrl+T ou depuis le menu contextuel) vous permet de naviguer vers toutes les fonctions appelées à partir d'une fonction spécifique, et à partir de toutes les fonctions qui effectuent des appels vers une fonction spécifique. Il s'agit d'une version améliorée de la fonctionnalité Explorateur d'appels présente dans les versions précédentes de Visual Studio. La fenêtre Hiérarchie d'appels est beaucoup mieux organisée et fournit des arborescences d'appels depuis et d'appels vers pour toute fonction qui apparaît dans la même fenêtre.

Notez que bien que toutes les fonctionnalités de navigation de code soient disponibles pour le code pur C++ et C++/CLI, les fonctionnalités liées à IntelliSense telles que les rapports d'erreurs en direct et Info express ne seront pas disponibles pour C++/CLI dans la version finale de Visual Studio 2010. 

Les autres fonctionnalités de l'éditeur ont également été améliorées dans cette version. Par exemple, la fonctionnalité Rechercher toutes les références qui permet de rechercher des références à des éléments de code (classes, membres de classes, fonctions et ainsi de suite) à l'intérieur d'une solution entière est désormais plus flexible. Les résultats de recherche peuvent être affinés à l'aide de l'option Résoudre les résultats disponible dans le menu contextuel.

Le code inactif conserve désormais les informations sémantiques en maintenant la colorisation (au lieu de devenir grisé). La figure 4 illustre la façon dont le code inactif est estompé mais continue de présenter différentes couleurs afin de refléter les informations sémantiques.

Figure 4 Les blocs de code inactif conservent la colorisation
Figure 4 Les blocs de code inactif conservent la colorisation

Outre les fonctionnalités décrites ci-dessus, l'expérience globale de l'éditeur a été améliorée dans Visual Studio 2010. Le nouvel environnement de développement intégré (IDE) WPF (Windows Presentation Foundation) a été remanié afin de supprimer les éléments superflus et d'améliorer la lisibilité. Les fenêtres de documents telles que l'éditeur de code et le mode Design peuvent désormais flotter en dehors de la fenêtre d'environnement IDE principale et être affichées sur plusieurs moniteurs. Il est plus facile d'agrandir et de réduire la fenêtre de l'éditeur de code à l'aide de la touche Ctrl et de la roulette de souris. L'environnement IDE offre également une prise en charge améliorée de l'extensibilité.

Systèmes de génération et de projet

Visual Studio 2010 offre également des améliorations substantielles en ce qui concerne le système de génération et le système de projet pour les projets C++.

Le changement le plus marquant est le fait que MSBuild est désormais utilisé pour générer les projets C++. MSBuild est un moteur d'orchestration de génération basé sur le langage XML et extensible qui était déjà utilisé pour les projets C# et Visual Basic dans les versions précédentes de Visual Studio. MSBuild est désormais le système de génération Microsoft commun à tous les langages. Il peut être utilisé à la fois dans le laboratoire de génération et sur les différents ordinateurs de développement.

Les processus de génération C++ sont maintenant définis en termes de fichiers de tâches et cibles MSBuild, et procurent un plus haut degré de personnalisation, contrôle et transparence.

Le type de projet C++ possède une nouvelle extension : .vcxproj. Visual Studio effectue automatiquement la mise à niveau des anciens fichiers et solutions .vcproj vers le nouveau format. Il existe également un outil en ligne de commande, vcupgrade.exe, qui permet de mettre à niveau un seul projet depuis la ligne de commande.

Par le passé, vous ne pouviez utiliser que l'ensemble d'outils (compilateur, bibliothèques, et ainsi de suite) fourni avec votre version actuelle de Visual Studio. Il vous fallait attendre d'être prêt à migrer vers le nouvel ensemble d'outils avant de pouvoir commencer à utiliser le nouvel environnement IDE. Visual Studio 2010 résout ce problème en vous offrant la possibilité de cibler plusieurs versions d'ensemble d'outils pour la génération. Vous pouvez par exemple cibler le compilateur et les bibliothèques Visual C++ 9.0 tout en travaillant dans Visual Studio 2010. La figure 5 montre les paramètres de multi-ciblage natifs dans la page de propriétés.

5 Ciblage de plusieurs ensembles d'outils de plateformes
Figure 5 Ciblage de plusieurs ensembles d'outils de plateformes

L'utilisation de MSBuild rend le système de génération beaucoup plus extensible. Lorsque le système de génération par défaut ne suffit pas à vos besoins, vous pouvez l'étendre en ajoutant votre propre outil ou toute autre étape de génération. MSBuild utilise des tâches en guise d'unités réutilisables de code exécutable pour effectuer les opérations de génération. Vous pouvez créer vos propres tâches et étendre le système de génération en les définissant dans un fichier XML. MSBuild génère les tâches à la volée à partir de ces fichiers XML.

Les plateformes et ensembles d'outils existants peuvent être étendus en ajoutant des fichiers .props et .targets pour des étapes supplémentaires dans les dossiers ImportBefore et ImportAfter. Ceci est particulièrement utile aux fournisseurs de bibliothèques et d'outils qui souhaitent étendre les systèmes de génération existants. Vous pouvez également définir votre propre ensemble d'outils de plateforme. Par ailleurs, MSBuild fournit de meilleures informations de diagnostic afin de faciliter le débogage des problèmes de génération, ce qui accroît également la fiabilité des builds incrémentielles. Il est possible de créer des systèmes de génération plus étroitement liés au contrôle de code source et au laboratoire de génération, et qui dépendent moins de la configuration de l'ordinateur du développeur.

Le système de projet qui repose sur le système de génération tire également parti de la flexibilité et de l'extensibilité fournies par MSBuild. Il comprend les processus MSBuild et autorise Visual Studio à afficher de manière transparente les informations mises à disposition par MSBuild.

Les personnalisations sont visibles et peuvent être configurées par le biais des pages de propriétés. Vous pouvez configurer votre système de projet de façon à utiliser votre propre plateforme (comme les plateformes x86 ou x64 existantes) ou votre propre débogueur. Les pages de propriétés vous permettent d'écrire et d'intégrer des composants qui effectuent la mise à jour dynamique de la valeur des propriétés qui dépendent du contexte. Le système de projet Visual Studio 2010 vous permet même d'écrire votre propre interface utilisateur personnalisée afin d'écrire et de lire des propriétés, au lieu d'utiliser les pages de propriétés.

Accélération de la compilation et amélioration des performances

Outre les améliorations liées à l'expérience au moment du design décrites jusqu'à maintenant, Visual Studio 2010 accroît également la vitesse de compilation, la qualité et les performances des applications générées avec le compilateur Visual C++ grâce aux nombreuses améliorations de génération de code apportées à l'application principale de compilateur.

Les performances de certaines applications dépendent du jeu de travail. La taille du code de l'architecture x64 a été réduite à une plage comprise entre 3 et 10 pour cent suite à plusieurs optimisations dans cette version, ce qui entraîne une amélioration des performances de ces applications.

La génération de code SIMD (Single Instruction Multiple Data), qui est importante pour les développeurs de jeux, audio, vidéo et graphismes, a été optimisée afin d'améliorer les performances et la qualité du code. Parmi ces améliorations, citons la rupture des fausses dépendances, la vectorisation des initialisations de vecteurs constants et la meilleure allocation des registres XMM afin de supprimer les chargements, stockages et déplacements redondants. De plus, la famille intrinsèque __mm_set_**, __mm_setr_** et __mm_set1_** a été optimisée.

Pour de meilleures performances, les applications doivent être générées à l'aide de la LTCG (Link Time Code Generation) et de la PGO (Profile Guided Optimization).

La vitesse de compilation sur les plateformes x64 a été augmentée grâce à des optimisations dans la génération de code x64. La compilation avec la LTCG, recommandée pour une meilleure optimisation, est généralement plus longue que la compilation non-LTCG, en particulier avec les grandes applications. Dans Visual Studio 2010, la compilation LTCG a été améliorée d'un facteur allant jusqu'à 30 pour cent. Un thread dédié à l'écriture des fichiers PDB ayant été introduit dans cette version, vous constaterez une réduction des délais de liaison lors de l'utilisation du commutateur /DEBUG.

Les séries d'instrumentation PGO ont été accélérées grâce à l'ajout de la prise en charge des versions sans verrou des binaires instrumentés. Il existe également une nouvelle option POGO, PogoSafeMode, qui vous permet de spécifier s'il faut utiliser le mode sans échec ou le mode rapide lors de l'optimisation d'une application. Le mode rapide est le comportement par défaut. Le mode sans échec est thread-safe, mais plus lent que le mode rapide.

La qualité du code généré par le compilateur a été améliorée. Les extensions AVX (Advanced Vector Extensions) sont désormais prises en charge complètement ; ces extensions sont très importantes pour les applications qui utilisent beaucoup les virgules flottantes dans les processeurs AMD et Intel via les options intrinsèques et /arch:AVX. Le calcul en virgule flottante est plus précis avec l'option /fp:fast.

Génération d'applications pour Windows 7

Windows 7 a introduit plusieurs nouvelles technologies et fonctionnalités passionnantes et ajouté de nouvelles API. Visual Studio 2010 procure un accès à toutes ces nouvelles API Windows. Les composants du Kit de développement logiciel SDK Windows nécessaires à l'écriture de code pour les API Windows natives sont fournis avec Visual Studio 2010. Vous pouvez tirer parti d'innovations telles que Direct3D 11, DirectWrite, Direct2D et les API de services Web Windows en utilisant les bibliothèques et en-têtes SDK disponibles dans Visual Studio 2010.

Outre la mise à disposition pour les développeurs de toutes les nouvelles API Windows, cette version de Visual Studio facilite également l'écriture d'applications pour Windows grâce à un MFC amélioré. Vous pouvez accéder aux fonctionnalités de Windows 7 par le biais des bibliothèques MFC sans avoir à écrire directement d'API natives. Vos applications MFC existantes s'adapteront à la puissance de Windows 7 simplement suite à une recompilation. Et vos nouvelles applications pourront tirer pleinement profit des nouvelles fonctionnalités.

MFC offre une meilleure intégration au shell Windows. L'intégration de votre application à l'Explorateur Windows est améliorée grâce à l'utilisation des gestionnaires de fichiers pour l'aperçu, les miniatures et la recherche qui ont été ajoutés dans cette version. Ces fonctionnalités sont proposées sous la forme d'options dans l'Assistant Application MFC, comme illustré à la figure 6. MFC génère automatiquement le projet DLL ATL qui implémente ces gestionnaires.

Figure 6 Assistant Application MFC avec options de gestionnaire de fichiers
Figure 6 Assistant Application MFC avec options de gestionnaire de fichiers

L'une des modifications les plus notables apportées à l'interface utilisateur dans Windows 7 est la nouvelle barre des tâches. MFC vous permet de rapidement tirer parti de fonctionnalités telles que les listes de raccourcis, les miniatures à onglets, les aperçus de miniatures, les barres de progression, la superposition d'image sur une icône, et ainsi de suite. La figure 7 montre les aperçus de miniatures et les miniatures à onglets pour une application MFC MDI à onglets.

Figure 7 Aperçus de miniatures et miniatures à onglets dans une application MFC
Figure 7 Aperçus de miniatures et miniatures à onglets dans une application MFC

L'interface utilisateur du Ruban possède désormais un Ruban de style Windows 7 et votre application peut changer d'interface utilisateur à la volée à tout moment lors du développement (choix entre plusieurs Rubans de style Office et le Ruban Windows 7) à l'aide du menu déroulant de style, comme illustré à la figure 8.

Figure 8 Menu déroulant de style de Ruban dans une application MFC
Figure 8 Menu déroulant de style de Ruban dans une application MFC

MFC permet de rendre vos applications compatibles avec le multipoint et appelle les messages appropriés que vous devez gérer lorsque les différents événements tactiles se produisent. Le simple enregistrement des événements tactiles et gestuels achemine ces événements pour votre application. MFC rend également vos applications compatibles avec la haute résolution par défaut, de sorte qu'elles s'adaptent aux écrans haute résolution et ne présentent pas un aspect pixélisé ou flou. MFC effectue en interne une mise à l'échelle et une modification des polices et autres éléments afin de garantir la netteté de votre interface utilisateur sur les affichages haute résolution.

Outre les nouvelles fonctionnalités de Windows 7, certaines autres fonctionnalités de Windows qui existent depuis Windows Vista mais étaient absentes des versions précédentes de MFC sont désormais disponibles. Par exemple, le Gestionnaire de redémarrage est une fonctionnalité utile introduite dans Windows Vista qui permet à une application d'effectuer un enregistrement d'application avant de se terminer. L'application peut recourir à cette fonctionnalité, puis restaurer son état au redémarrage. Vous pouvez désormais tirer pleinement parti du Gestionnaire de redémarrage dans votre application MFC afin de gérer les blocages et redémarrages de manière plus élégante. Il vous suffit d'ajouter une ligne de code pour activer le redémarrage et la récupération dans votre application existante :

CMyApp::CMyApp() {
  m_dwRestartManagerSupportFlags = 
    AFX_RESTART_MANAGER_SUPPORT_RESTART;
// other lines of code ...
}

Les nouvelles applications MFC bénéficient automatiquement de cette fonctionnalité grâce à l'Assistant Application MFC. Le mécanisme d'enregistrement automatique est accessible aux applications qui enregistrent des documents, et l'intervalle d'enregistrement automatique peut être défini par l'utilisateur. Les applications peuvent choisir la prise en charge du redémarrage uniquement ou du démarrage avec récupération d'application (applicable aux applications de type Doc/View) dans l'Assistant Application MFC.

Autre ajout, la boîte de dialogue Tâche Windows est un type amélioré de boîte de message (voir la figure 9). MFC possède désormais un wrapper pour la boîte de dialogue Tâche que vous pouvez utiliser dans vos applications.

Figure 9 Boîte de dialogue Tâche
Figure 9 Boîte de dialogue Tâche

L'Assistant Classe MFC est de retour

Non seulement de nouvelles fonctionnalités ont été ajoutées à la bibliothèque MFC, mais cette version simplifie également l'utilisation de MFC dans l'environnement IDE de Visual studio. L'une des fonctionnalités les plus souvent demandées, l'Assistant Classe MFC (illustré à la figure 10), a été rétablie et améliorée. Vous pouvez désormais ajouter des classes, des gestionnaires d'événements et autres éléments à votre application à l'aide de l'Assistant Classe MFC.

Figure 10 Assistant Classe MFC
Figure 10 Assistant Classe MFC

Le Concepteur de ruban est une autre nouveauté. Il vous permet de concevoir sous forme graphique votre interface utilisateur de Ruban (au lieu de la définir dans le code, comme dans Visual Studio 2008) et de la stocker en tant que ressource XML. Ce concepteur est bien évidemment utile pour la création de nouvelles applications, mais les applications existantes peuvent également en profiter pour mettre à jour leur interface utilisateur. La définition XML peut être créée simplement en ajoutant temporairement une ligne de code à la définition de code existante de l'interface utilisateur du Ruban :

m_wndRibbonBar.SaveToXMLFile(L"YourRibbon.mfcribbon-ms");

Le fichier XML résultant peut ensuite être consommé comme un fichier de ressources et des modifications supplémentaires peuvent être apportées à l'aide du Concepteur de ruban.

Synthèse

Visual Studio 2010 constitue une version majeure dans l'évolution de Visual C++ et simplifie la vie des développeurs de nombreuses façons. Je n'ai fait ici qu'effleurer la surface des différentes améliorations en rapport avec C++. Pour une discussion approfondie des différentes fonctionnalités, reportez-vous à la documentation MSDN et au blog de l'équipe Visual C++ à l'adresse blogs.msdn.com/vcblog, qui a également servi de base à certaines sections de cet article.   

Sumit Kumar est responsable de programme au sein de l'équipe IDE Visual C++. Il est titulaire d'une Maîtrise en informatique délivrée par l'Université du Texas (Dallas).

Je remercie les experts techniques suivants : Stephan T. Lavavej, Marian Luparu et Tarek Madkour