Tutoriel : Importer la bibliothèque standard C++ en utilisant des modules à partir de la ligne de commande
Découvrez comment importer la bibliothèque standard C++ en utilisant des modules de bibliothèque C++. Cela permet une compilation plus rapide et est plus robuste que l’utilisation de fichiers d’en-tête, d’unités d’en-tête ou d’en-têtes précompilés (PCH).
Dans ce tutoriel, découvrez :
- Comment importer la bibliothèque standard en tant que module à partir de la ligne de commande.
- Les avantages en matière de performances et d’utilisabilité des modules.
- Les deux modules
std
etstd.compat
de bibliothèque standard, et ce qui les différencie.
Prérequis
Ce tutoriel nécessite Visual Studio 2022 17.5 ou ultérieur.
Présentation des modules de bibliothèque standard
Les fichiers d’en-tête souffrent d’une sémantique qui peut changer en fonction des définitions des macros, de l’ordre dans lequel vous les incluez et de la lenteur de leur compilation. Les modules résolvent ces problèmes.
Il est désormais possible d’importer la bibliothèque standard sous la forme d’un module et non plus d’un enchevêtrement de fichiers d’en-tête. Cette méthode est beaucoup plus rapide et plus robuste que l’inclusion de fichiers d’en-tête, d’unités d’en-tête ou d’en-têtes précompilés (PCH).
La bibliothèque standard C++23 introduit les deux modules nommés std
et std.compat
:
std
exporte les déclarations et les noms définis dans l’espace de nomsstd
de la bibliothèque standard C++, commestd::vector
. Il exporte également le contenu des en-têtes de wrapper C, comme<cstdio>
et<cstdlib>
, qui fournissent des fonctions commestd::printf()
. Les fonctions C définies dans l’espace de noms global, comme::printf()
, ne sont pas exportées. Cela permet d’améliorer les situations où l’inclusion d’un en-tête de wrapper C comme<cstdio>
inclut également des fichiers d’en-tête C commestdio.h
, qui intègrent les versions de l’espace de noms global C. Ce n’est pas un problème si vous importezstd
.std.compat
exporte tout dansstd
et ajoute les espaces de noms globaux du runtime C, comme::printf
,::fopen
,::size_t
,::strlen
, etc. Le modulestd.compat
facilite le travail avec les codebases qui référencent de nombreuses fonctions/types du runtime C dans l’espace de noms global.
Le compilateur importe l’ensemble de la bibliothèque standard quand vous utilisez import std;
ou import std.compat;
, et il le fait plus rapidement que dans le cas de l’insertion d’un seul fichier d’en-tête. Il est plus rapide d’intégrer toute la bibliothèque standard avec import std;
(ou import std.compat
) qu’avec par exemple #include <vector>
.
Comme les modules nommés n’exposent pas de macros, des macros comme assert
, errno
, offsetof
, va_arg
et d’autres ne sont pas disponibles quand vous importez std
ou std.compat
. Consultez Considérations relatives aux modules nommés de la bibliothèque standard pour obtenir des solutions de contournement.
À propos des modules C++
Les fichiers d’en-tête ont été utilisés pour partager les déclarations et les définitions entre les fichiers sources en C++. Avant les modules de bibliothèque standard, vous deviez inclure la partie de la bibliothèque standard dont vous aviez besoin, avec une directive comme #include <vector>
. Les fichiers d’en-tête sont fragiles et difficiles à composer, car leur sémantique peut changer selon l’ordre dans lequel vous les incluez ou si certaines macros sont définies. Ils ralentissent également la compilation, car ils sont retraités par chaque fichier source qui les inclut.
C++20 introduit une alternative moderne appelée modules. En C++23, nous avons pu capitaliser sur la prise en charge des modules pour introduire les modules nommés destinés à représenter la bibliothèque standard.
Comme les fichiers d’en-tête, les modules vous permettent de partager des déclarations et des définitions entre des fichiers sources. Mais contrairement aux fichiers d’en-tête, les modules ne sont pas fragiles et sont plus faciles à composer, car leur sémantique ne change pas en raison des définitions de macros ou de l’ordre dans lequel vous les importez. Le compilateur peut traiter les modules beaucoup plus rapidement que les fichiers #include
et il utilise moins de mémoire au moment de la compilation. Les modules nommés n’exposent pas de définitions de macros ni de détails d’implémentation privés.
Pour plus d’informations sur les modules, consultez Vue d’ensemble des modules en C++. Cet article traite également de la consommation de la bibliothèque standard C++ sous forme de modules, mais il utilise pour cela une méthode plus ancienne et expérimentale.
Cet article montre la nouvelle et meilleure façon de consommer la bibliothèque standard. Pour plus d’informations sur les autres façons d’utiliser la bibliothèque standard, consultez Comparer les unités d’en-tête, les modules et les en-têtes précompilés.
Importer la bibliothèque standard avec std
Les exemples suivants montrent comment consommer la bibliothèque standard en tant que module en utilisant le compilateur en ligne de commande. Pour plus d’informations sur la façon de le faire dans l’IDE Visual Studio, consultez Créer des modules de bibliothèque standard C++23 ISO.
L’instruction import std;
ou import std.compat;
importe la bibliothèque standard dans votre application. Cependant, vous d’abord devez compiler les modules nommés de la bibliothèque standard sous une forme binaire. Les étapes suivantes montrent comment procéder.
Exemple : Comment créer et importer std
Ouvrez une invite de commandes des outils natifs x86 pour VS : dans le menu Démarrer de Windows, tapez x86 natif : l’invite doit apparaître dans la liste des applications. Vérifiez que l’invite concerne Visual Studio 2022 version 17.5 ou ultérieure. Vous recevrez des erreurs si vous utilisez la version incorrecte de l’invite. Les exemples utilisés dans ce didacticiel sont destinés au shell CMD.
Créez un répertoire, comme
%USERPROFILE%\source\repos\STLModules
, et faites-en le répertoire actif. Si vous choisissez un répertoire auquel vous n’avez pas accès en écriture, vous recevrez des erreurs lors de la compilation.Compilez le module nommé
std
avec la commande suivante :cl /std:c++latest /EHsc /nologo /W4 /c "%VCToolsInstallDir%\modules\std.ixx"
Si vous recevez des erreurs, vérifiez que vous utilisez la version correcte de l’invite de commandes.
Compilez le module nommé
std
en utilisant les mêmes paramètres de compilateur que ceux que vous prévoyez d’utiliser avec le code qui importe le module généré. Si vous avez une solution multiprojet, vous pouvez compiler une seule fois le module nommé de bibliothèque standard, puis y faire référence depuis tous vos projets en utilisant l’option/reference
du compilateur.Avec la commande de compilateur précédente, le compilateur produit en sortie deux fichiers :
std.ifc
est la représentation binaire compilée de l’interface du module nommé que le compilateur consulte pour traiter l’instructionimport std;
. C’est un artefact uniquement au moment de la compilation. Il n’est pas fourni avec votre application.std.obj
contient l’implémentation du module nommé. Ajoutezstd.obj
à la ligne de commande quand vous compilez l’exemple d’application pour lier statiquement les fonctionnalités de la bibliothèque standard que vous utilisez dans votre application.
Les commutateurs de ligne de commande clés dans cet exemple sont les suivants :
Commutateur Signification /std:c++:latest
Utilisez la dernière version de la norme du langage C++ et de la bibliothèque. Bien que la prise en charge des modules soit disponible sous /std:c++20
, vous avez besoin de la dernière version de la bibliothèque standard pour obtenir la prise en charge des modules nommés de la bibliothèque standard./EHsc
Utilisez la gestion des exceptions C++, sauf pour les fonctions marquées extern "C"
./W4
L’utilisation de /W4 est généralement recommandée, en particulier pour les nouveaux projets, car elle active tous les avertissements de niveau 1, 2 et 3, et la plupart des avertissements de niveau 4 (information), ce qui peut vous aider à détecter plus tôt les problèmes potentiels. Elle fournit essentiellement des avertissements de type lint, qui peuvent contribuer à réduire au minimum les défauts du code difficiles à trouver. /c
Compilez sans liaison, car à ce stade, nous créons simplement l’interface du module nommé binaire. Vous pouvez contrôler le nom du fichier objet et le nom du fichier d’interface du module nommé avec les commutateurs suivants :
/Fo
définit le nom du fichier objet. Par exemple :/Fo:"somethingelse"
. Par défaut, le compilateur utilise pour le fichier objet le même nom que le fichier source du module (.ixx
) que vous compilez. Dans l’exemple, le nom du fichier objet eststd.obj
par défaut, car nous compilons le fichier de modulestd.ixx
./ifcOutput
définit le nom du fichier d’interface du module nommé (.ifc
). Par exemple :/ifcOutput "somethingelse.ifc"
. Par défaut, le compilateur utilise pour le fichier d’interface du module (.ifc
) le même nom que celui du fichier source du module (.ixx
) que vous compilez. Dans l’exemple, le fichierifc
généré eststd.ifc
par défaut, car nous compilons le fichier de modulestd.ixx
.
Importez la bibliothèque
std
que vous avez générée en créant d’abord un fichier nomméimportExample.cpp
avec le contenu suivant :// requires /std:c++latest import std; int main() { std::cout << "Import the STL library for best performance\n"; std::vector<int> v{5, 5, 5}; for (const auto& e : v) { std::cout << e; } }
Dans le code précédent,
import std;
remplace#include <vector>
et#include <iostream>
. L’instructionimport std;
rend toutes les bibliothèques standard disponibles avec une seule instruction. L’importation de l’ensemble de la bibliothèque standard est souvent beaucoup plus rapide que le traitement d’un seul fichier d’en-tête de bibliothèque standard, comme#include <vector>
.Compilez l’exemple en utilisant la commande suivante dans le même répertoire que celui de l’étape précédente :
cl /c /std:c++latest /EHsc /nologo /W4 /reference "std=std.ifc" importExample.cpp link importExample.obj std.obj
Il n’est pas nécessaire de spécifier
/reference "std=std.ifc"
sur la ligne de commande dans cet exemple, car le compilateur recherche automatiquement le fichier.ifc
correspondant au nom du module spécifié par l’instructionimport
. Quand le compilateur rencontreimport std;
, il peut trouverstd.ifc
s’il se trouve dans le même répertoire que le code source. Si le fichier.ifc
se trouve dans un répertoire différent du code source, utilisez le commutateur/reference
du compilateur pour y faire référence.Dans cet exemple, la compilation du code source et la liaison de l’implémentation du module dans l’application sont des étapes distinctes. Ce n’est pas obligatoire. Vous pourriez utiliser
cl /std:c++latest /EHsc /nologo /W4 /reference "std=std.ifc" importExample.cpp std.obj
pour compiler et lier en une seule étape. Il peut cependant être pratique de générer et de lier séparément, car vous ne devez alors générer qu’une seule fois le module nommé de la bibliothèque standard, puis vous pouvez y faire référence depuis votre projet ou depuis plusieurs projets dans l’étape de liaison de votre build.Si vous générez un seul projet, vous pouvez combiner les étapes de génération du module nommé de bibliothèque standard
std
et l’étape de génération de votre application en ajoutant"%VCToolsInstallDir%\modules\std.ixx"
à la ligne de commande. Placez-le avant les fichiers.cpp
qui consomment le modulestd
.Par défaut, le nom de l’exécutable en sortie est tiré du premier fichier en entrée. Utilisez l’option de compilateur
/Fe
pour spécifier le nom du fichier exécutable souhaité. Ce tutoriel montre la compilation du module nomméstd
dans une étape distincte, car vous ne devez générer le module nommé de bibliothèque standard qu’une seule fois, ensuite de quoi vous pouvez y faire référence depuis votre projet ou depuis plusieurs projets. Il peut cependant être pratique de tout générer ensemble, comme le fait cette ligne de commande :cl /FeimportExample /std:c++latest /EHsc /nologo /W4 "%VCToolsInstallDir%\modules\std.ixx" importExample.cpp
Avec la ligne de commande précédente, le compilateur produit un exécutable nommé
importExample.exe
. Quand vous l’exécutez, il génère la sortie suivante :Import the STL library for best performance 555
Importer la bibliothèque standard et les fonctions C globales avec std.compat
La bibliothèque standard C++ inclut la bibliothèque standard C ISO. Le module std.compat
fournit toutes les fonctionnalités du module std
, comme std::vector
, std::cout
, std::printf
, std::scanf
, etc. Mais il fournit également les versions de l’espace de noms global de ces fonctions, comme ::printf
, ::scanf
, ::fopen
, ::size_t
, etc.
Le module nommé std.compat
est une couche de compatibilité fournie pour faciliter la migration du code existant qui fait référence à des fonctions du runtime C dans l’espace de noms global. Si vous voulez éviter d’ajouter des noms à l’espace de noms global, utilisez import std;
. Si vous devez faciliter la migration d’un codebase qui utilise de nombreuses fonctions non qualifiées du runtime C (espace de noms global), utilisez import std.compat;
. Ceci fournit les noms du runtime C de l’espace de noms global, ce qui vous permet de ne pas devoir qualifier tous les noms globaux avec std::
. Si vous n’avez pas de code existant qui utilise les fonctions du runtime C de l’espace de noms global, vous n’avez pas besoin d’utiliser import std.compat;
. Si vous appelez seulement quelques fonctions du runtime C dans votre code, il peut être préférable d’utiliser import std;
et de qualifier les quelques noms du runtime C de l’espace de noms global qui en ont besoin avec std::
. Par exemple : std::printf()
. Si vous voyez une erreur comme error C3861: 'printf': identifier not found
quand vous essayez de compiler votre code, envisagez d’utiliser import std.compat;
pour importer les fonctions du runtime C de l’espace de noms global.
Exemple : Comment générer et importer std.compat
Avant de pouvoir utiliser import std.compat;
, vous devez compiler le fichier d’interface du module trouvé sous forme de code source dans std.compat.ixx
. Visual Studio fournit le code source du module pour vous permettre de le compiler en utilisant les paramètres du compilateur qui correspondent à votre projet. Les étapes sont similaires à celles pour la génération du module nommé std
. Le module nommé std
est généré en premier, car std.compat
dépend de lui :
Ouvrez une invite de commandes des outils natifs pour VS : dans le menu Démarrer de Windows, tapez x86 natif : l’invite doit apparaître dans la liste des applications. Vérifiez que l’invite concerne Visual Studio 2022 version 17.5 ou ultérieure. Vous recevrez des erreurs du compilateur si vous utilisez la version incorrecte de l’invite.
Créez un répertoire pour essayer cet exemple, comme
%USERPROFILE%\source\repos\STLModules
, et faites-en le répertoire actif. Si vous choisissez un répertoire auquel vous n’avez pas accès en écriture, vous recevrez des erreurs.Compilez les modules nommés
std
etstd.compat
avec la commande suivante :cl /std:c++latest /EHsc /nologo /W4 /c "%VCToolsInstallDir%\modules\std.ixx" "%VCToolsInstallDir%\modules\std.compat.ixx"
Vous devez compiler
std
etstd.compat
en utilisant les mêmes paramètres de compilateur que ceux que vous envisagez d’utiliser avec le code qui les importera. Si vous avez une solution multiprojet, vous pouvez les compiler une seule fois, puis y faire référence depuis tous vos projets en utilisant l’option de compilateur/reference
.Si vous recevez des erreurs, vérifiez que vous utilisez la version correcte de l’invite de commandes.
Le compilateur génère quatre fichiers au cours des deux étapes précédentes :
std.ifc
est l’interface du module nommé binaire compilé que le compilateur consulte pour traiter l’instructionimport std;
. Le compilateur consulte égalementstd.ifc
pour traiterimport std.compat;
, carstd.compat
s’appuie surstd
. C’est un artefact uniquement au moment de la compilation. Il n’est pas fourni avec votre application.std.obj
contient l’implémentation de la bibliothèque standard.std.compat.ifc
est l’interface du module nommé binaire compilé que le compilateur consulte pour traiter l’instructionimport std.compat;
. C’est un artefact uniquement au moment de la compilation. Il n’est pas fourni avec votre application.std.compat.obj
contient l’implémentation. Cependant, la plus grande partie de l’implémentation est fournie parstd.obj
. Ajoutezstd.obj
à la ligne de commande quand vous compilez l’exemple d’application pour lier statiquement les fonctionnalités de la bibliothèque standard que vous utilisez dans votre application.
Vous pouvez contrôler le nom du fichier objet et le nom du fichier d’interface du module nommé avec les commutateurs suivants :
/Fo
définit le nom du fichier objet. Par exemple :/Fo:"somethingelse"
. Par défaut, le compilateur utilise pour le fichier objet le même nom que le fichier source du module (.ixx
) que vous compilez. Dans l’exemple, les noms des fichiers objet sont par défautstd.obj
etstd.compat.obj
, car nous compilons les fichiers de modulestd.ixx
etstd.compat.obj
./ifcOutput
définit le nom du fichier d’interface du module nommé (.ifc
). Par exemple :/ifcOutput "somethingelse.ifc"
. Par défaut, le compilateur utilise pour le fichier d’interface du module (.ifc
) le même nom que celui du fichier source du module (.ixx
) que vous compilez. Dans l’exemple, les fichiersifc
générés sontstd.ifc
etstd.compat.ifc
par défaut, car nous compilons les fichiers de modulestd.ixx
etstd.compat.ixx
.
Importez la bibliothèque
std.compat
en créant d’abord un fichier nomméstdCompatExample.cpp
avec le contenu suivant :import std.compat; int main() { printf("Import std.compat to get global names like printf()\n"); std::vector<int> v{5, 5, 5}; for (const auto& e : v) { printf("%i", e); } }
Dans le code précédent,
import std.compat;
remplace#include <cstdio>
et#include <vector>
. L’instructionimport std.compat;
rend la bibliothèque standard et les fonctions du runtime C disponibles avec une seule instruction. L’importation de ce module nommé, qui inclut la bibliothèque standard C++ et les fonctions de l’espace de noms global de la bibliothèque du runtime C, est plus rapide que le traitement d’un seul#include
comme#include <vector>
.Compilez l’exemple en utilisant la commande suivante :
cl /std:c++latest /EHsc /nologo /W4 stdCompatExample.cpp link stdCompatExample.obj std.obj std.compat.obj
Nous n’avons pas dû spécifier
std.compat.ifc
sur la ligne de commande, car le compilateur recherche automatiquement le fichier.ifc
qui correspond au nom du module dans une instructionimport
. Quand le compilateur rencontreimport std.compat;
, il trouvestd.compat.ifc
, car nous l’avons placé dans le même répertoire que le code source, ce qui nous évite de devoir le spécifier sur la ligne de commande. Si le fichier.ifc
se trouve dans un répertoire différent de celui du code source ou qu’il a un nom différent, utilisez le commutateur du compilateur/reference
pour y faire référence.Quand vous importez
std.compat
, vous devez établir un lien avecstd.compat
et avecstd.obj
, carstd.compat
utilise du code dansstd.obj
.Si vous créez un seul projet, vous pouvez combiner les étapes de génération des modules nommés de la bibliothèque standard
std
etstd.compat
en ajoutant"%VCToolsInstallDir%\modules\std.ixx"
et"%VCToolsInstallDir%\modules\std.compat.ixx"
(dans cet ordre) à la ligne de commande. Ce tutoriel montre la génération des modules de la bibliothèque standard comme une étape distincte, car vous ne devez générer qu’une seule fois les modules nommés de la bibliothèque standard, ensuite de quoi vous pouvez y faire référence depuis votre projet ou depuis plusieurs projets. Cependant, s’il est pratique de les générer tous en même temps, veillez à les placer avant les fichiers.cpp
qui les consomment, et spécifiez/Fe
pour nommer les fichiersexe
générés comme indiqué dans cet exemple :cl /c /FestdCompatExample /std:c++latest /EHsc /nologo /W4 "%VCToolsInstallDir%\modules\std.ixx" "%VCToolsInstallDir%\modules\std.compat.ixx" stdCompatExample.cpp link stdCompatExample.obj std.obj std.compat.obj
Dans cet exemple, la compilation du code source et la liaison de l’implémentation du module dans votre application sont des étapes distinctes. Ce n’est pas obligatoire. Vous pourriez utiliser
cl /std:c++latest /EHsc /nologo /W4 stdCompatExample.cpp std.obj std.compat.obj
pour compiler et lier en une seule étape. Il peut cependant être pratique de générer et de lier séparément, car vous ne devez alors générer qu’une seule fois les modules nommés de la bibliothèque standard, puis vous pouvez y faire référence depuis votre projet ou depuis plusieurs projets dans l’étape de liaison de votre build.La commande du compilateur précédente produit un exécutable nommé
stdCompatExample.exe
. Quand vous l’exécutez, il génère la sortie suivante :Import std.compat to get global names like printf() 555
Considérations relatives aux modules nommés de la bibliothèque standard
Le contrôle de version pour les modules nommés est le même que pour les en-têtes. Les fichiers du module nommé .ixx
sont installés au côté des en-têtes, par exemple : "%VCToolsInstallDir%\modules\std.ixx
, qui se résout en C:\Program Files\Microsoft Visual Studio\2022\Enterprise\VC\Tools\MSVC\14.38.33130\modules\std.ixx
dans la version des outils utilisés au moment de la rédaction de cette documentation. Sélectionnez la version du module nommé de la même façon que vous choisissez la version du fichier d’en-tête à utiliser : via le répertoire à partir duquel vous y faites référence.
Ne mélangez pas et ne mettez pas en correspondance l’importation d’unités d’en-tête et de modules nommés. Par exemple, n’utilisez pas import <vector>;
et import std;
dans le même fichier.
Ne mélangez pas et ne mettez pas en correspondance l’importation de fichiers d’en-tête de la bibliothèque standard C++ et les modules nommés std
ou std.compat
. Par exemple, n’utilisez pas #include <vector>
et import std;
dans le même fichier. Cependant, vous pouvez inclure des en-têtes C et importer des modules nommés dans le même fichier. Par exemple, vous pouvez utiliser import std;
et #include <math.h>
dans le même fichier. Simplement, n’incluez pas la version de la bibliothèque standard C++ <cmath>
.
Vous n’avez pas à vous défendre contre l’importation d’un module plusieurs fois. Autrement dit, vous n’avez pas besoin de protections des en-têtes de style #ifndef
dans les modules. Le compilateur sait quand il a déjà importé un module nommé et ignore les autres tentatives de le faire à nouveau.
Si vous devez utiliser la macro assert()
, utilisez #include <assert.h>
.
Si vous devez utiliser la macro errno
, utilisez #include <errno.h>
. Comme les modules nommés n’exposent pas de macros, c’est la solution de contournement si vous devez par exemple vérifier des erreurs de <math.h>
.
Les macros comme NAN
, INFINITY
et INT_MIN
sont définies par <limits.h>
, que vous pouvez inclure. Cependant, si vous utilisez import std;
, vous pouvez utiliser numeric_limits<double>::quiet_NaN()
et numeric_limits<double>::infinity()
à la place de NAN
et INFINITY
, et std::numeric_limits<int>::min()
à la place de INT_MIN
.
Résumé
Dans ce tutoriel, vous avez importé la bibliothèque standard en utilisant des modules. Découvrez ensuite comment créer et importer vos propres modules dans le tutoriel sur les modules nommés en C++.
Voir aussi
Comparer les unités d’en-tête, les modules et les en-têtes précompilés
Vue d’ensemble des modules dans C++
Visite guidée des modules C++ dans Visual Studio
Modification d’un projet pour utiliser des modules nommés C++
Commentaires
https://aka.ms/ContentUserFeedback.
Bientôt disponible : Tout au long de 2024, nous allons supprimer progressivement GitHub Issues comme mécanisme de commentaires pour le contenu et le remplacer par un nouveau système de commentaires. Pour plus d’informations, consultezEnvoyer et afficher des commentaires pour