Sdílet prostřednictvím


Kurz: Import standardní knihovny C++ pomocí modulů z příkazového řádku

Naučte se importovat standardní knihovnu C++ pomocí modulů knihovny C++. Výsledkem je rychlejší kompilace a je robustnější než použití souborů hlaviček nebo jednotek hlaviček nebo předkompilovaných hlaviček (PCH).

V tomto kurzu se seznámíte s následujícími informacemi:

  • Jak importovat standardní knihovnu jako modul z příkazového řádku
  • Výhody výkonu a použitelnosti modulů.
  • Dva standardní moduly std knihovny a std.compat rozdíl mezi nimi.

Požadavky

Tento kurz vyžaduje Visual Studio 2022 17.5 nebo novější.

Úvod do modulů standardní knihovny

Soubory hlaviček trpí sémantikou, která se může měnit v závislosti na definicích maker, pořadí jejich zahrnutí a pomalé kompilaci. Moduly tyto problémy řeší.

Teď je možné importovat standardní knihovnu jako modul místo jako tanglu souborů hlaviček. Je to mnohem rychlejší a robustnější než zahrnutí souborů hlaviček nebo jednotek hlaviček nebo předkompilovaných hlaviček (PCH).

Standardní knihovna C++23 zavádí dva pojmenované moduly: std a std.compat:

  • std exportuje deklarace a názvy definované v oboru názvů stdstandardní knihovny jazyka C++, například std::vector. Exportuje také obsah hlaviček obálky jazyka C, například <cstdio> a <cstdlib>, které poskytují funkce jako std::printf(). Funkce jazyka C definované v globálním oboru názvů, například ::printf(), se neexportují. To zlepšuje situaci, kdy zahrnutí hlavičky obálky jazyka C, jako je <cstdio>například, obsahuje také soubory stdio.hhlaviček jazyka C, které přinášejí globální verze oboru názvů jazyka C. To není problém, pokud importujete std.
  • std.compatexportuje vše v std a přidá globální obory názvů modulu runtime jazyka C, například ::printf, ::fopen::size_t, ::strlen, atd. Tento std.compat modul usnadňuje práci se základy kódu, které odkazují na mnoho funkcí a typů modulu runtime jazyka C v globálním oboru názvů.

Kompilátor naimportuje celou standardní knihovnu, když použijete import std; nebo import std.compat; ji provede rychleji než přenesením jednoho souboru hlavičky. Je rychlejší přenést celou standardní knihovnu s import std; (nebo import std.compat) než do #include <vector>, například.

Protože pojmenované moduly nezpřístupňují makra, makra jako assert, errnooffsetof, va_arg, a další nejsou při importu std nebo std.compat. Alternativní řešení najdete v tématu Standardní knihovna s názvem modul.

Informace o modulech C++

Soubory hlaviček jsou způsob sdílení deklarací a definic mezi zdrojovými soubory v jazyce C++. Před standardními moduly knihovny byste zahrnuli část standardní knihovny, kterou jste potřebovali, se direktivou, jako je #include <vector>. Soubory hlaviček jsou křehké a obtížně se vytváří, protože jejich sémantika se může měnit v závislosti na pořadí, ve které je zahrnete, nebo jestli jsou definovaná určitá makra. Také pomalou kompilaci, protože jsou znovu zpracovány každým zdrojovým souborem, který je obsahuje.

C++20 zavádí moderní alternativu označovanou jako moduly. V jazyce C++23 jsme byli schopni zavádět pojmenované moduly tak, aby představovaly standardní knihovnu.

Podobně jako soubory hlaviček umožňují moduly sdílet deklarace a definice napříč zdrojovými soubory. Na rozdíl od souborů hlaviček ale moduly nejsou křehké a dají se snadněji vytvářet, protože jejich sémantika se nemění kvůli definicím maker nebo pořadí, ve kterém je importujete. Kompilátor dokáže zpracovávat moduly mnohem rychleji, než může zpracovávat #include soubory, a zároveň využívá méně paměti v době kompilace. Pojmenované moduly nezpřístupňují definice maker ani podrobnosti privátní implementace.

Podrobné informace o modulech najdete v článku Přehled modulů v jazyce C++ . Tento článek také popisuje využívání standardní knihovny jazyka C++jako modulů, ale používá starší a experimentální způsob, jak to udělat.

Tento článek ukazuje nový a nejlepší způsob, jak využívat standardní knihovnu. Další informace o alternativních způsobech využití standardní knihovny naleznete v tématu Porovnání jednotek hlaviček, modulů a předkompilovaných hlaviček.

Import standardní knihovny pomocí std

Následující příklady ukazují, jak používat standardní knihovnu jako modul pomocí kompilátoru příkazového řádku. Informace o tom, jak to provést v integrovaném vývojovém prostředí sady Visual Studio, naleznete v tématu Sestavení modulů standardní knihovny ISO C++23.

import std; Příkaz nebo import std.compat; importuje standardní knihovnu do vaší aplikace. Nejprve ale musíte zkompilovat standardní knihovnu pojmenované moduly do binárního formátu. Následující kroky ukazují, jak na to.

Příklad: Postup sestavení a importu std

  1. Otevřete příkazový řádek nativních nástrojů x86 pro VS: v nabídce Start systému Windows zadejte nativní x86 a v seznamu aplikací by se měl zobrazit výzva. Ujistěte se, že je výzva k sadě Visual Studio 2022 verze 17.5 nebo vyšší. Pokud použijete nesprávnou verzi výzvy, zobrazí se vám chyby. Příklady použité v tomto kurzu jsou určené pro prostředí CMD.

  2. Vytvořte adresář, například %USERPROFILE%\source\repos\STLModulesa nastavte ho jako aktuální adresář. Pokud zvolíte adresář, ke kterému nemáte přístup k zápisu, při kompilaci se zobrazí chyby.

  3. Zkompilujte pojmenovaný std modul pomocí následujícího příkazu:

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

    Pokud dojde k chybám, ujistěte se, že používáte správnou verzi příkazového řádku.

    std Zkompilujte pojmenovaný modul pomocí stejného nastavení kompilátoru, které chcete použít s kódem, který importuje sestavený modul. Pokud máte víceprojektové řešení, můžete jednou zkompilovat standardní knihovnu pojmenovanou modul a pak na něj odkazovat ze všech projektů pomocí možnosti kompilátoru /reference .

    Pomocí předchozího příkazu kompilátoru kompilátor vypíše dva soubory:

    • std.ifc je zkompilovaná binární reprezentace pojmenovaného rozhraní modulu, které kompilátor konzultuje s procesem import std; příkazu. Jedná se o artefakt jen pro kompilaci. Nedoručuje se s vaší aplikací.
    • std.obj obsahuje implementaci pojmenovaného modulu. Když std.obj zkompilujete ukázkovou aplikaci, přidejte ji do příkazového řádku, abyste staticky propojili funkce, které používáte ze standardní knihovny do aplikace.

    Klíčové přepínače příkazového řádku v tomto příkladu jsou:

    Přepínač Význam
    /std:c++:latest Použijte nejnovější verzi standardu a knihovny jazyka C++. I když je podpora modulů dostupná v rámci /std:c++20, potřebujete nejnovější standardní knihovnu, abyste získali podporu pro standardní knihovny pojmenované moduly.
    /EHsc Použití zpracování výjimek jazyka C++ s výjimkou funkcí označených extern "C".
    /W4 Použití /W4 se obecně doporučuje, zejména pro nové projekty, protože umožňuje všechna upozornění úrovně 1, úrovně 2, úrovně 3 a většiny upozornění úrovně 4 (informační), která vám můžou pomoct zachytit potenciální problémy brzy. V podstatě poskytuje upozornění podobná lintům, která vám můžou pomoct zajistit co nejmenší možné chyby kódu.
    /c Kompilace bez propojení, protože právě v tuto chvíli vytváříme binární pojmenované rozhraní modulu.

    Název souboru objektu a název souboru rozhraní pojmenovaného modulu můžete řídit pomocí následujících přepínačů:

    • /Fo nastaví název souboru objektu. Například /Fo:"somethingelse". Kompilátor ve výchozím nastavení používá pro soubor objektu stejný název jako zdrojový soubor modulu (.ixx), který kompilujete. V příkladu je std.obj název souboru objektu ve výchozím nastavení, protože kompilujeme soubor std.ixxmodulu .
    • /ifcOutput nastaví název souboru rozhraní pojmenovaného modulu (.ifc). Například /ifcOutput "somethingelse.ifc". Kompilátor ve výchozím nastavení používá stejný název pro soubor rozhraní modulu (.ifc) jako zdrojový soubor modulu (.ixx), který kompilujete. V příkladu je std.ifc vygenerovaný ifc soubor ve výchozím nastavení, protože kompilujeme soubor std.ixxmodulu .
  4. Importujte knihovnu std , kterou jste vytvořili, nejprve vytvořte soubor s názvem importExample.cpp s následujícím obsahem:

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

    V předchozím kódu import std; nahradí #include <vector> a #include <iostream>. Tento příkaz import std; zpřístupní všechny standardní knihovny jedním příkazem. Import celé standardní knihovny je často mnohem rychlejší než zpracování jednoho standardního souboru hlaviček knihovny, například #include <vector>.

  5. Zkompilujte příklad pomocí následujícího příkazu ve stejném adresáři jako v předchozím kroku:

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

    V tomto příkladu není nutné zadávat /reference "std=std.ifc" příkazový řádek, protože kompilátor automaticky hledá .ifc soubor odpovídající názvu modulu určenému příkazem import . Když kompilátor narazí import std; , může zjistit std.ifc , jestli se nachází ve stejném adresáři jako zdrojový kód. .ifc Pokud je soubor v jiném adresáři než zdrojový kód, použijte přepínač kompilátoru/reference, který na něj odkazuje.

    V tomto příkladu kompilace zdrojového kódu a propojení implementace modulu do aplikace jsou samostatné kroky. Nemusí být. Můžete použít cl /std:c++latest /EHsc /nologo /W4 /reference "std=std.ifc" importExample.cpp std.obj ke kompilaci a propojení v jednom kroku. Může ale být vhodné sestavovat a odkazovat samostatně, protože pak stačí vytvořit standardní knihovnu s názvem modul jen jednou a pak na něj můžete odkazovat z projektu nebo z více projektů v kroku odkazu sestavení.

    Pokud vytváříte jeden projekt, můžete zkombinovat kroky sestavení std standardní knihovny s názvem modulu a kroku sestavení aplikace přidáním "%VCToolsInstallDir%\modules\std.ixx" do příkazového řádku. Vložte ho std před všechny .cpp soubory, které modul spotřebovávají.

    Ve výchozím nastavení se název výstupního spustitelného souboru přebírá z prvního vstupního souboru. Pomocí možnosti kompilátoru /Fe zadejte požadovaný název spustitelného souboru. Tento kurz ukazuje kompilaci std pojmenovaného modulu jako samostatného kroku, protože stačí vytvořit standardní knihovnu s názvem modul jen jednou a pak na něj můžete odkazovat z projektu nebo z více projektů. Ale může být vhodné sestavit všechno společně, jak je znázorněno na tomto příkazovém řádku:

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

    Vzhledem k předchozímu příkazovému řádku kompilátor vytvoří spustitelný soubor s názvem importExample.exe. Když ho spustíte, vytvoří následující výstup:

    Import the STL library for best performance
    555
    

Import standardní knihovny a globálních funkcí jazyka C pomocí std.compat

Standardní knihovna jazyka C++ zahrnuje standardní knihovnu ISO C. Modul std.compat poskytuje všechny funkce std modulu, jako je std::vector, std::cout, std::printf, std::scanfa tak dále. Poskytuje ale také globální verze oboru názvů těchto funkcí, jako ::printfjsou , ::scanf, ::fopen, ::size_tatd.

Pojmenovaný std.compat modul je vrstva kompatibility, která usnadňuje migraci existujícího kódu, který odkazuje na funkce modulu runtime jazyka C v globálním oboru názvů. Pokud se chcete vyhnout přidávání názvů do globálního oboru názvů, použijte import std;. Pokud potřebujete usnadnit migraci základu kódu, který používá mnoho nekvalifikovaných (globálních oborů názvů) funkcí modulu runtime jazyka C, použijte import std.compat;. To poskytuje globální názvy modulu runtime oboru názvů C, abyste nemuseli opravovat všechny globální názvy pomocí std::. Pokud nemáte žádný existující kód, který používá funkce modulu runtime jazyka C globálního oboru názvů, nemusíte používat import std.compat;. Pokud ve svém kódu voláte pouze několik funkcí modulu runtime jazyka C, může být lepší použít import std; a kvalifikovat několik názvů modulu runtime C globálního oboru názvů, které ho potřebují.std:: Například std::printf(). Pokud se při pokusu o kompilaci kódu zobrazí chyba error C3861: 'printf': identifier not found , zvažte použití import std.compat; k importu globálních funkcí modulu runtime oboru názvů C.

Příklad: Postup sestavení a importu std.compat

Než budete moci použít import std.compat; , musíte zkompilovat soubor rozhraní modulu nalezený ve formuláři zdrojového kódu v std.compat.ixx. Visual Studio dodává zdrojový kód modulu, abyste mohli modul zkompilovat pomocí nastavení kompilátoru, které odpovídají vašemu projektu. Postup je podobný sestavení pojmenovaného std modulu. Pojmenovaný std modul se sestaví jako první, protože std.compat na něm závisí:

  1. Otevřete příkazový řádek nativních nástrojů pro VS: v nabídce Start systému Windows zadejte nativní příkaz x86 a v seznamu aplikací by se měl zobrazit výzva. Ujistěte se, že je výzva k sadě Visual Studio 2022 verze 17.5 nebo vyšší. Pokud použijete nesprávnou verzi výzvy, zobrazí se chyby kompilátoru.

  2. Vytvořte adresář pro vyzkoušení tohoto příkladu, například %USERPROFILE%\source\repos\STLModulesa nastavte ho jako aktuální adresář. Pokud zvolíte adresář, ke kterému nemáte přístup k zápisu, zobrazí se chyby.

  3. std Zkompilujte a std.compat pojmenované moduly pomocí následujícího příkazu:

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

    Měli byste zkompilovat std a std.compat použít stejné nastavení kompilátoru, které chcete použít s kódem, který je naimportuje. Pokud máte víceprojektové řešení, můžete je zkompilovat jednou a pak na ně odkazovat ze všech projektů pomocí možnosti kompilátoru /reference .

    Pokud dojde k chybám, ujistěte se, že používáte správnou verzi příkazového řádku.

    Kompilátor vypíše čtyři soubory z předchozích dvou kroků:

    • std.ifc je zkompilované binární pojmenované rozhraní modulu, které kompilátor zkonzultuje s procesem import std; příkazu. Kompilátor se také pohlíždá std.ifc na zpracování import std.compat; , protože std.compat je založen na std. Jedná se o artefakt jen pro kompilaci. Nedoručuje se s vaší aplikací.
    • std.obj obsahuje implementaci standardní knihovny.
    • std.compat.ifc je zkompilované binární pojmenované rozhraní modulu, které kompilátor zkonzultuje s procesem import std.compat; příkazu. Jedná se o artefakt jen pro kompilaci. Nedoručuje se s vaší aplikací.
    • std.compat.obj obsahuje implementaci. Většinu implementace však poskytuje std.obj. Přidejte std.obj do příkazového řádku při kompilaci ukázkové aplikace staticky propojit funkce, které používáte ze standardní knihovny do aplikace.

    Název souboru objektu a název souboru rozhraní pojmenovaného modulu můžete řídit pomocí následujících přepínačů:

    • /Fo nastaví název souboru objektu. Například /Fo:"somethingelse". Kompilátor ve výchozím nastavení používá pro soubor objektu stejný název jako zdrojový soubor modulu (.ixx), který kompilujete. V tomto příkladu jsou std.obj názvy souborů objektů a std.compat.obj ve výchozím nastavení, protože kompilujeme soubory std.ixx modulů a std.compat.obj.
    • /ifcOutput nastaví název souboru rozhraní pojmenovaného modulu (.ifc). Například /ifcOutput "somethingelse.ifc". Kompilátor ve výchozím nastavení používá stejný název pro soubor rozhraní modulu (.ifc) jako zdrojový soubor modulu (.ixx), který kompilujete. V tomto příkladu jsou vygenerované ifc soubory ve výchozím nastavení, std.compat.ifc protože kompilujeme soubory std.ixx modulů a std.compat.ixx.std.ifc
  4. Importujte knihovnu std.compat tak, že nejprve vytvoříte soubor s názvem stdCompatExample.cpp s následujícím obsahem:

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

    V předchozím kódu import std.compat; nahradí #include <cstdio> a #include <vector>. import std.compat; Příkaz zpřístupňuje standardní funkce knihovny a modulu runtime jazyka C jedním příkazem. Import tohoto pojmenovaného modulu, který zahrnuje standardní knihovnu C++ a globální funkce oboru názvů knihovny modulu runtime jazyka C, je rychlejší než zpracování jednoho #include typu #include <vector>.

  5. Zkompilujte příklad pomocí následujícího příkazu:

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

    Na příkazovém řádku jsme nemuseli zadávat std.compat.ifc , protože kompilátor automaticky hledá .ifc soubor, který odpovídá názvu modulu v import příkazu. Když kompilátor zjistí import std.compat; , že ho najde std.compat.ifc , protože ho umístíme do stejného adresáře jako zdrojový kód – tím nás nutí určit ho na příkazovém řádku. .ifc Pokud je soubor v jiném adresáři než zdrojový kód nebo má jiný název, použijte přepínač kompilátoru/reference, který na něj odkazuje.

    Při importu std.compatje nutné propojit s oběma std.compat a std.obj protože std.compat používá kód v std.obj.

    Pokud vytváříte jeden projekt, můžete zkombinovat kroky sestavení std a std.compat standardní knihovny pojmenované moduly přidáním "%VCToolsInstallDir%\modules\std.ixx" a "%VCToolsInstallDir%\modules\std.compat.ixx" (v takovém pořadí) do příkazového řádku. Tento kurz ukazuje sestavení standardních modulů knihovny jako samostatný krok, protože stačí vytvořit standardní knihovnu pojmenovaných modulů jen jednou a pak na ně můžete odkazovat z projektu nebo z více projektů. Pokud je ale vhodné je sestavit všechny najednou, nezapomeňte je umístit před všechny .cpp soubory, které je spotřebovávají, a zadat /Fe název předdefinované exe , jak je znázorněno v tomto příkladu:

    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
    

    V tomto příkladu kompilace zdrojového kódu a propojení implementace modulu do vaší aplikace jsou samostatné kroky. Nemusí být. Můžete použít cl /std:c++latest /EHsc /nologo /W4 stdCompatExample.cpp std.obj std.compat.obj ke kompilaci a propojení v jednom kroku. Může ale být vhodné sestavovat a odkazovat samostatně, protože pak stačí vytvořit standardní knihovnu s názvem moduly jen jednou a pak na ně můžete odkazovat z projektu nebo z více projektů v kroku odkazu sestavení.

    Předchozí příkaz kompilátoru vytvoří spustitelný soubor s názvem stdCompatExample.exe. Když ho spustíte, vytvoří následující výstup:

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

Důležité informace o standardní knihovně s názvem modulu

Správa verzí pojmenovaných modulů je stejná jako u hlaviček. Pojmenované .ixx soubory modulu se instalují spolu s hlavičkami, například: "%VCToolsInstallDir%\modules\std.ixx, které se přeloží na C:\Program Files\Microsoft Visual Studio\2022\Enterprise\VC\Tools\MSVC\14.38.33130\modules\std.ixx verzi nástrojů použitých v době psaní tohoto zápisu. Vyberte verzi pojmenovaného modulu stejným způsobem, jakým zvolíte verzi hlavičkového souboru, kterou chcete použít – podle adresáře, ze který na ně odkazujete.

Nekombinujte a neshodujte import jednotek záhlaví a pojmenovaných modulů. Například ne import <vector>; a import std; ve stejném souboru.

Nekombinujte a neshodujte import souborů hlaviček standardní knihovny C++ a pojmenovaných modulů std nebo std.compat. Například ne #include <vector> a import std; ve stejném souboru. Do stejného souboru ale můžete zahrnout hlavičky jazyka C a importovat pojmenované moduly. Můžete například import std; a #include <math.h> ve stejném souboru. Nezahrnujte jenom standardní verzi <cmath>knihovny C++.

Nemusíte se bránit proti vícenásobnému importu modulu. To znamená, že #ifndef vmodulech Kompilátor ví, kdy už naimportoval pojmenovaný modul, a ignoruje duplicitní pokusy.

Pokud potřebujete makro použít assert() , pak #include <assert.h>.

Pokud potřebujete makro použíterrno, #include <errno.h> Vzhledem k tomu, že pojmenované moduly nezpřístupňují makra, toto je alternativní řešení, pokud potřebujete například zkontrolovat chyby.<math.h>

Makra, jako NANje , INFINITYa INT_MIN jsou definována pomocí <limits.h>, které můžete zahrnout. Nicméně, pokud můžete import std; použít numeric_limits<double>::quiet_NaN() a numeric_limits<double>::infinity() místo NAN a INFINITY, a std::numeric_limits<int>::min() místo INT_MIN.

Shrnutí

V tomto kurzu jste importovali standardní knihovnu pomocí modulů. Dále se dozvíte o vytváření a importu vlastních modulů v kurzu pojmenovaných modulů v jazyce C++.

Viz také

Porovnání jednotek záhlaví, modulů a předkompilovaných hlaviček
Přehled modulů v jazyce C++
Prohlídka modulů C++ v sadě Visual Studio
Přesunutí projektu do modulů s názvem C++