Oktatóanyag: A C++ standard kódtár importálása modulok használatával a parancssorból

Megtudhatja, hogyan importálhatja a C++ standard kódtárat C++ kódtármodulok használatával. Ez gyorsabb fordítást eredményez, és robusztusabb, mint a fejlécfájlok, fejlécegységek vagy előre összeállított fejlécek (PCH) használata.

Ebben az oktatóanyagban a következőket ismerheti meg:

  • A standard kódtár importálása modulként a parancssorból.
  • A modulok teljesítmény- és használhatósági előnyei.
  • A két standard kódtármodul std és std.compat a köztük lévő különbség.

Előfeltételek

Ehhez az oktatóanyaghoz a Visual Studio 2022 17.5-ös vagy újabb verziójára van szükség.

A standard kódtármodulok bemutatása

A fejlécfájl szemantikája a makródefinícióktól, a belefoglalásuk sorrendjétől és a fordítás lassúságától függően változhat. A modulok megoldják ezeket a problémákat.

Mostantól a standard könyvtár modulként importálható ahelyett, hogy fejlécfájlok kuszaságaként kezelnénk. Ez sokkal gyorsabb és robusztusabb, mint a fejlécfájlok, fejlécegységek vagy előre összeállított fejlécek (PCH).

A C++23 standard kódtár két nevesített modult vezet be: std és std.compat:

  • std exportálja a C++ standard kódtár névterében stddefiniált deklarációkat és neveket, például std::vector. Emellett exportálja a C burkolófejek tartalmát is, például <cstdio> és <cstdlib>, amelyek olyan függvényeket biztosítanak, mint a std::printf(). A globális névtérben definiált C-függvények, például ::printf()a , nem lesznek exportálva. Ez javítja azt a helyzetet, amikor egy C burkoló fejléc például <cstdio>is C fejlécfájlokat tartalmaz, mint például stdio.h, amelyek a C globális névtér változatait is magukkal hozzák. Ez nem jelent problémát az importáláskor std.
  • std.compat mindent exportál a std-ban, és hozzáadja a C futtatási környezet globális névtereit, például az ::printf, ::fopen, ::size_t, ::strlen és így tovább. A std.compat modul megkönnyíti a globális névtérben található számos C futtatókörnyezeti függvényre/típusra hivatkozó kódbázisok használatát.

A fordító a teljes standard könyvtárat importálja, amikor ön a import std; vagy import std.compat; használja, és ezt gyorsabban hajtja végre, mint egyetlen fejlécfájl betöltése esetén. Gyorsabb, ha például a teljes standard könyvtárat import std; (vagy import std.compat) használatával importáljuk, mint #include <vector>.

Mivel a nevesített modulok nem teszik elérhetővé a makrókat, például a assert, errno, offsetof, va_arg és más makrók nem érhetők el, amikor importálja a std vagy std.compat modulokat. A megoldásokért tekintse meg a A standard könyvtár elnevezett moduljainak szempontjai.

Tudnivalók a C++ modulokról

A fejlécfájlok a deklarációk és definíciók megosztásának módját képezik a C++-ban található forrásfájlok között. A szabványos kódtármodulok előtt a szükséges standard kódtár részét is belefoglalná egy olyan irányelvbe, mint a #include <vector>. A fejlécfájlok törékenyek és nehezen írhatók, mert szemantikáik a belefoglalásuk sorrendjétől vagy bizonyos makrók definiálásától függően változhatnak. A fordítás is lassú, mert minden olyan forrásfájl újra feldolgozta őket, amely tartalmazza őket.

A C++20 egy modern, moduloknak nevezett alternatívát vezet be. A C++23-ban a modultámogatást kihasználva elnevezett modulokat vezettünk be, amelyek a standard kódtárat képviselik.

A fejlécfájlokhoz hasonlóan a modulok lehetővé teszik a deklarációk és definíciók megosztását a forrásfájlok között. A fejlécfájlokkal ellentétben azonban a modulok nem törékenyek, és könnyebben írhatók, mert a szemantikák nem változnak a makródefiníciók vagy az importálásuk sorrendje miatt. A fordító sokkal gyorsabban tudja feldolgozni a modulokat, mint a #include fájlokat, és kevesebb memóriát használ fordításkor. A nevesített modulok nem teszik közzé a makródefiníciókat és a privát megvalósítás részleteit.

Ez a cikk bemutatja a standard kódtár felhasználásának új és legjobb módját. A standard kódtár használatának alternatív módjairól további információt a fejlécegységek, modulok és előre összeállított fejlécek összehasonlítása című témakörben talál.

A standard kódtár importálása a következővel: std

Az alábbi példák bemutatják, hogyan használhatja a standard kódtárat modulként a parancssori fordító használatával. Ennek a Visual Studio IDE-ben való használatáról az ISO C++23 Standard kódtármodulok buildelése című témakörben olvashat.

Az utasítás import std; vagy import std.compat; a standard könyvtárat importálja az alkalmazásba. Először azonban bináris formában kell lefordítania a standard kódtár nevesített moduljait. Az alábbi lépések bemutatják, hogyan.

Példa: Felépítés és importálás std

  1. Nyisson meg egy x86 natív eszközök parancssort a VS-hez: a Windows Start menüben írja be az x86 natív típust , és a parancssornak szerepelnie kell az alkalmazások listájában. Győződjön meg arról, hogy az előugró ablak a Visual Studio 2022 17.5-ös vagy újabb verziójára vonatkozik. Ha a prompt rossz verzióját használja, hibákat kap. Az oktatóanyagban használt példák a CMD parancssorhoz készültek.

  2. Hozzon létre egy könyvtárat, például %USERPROFILE%\source\repos\STLModules, és állítsa be aktuális könyvtárként. Ha olyan könyvtárat választ, amelyhez nem rendelkezik írási hozzáféréssel, a fordítás során hibaüzenetek jelennek meg.

  3. Fordítsa le a std megnevezett modult a következő paranccsal:

    cl /std:c++latest /EHsc /nologo /W4 /c "%VCToolsInstallDir%\modules\std.ixx"
    

    Ha hibaüzenetet kap, győződjön meg arról, hogy a parancssor megfelelő verzióját használja.

    Fordítsa le a std megnevezett modult ugyanazokkal a fordítóbeállításokkal, amelyeket a beépített modult importáló kóddal használni kíván. Ha többprojektes megoldással rendelkezik, egyszer lefordíthatja a standard könyvtár-modult, majd az összes projektből hivatkozhat rá a /reference kapcsoló segítségével.

    Az előző fordítóparancs használatával a fordító két fájlt ad ki:

    • std.ifc azon elnevezett modulfelület lefordított bináris reprezentációja, amelyet a fordító az import std; utasítás feldolgozásához szükséges konzultációként használ. Ez csak fordítási idejű összetevő. Nem része az alkalmazás csomagjának.
    • std.obj tartalmazza a megnevezett modul implementációját. A mintaalkalmazás lefordításakor vegye fel std.obj a parancssorba, hogy statikusan összekapcsolja a standard kódtárból használt funkciókat az alkalmazással.

    A példában szereplő fő parancssori kapcsolók a következők:

    Kapcsoló Meaning
    /std:c++latest Használja a C++ nyelvi szabvány és a kódtár legújabb verzióját. Bár a modultámogatás a következő területen /std:c++20érhető el, a standard kódtár nevesített modulok támogatásához a legújabb standard kódtárra van szüksége.
    /EHsc C++ kivételkezelés használata a megjelölt extern "C"függvények kivételével.
    /W4 A használat /W4 általánosan ajánlott, különösen az új projektek esetében, mivel lehetővé teszi az 1. szint, a 2. szint, a 3. szint és a legtöbb 4. szintű (tájékoztató) figyelmeztetést, ami segíthet a potenciális problémák korai észlelésében. Lényegében lint-szerű figyelmeztetéseket biztosít, amelyek segítenek biztosítani a lehető legkevesebb nehezen kereshető kódhibát.
    /c Fordítás csatolás nélkül, mert jelenleg csak a bináris elnevezett modul felületét készítjük el.

    Az objektumfájl nevét és a modul felületének nevét a következő kapcsolókkal szabályozhatja:

    • /Fo az objektumfájl nevét állítja be. Például: /Fo:"somethingelse". Alapértelmezés szerint a fordító ugyanazt a nevet használja az objektumfájlhoz, mint a modul forrásfájlja (.ixx) amelyet összeállít. A példában az objektumfájl neve alapértelmezés szerint azért van std.obj , mert a modulfájlt std.ixxállítjuk össze.
    • /ifcOutput beállítja a megnevezett modul felületfájljának (.ifc) nevét. Például: /ifcOutput "somethingelse.ifc". Alapértelmezés szerint a fordító ugyanazt a nevet használja a modul felületfájlja (.ifc) számára, mint a modul forrásfájlja (.ixx) amelyet összeállít. A példában a létrehozott ifc fájl alapértelmezés szerint azért van std.ifc , mert a modulfájlt std.ixxállítjuk össze.
  4. Importálja a std létrehozott tárat úgy, hogy először létrehoz egy fájlt importExample.cpp a következő tartalommal:

    // 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;
        }
    }
    

    Az előző kódban a import std; helyettesíti a #include <vector> és #include <iostream> elemeket. Az utasítás import std; az összes standard kódtárat egyetlen utasítással teszi elérhetővé. A teljes standard kódtár importálása gyakran sokkal gyorsabb, mint egy szabványos kódtár fejlécfájljának feldolgozása, például #include <vector>.

  5. Fordítsa le a példát az előző lépéssel megegyező könyvtárban található alábbi paranccsal:

    cl /c /std:c++latest /EHsc /nologo /W4 /reference "std=std.ifc" importExample.cpp
    link importExample.obj std.obj
    

    Ebben a példában nem szükséges a /reference "std=std.ifc" megadása a parancssorban, mert a fordító automatikusan megkeresi az import utasítás által megadott modulnevének megfelelő .ifc fájlt. Amikor a fordító találkozik import std;, akkor megtalálhatja std.ifc, ha ugyanabban a könyvtárban található, mint a forráskód. Ha a .ifc fájl a forráskódtól eltérő könyvtárban található, a /reference fordítókapcsolóval hivatkozhat rá.

    Ebben a példában a forráskód összeállítása és a modul implementációjának az alkalmazáshoz való csatolása külön lépések. Nem kell, hogy legyenek. Használhatja a cl /std:c++latest /EHsc /nologo /W4 /reference "std=std.ifc" importExample.cpp std.obj-t arra, hogy egyetlen lépésben fordítson és linkeljen. Azonban célszerű lehet külön építeni és összekapcsolni, mert akkor csak egyszer kell létrehoznia a standard kódtár nevesített modult, majd a build hivatkozási lépésében hivatkozhat rá a projektből vagy több projektből.

    Ha egyetlen projektet hoz létre, a std standard könyvtár modul létrehozásának lépéseit és az alkalmazás létrehozásának lépéseit kombinálhatja úgy, hogy hozzáadja a "%VCToolsInstallDir%\modules\std.ixx" a parancssorhoz. Helyezze a modult .cpp használó std fájlok elé.

    Alapértelmezés szerint a kimeneti végrehajtható fájl neve az első bemeneti fájlból származik. A fordítóprogram /Fe opciójával adja meg a kívánt végrehajtható fájlnevet. Ez az oktatóanyag bemutatja, hogyan állítsa össze külön lépésként a std nevű modult, mivel a standard könyvtár nevezetű modult csak egyszer kell létrehoznia, és aztán hivatkozhat rá a projektjéből vagy akár több projektből is. De kényelmes lehet mindent együtt felépíteni, ahogy az ebben a parancssorban látható:

    cl /FeimportExample /std:c++latest /EHsc /nologo /W4 "%VCToolsInstallDir%\modules\std.ixx" importExample.cpp
    

    Az előző parancssor alapján a fordító létrehoz egy importExample.exe nevű végrehajtható fájlt. A futtatáskor a következő kimenet jön létre:

    Import the STL library for best performance
    555
    

A standard kódtár és a globális C függvény importálása a következővel: std.compat

A C++ szabványkódtár tartalmazza az ISO C szabványkódtárat. A std.compat modul az összes funkcióját biztosítja, amit a std modul biztosít, például std::vector, std::cout, std::printf, std::scanf stb. De ezen függvények globális névtérverzióit is biztosítja, például ::printf, ::scanf, ::fopen, ::size_tstb.

A std.compat megnevezett modul egy kompatibilitási réteg, amely megkönnyíti a meglévő kód áttelepítését, amely a C futtatókörnyezeti függvényekre hivatkozik a globális névtérben. Ha el szeretné kerülni, hogy neveket adjon hozzá a globális névtérhez, használja a következőt import std;: . Ha egyszerűbben kell migrálnia egy olyan kódbázist, amely sok nem minősített (globális névtér) C futtatókörnyezeti függvényt használ, használja a következőt import std.compat;: . C-futtatókörnyezet globális névterében található nevek biztosításával nem szükséges az összes globális nevet std:: minősítéssel ellátnia. Ha nem rendelkezik olyan kóddal, amely a C futtatókörnyezet globális névterét használja, akkor nem kell használnia import std.compat;. Ha csak néhány C futtatókörnyezeti függvényt hív meg a kódban, akkor jobb lehet használni import std; és minősíteni azt a néhány globális névteret, amelynek C futtatókörnyezeti std::neve szükséges. Például: std::printf(). Ha a kód lefordításakor hasonló error C3861: 'printf': identifier not found hibaüzenet jelenik meg, fontolja meg a import std.compat; használatát a C futtató környezeti globális névtérfüggvények importálásához.

Példa: Felépítés és importálás std.compat

Mielőtt használhatná a import std.compat;, le kell fordítania a modul leírófájlját, amely forráskód formájában std.compat.ixx található. A Visual Studio a modul forráskódját tartalmazza, hogy a modult a projektnek megfelelő fordítóbeállítások használatával fordíthassa le. A lépések hasonlóak a std nevesített modul létrehozásához. A std nevesített modul azért van először felépítve, mert std.compat attól függ:

  1. Nyisson meg egy Natív eszközök parancssort a VS-hez: a Windows Start menüjében írja be az x86 natív típust, és a parancssornak szerepelnie kell az alkalmazások listájában. Győződjön meg arról, hogy az előugró ablak a Visual Studio 2022 17.5-ös vagy újabb verziójára vonatkozik. Ha nem a megfelelő verziót használja, fordítási hibákat fog kapni.

  2. Hozzon létre egy könyvtárat a példa kipróbálásához, például %USERPROFILE%\source\repos\STLModulesaz aktuális könyvtárként. Ha olyan könyvtárat választ, amelyhez nem rendelkezik írási hozzáféréssel, hibaüzenetek jelennek meg.

  3. Fordítsa le a std és std.compat elnevezett modulokat a következő paranccsal:

    cl /std:c++latest /EHsc /nologo /W4 /c "%VCToolsInstallDir%\modules\std.ixx" "%VCToolsInstallDir%\modules\std.compat.ixx"
    

    Az std és std.compat kódokat ugyanazokkal a fordítóbeállításokkal kell komplikálnod, amelyeket majd az azokat importáló kód esetében is használni fogsz. Ha többprojektes megoldással rendelkezik, egyszer lefordíthatja őket, majd a fordítóval hivatkozhat rájuk az /reference összes projektből.

    Ha hibaüzenetet kap, győződjön meg arról, hogy a parancssor megfelelő verzióját használja.

    A fordító négy fájlt ad ki az előző két lépésből:

    • std.ifc az a megnevezett modulfelületként ismert lefordított bináris, amelyet a fordító a import std; utasítás feldolgozásához használ. A fordító a std.ifc is konzultálja import std.compat; feldolgozása érdekében, mert std.compat az std-re épít. Ez egy kizárólag fordítási időben létező összetevő. Nem szállítják az alkalmazással együtt.
    • std.obj A standard kódtár implementációját tartalmazza.
    • std.compat.ifc a lefordított bináris modul interfész neve, amelyet a fordító a import std.compat; utasítás feldolgozásához kér le. Ez csak fordítási idejű összetevő. Nem része az alkalmazás csomagjának.
    • std.compat.obj implementációt tartalmaz. A megvalósítás nagy részét azonban a std.obj biztosítja. Amikor a mintaalkalmazást fordítja, adja hozzá a std.obj-t a parancssorhoz, hogy a standard kódtárból az alkalmazáshoz használt funkciókat statikusan csatolja.

    Az objektumfájl nevét és a modul felületének nevét a következő kapcsolókkal szabályozhatja:

    • /Fo az objektumfájl nevét állítja be. Például: /Fo:"somethingelse". Alapértelmezés szerint a fordító ugyanazt a nevet használja az objektumfájlhoz, mint a modul forrásfájlja (.ixx) amelyet összeállít. A példában alapértelmezés szerint std.obj és std.compat.obj az objektumfájlnevek, mert a modulfájlokat std.ixx és std.compat.ixx állítjuk össze.
    • /ifcOutput beállítja a megnevezett modul felületfájljának (.ifc) nevét. Például: /ifcOutput "somethingelse.ifc". Alapértelmezés szerint a fordító ugyanazt a nevet használja a modul felületfájlja (.ifc) számára, mint a modul forrásfájlja (.ixx) amelyet összeállít. A példában a létrehozott ifc fájlok alapértelmezés szerint std.ifc és std.compat.ifc fájlok, mert a std.ixx és std.compat.ixx modulfájlokat fordítjuk.
  4. Importálja a std.compat tárat úgy, hogy először létrehoz egy fájlt stdCompatExample.cpp a következő tartalommal:

    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);
        }
    }
    

    Az előző kódban a import std.compat; helyettesíti a #include <cstdio> és #include <vector> elemeket. Az utasítás import std.compat; egy utasítással elérhetővé teszi a standard kódtárat és a C futtatókörnyezeti függvényeket. Ennek a nevesített modulnak az importálása, amely magában foglalja a C++ standard könyvtárat és a C futtatókörnyezeti könyvtár globális névtér függvényeit, gyorsabb, mint feldolgozni egyetlen #include hasonló #include <vector>.

  5. A példa lefordítása a következő parancs használatával:

    cl /std:c++latest /EHsc /nologo /W4 stdCompatExample.cpp
    link stdCompatExample.obj std.obj std.compat.obj
    

    Nem kellett megadni std.compat.ifc a parancssorban, mert a fordító automatikusan megkeresi azt a fájlt, amely megfelel a .ifc modul nevének egy import utasításban. Amikor a fordító találkozik import std.compat;-val, észleli a std.compat.ifc-t, mivel ugyanabba a könyvtárba helyeztük, mint a forráskódot, így nem szükséges megadni a parancssorban. Ha a .ifc fájl más könyvtárban található, mint a forráskód, vagy más néven van, a /reference fordító kapcsolóval hivatkozhat rá.

    Importáláskor össze kell kapcsolni mind std.compat, mind std.obj elemmel, mert std.compat kódot használ std.obj-ban.

    Ha egyetlen projektet hoz létre, kombinálhatja a std és a std.compat standard könyvtárat elnevezett modulok létrehozásának lépéseit azáltal, hogy a "%VCToolsInstallDir%\modules\std.ixx" és "%VCToolsInstallDir%\modules\std.compat.ixx" (ebben a sorrendben) hozzáadja a parancsorhoz. Ez az oktatóanyag azt mutatja be, hogy a standard kódtármodulokat külön lépésként kell felépíteni, mert csak egyszer kell létrehoznia a standard kódtár nevesített moduljait, majd a projektből vagy több projektből is hivatkozhat rájuk. Ha azonban kényelmes egyszerre felépíteni őket, ügyeljen arra, hogy azokat minden olyan fájl elé .cpp helyezze, amely felhasználja őket, és adja meg /Fe , hogy a buildet exe az alábbi példában látható módon nevezze el:

    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
    

    Ebben a példában a forráskód összeállítása és a modul implementációjának az alkalmazáshoz való csatolása külön lépések. Nem kell, hogy legyenek. Használhatja a cl /std:c++latest /EHsc /nologo /W4 stdCompatExample.cpp std.obj std.compat.obj-t arra, hogy egyetlen lépésben fordítson és linkeljen. Célszerű lehet azonban külön építeni és összekapcsolni a modulokat, mert akkor csak egyszer kell létrehoznia a standard kódtár nevesített moduljait, majd a build hivatkozási lépésében hivatkozhat rájuk a projektből vagy több projektből.

    Az előző fordítóparancs létrehoz egy végrehajtható nevet stdCompatExample.exe. A futtatáskor a következő kimenet jön létre:

    Import std.compat to get global names like printf()
    555
    

Standard kódtár nevesített modulokkal kapcsolatos szempontok

A megnevezett modulok verziószámozása megegyezik a fejlécek verziószámozásával. A .ixx nevesített modulfájlok a fejlécek mellett vannak telepítve, például: "%VCToolsInstallDir%\modules\std.ixx", amely az íráskor használt eszközök verziójában oldható fel C:\Program Files\Microsoft Visual Studio\2022\Enterprise\VC\Tools\MSVC\14.38.33130\modules\std.ixx . Válassza ki az elnevezett modul verzióját ugyanúgy, ahogyan a használni kívánt fejlécfájl verzióját – a hivatkozott könyvtár alapján.

Ne keverje a fejlécegységek importálását és a megnevezett modulokat. Például ne import <vector>; és import std; ugyanabban a fájlban.

Ne keverje és ne használja vegyesen a C++ szabványkönyvtár fejlécfájljait és a megnevezett modulokat std vagy std.compat. Például ne #include <vector> és import std; ugyanabban a fájlban. A C fejléceket azonban belefoglalhatja, és importálhatja a nevesített modulokat ugyanabban a fájlban. Például ugyanabban a fájlban [művelet1] és [művelet2] is elvégezhető. Csak ne tartalmazza a C++ standard kódtár verzióját <cmath>.

Nem kell többször védekeznie egy modul importálása ellen. Vagyis nincs szükség stílusfejléc-védőkre #ifndef a modulokban. A fordító tudja, hogy mikor importált már egy elnevezett modult, és figyelmen kívül hagyja az ismétlődő próbálkozásokat.

Ha a makrót assert() kell használnia, akkor #include <assert.h>.

Ha a makrót errno kell használnia, #include <errno.h>. Mivel a nevesített modulok nem teszik elérhetővé a makrókat, például az <math.h> hibák keresésére, ez a megoldás.

Az olyan makrókat, mint a NAN, INFINITY és INT_MIN, a <limits.h> határozza meg, amelyeket belefoglalhat. Azonban, ha import std;, akkor használhatja std::numeric_limits<double>::quiet_NaN() és std::numeric_limits<double>::infinity()-t a NAN és INFINITY helyett, valamint std::numeric_limits<int>::min()-t a INT_MIN helyett.

Összefoglalás

Ebben az oktatóanyagban modulokkal importálta a standard kódtárat. A következő lépésben megismerheti saját moduljainak létrehozását és importálását a Nevesített modulok oktatóanyagban a C++-ban.

Lásd még

Fejlécegységek, modulok és előre összeállított fejlécek összehasonlítása
A C++ moduljainak áttekintése
C++ modulok bemutatója a Visual Studióban
Projekt áthelyezése C++ nevű modulokba