Unterstützung von C++20-Standardmodulen mit MSVC in Visual Studio 2019 Version 16.8

Unterstützung von C++20-Standardmodulen mit MSVC in Visual Studio 2019 Version 16.8

Bitte lesen Sie unsere Versionshinweise zu Visual Studio 2019 Version 16.8 Vorschau 3 für weitere unserer neuesten Funktionen.

Seit unserer letzten Aktualisierung bezüglich der Konformität von C++-Modulen ist einige Zeit vergangen. Die Toolset-, Projektsystem- und IDE-Teams haben hart daran gearbeitet, ein erstklassiges C++-Modulerlebnis in Visual Studio 2019 zu schaffen. Es gibt viel zu teilen, also fangen wir gleich an:

Was ist neu?

  • /std:c++latest Impliziert C++-Module.
  • Private Modulfragmente sind eine neue Form der API-Kapselung für primäre Modulschnittstellen.
  • Übersetzung einschließen ermöglicht die einfache Übernahme von Header-Einheiten, ohne bestehenden Code zu ändern.
  • Modulverknüpfung ist eine neue Art der Verknüpfung, die vom Back-End und Linker erzwungen wird.
  • Projektsystemänderungen zur Aktivierung von C++-Modulszenarien.
  • IntelliSense-Updates.

/std:c++latest Impliziert C++-Module

Seit MSVC mit der Implementierung des Modules TS begann, erforderte das Toolset immer die Verwendung von /experimental:module auf jeder Zusammenstellung. Seit der Zusammenführung von Modulen in den C++20-Standard (wir können jetzt offiziell C++20 sagen!) hat der Compiler an der Konformität von C++20-Modulen gearbeitet, bis genau zu dem Zeitpunkt, an dem wir Module getrost in /std:c++latest . Diese Zeit ist jetzt!

Es gibt einige Einschränkungen bei der Implikation von C++-Modulen unter /std:c++latest :

  • /std:c++latest bedeutet jetzt /permissive- . Das bedeutet, dass Kunden derzeit auf das permissive Verhalten des Compilers in Kombination mit /std:c++latest setzen müssen jetzt /permissive anwenden auf der Kommandozeile. Hinweis: Aktivieren von /permissive deaktiviert auch die Verwendung von Modulen.
  • Jetzt, da Module in den neuesten Sprachmodus einige gerollt werden Code kann aufgrund von module beschädigt werden und import in Schlüsselwörter umgewandelt werden. Wir haben einige der gängigen Szenarien dokumentiert. Das Papier MSVC implementiert, um module zu konvertieren und import in Stichworten hat noch mehr Szenarien:P1857R1.
  • Der std.* Module, die mit Visual Studio geliefert werden, sind nicht über /std:c++latest verfügbar allein. Die Standardbibliotheksmodule wurden noch nicht standardisiert und bleiben daher experimentell. Um weiterhin die Module der Standardbibliothek zu verwenden, benötigen Benutzer /experimental:module als Teil ihrer Befehlszeilenoptionen.

Private Modulfragmente

C++20 fügte einer primären Modulschnittstelle einen neuen Abschnitt hinzu, der als privates Modulfragment [module.private.frag] bekannt ist. Fragmente privater Module ermöglichen es Autoren, Details einer Bibliothek wirklich zu verbergen, ohne eine separate C++-Quelldatei erstellen zu müssen, die Implementierungsdetails enthält. Stellen Sie sich ein Szenario vor, in dem ein PIMPL-Muster in einer primären Modulschnittstelle verwendet wird:

module;
#include <memory>
export module m;
struct Impl;

export
class S {
public:
  S();
  ~S();
  void do_stuff();
  Impl* get() const { return impl.get(); }
private:
  std::unique_ptr<Impl> impl;
};

module :private; // Everything beyond this point is not available to importers of 'm'.

struct Impl {
  void do_stuff() { }
};

S::S():
  impl{ std::make_unique<Impl>() }
{
}

S::~S() { }

void S::do_stuff() {
  impl->do_stuff();
}

Und auf der Importseite:

import m;

int main() {
    S s;
    s.do_stuff();         // OK.
    s.get();              // OK: pointer to incomplete type.
    auto impl = *s.get(); // ill-formed: use of undefined type 'Impl'.
}

Die private Module-Partition ist eine Abstraktionsbarriere, die den Verbraucher des enthaltenden Moduls vor allem schützt, was im Geltungsbereich der privaten Partition definiert ist, und Bibliotheken mit einem einzigen „Header“ effektiv mit besserer Hygiene, verbesserter Kapselung und reduzierter Verwaltung des Build-Systems ermöglicht.

Übersetzung einschließen

Mit der Einführung von Header-Units kommt die Header-Include-Übersetzung, [cpp.include]/7 ermöglicht dem Compiler, #include zu übersetzen Direktiven in import Anweisungen vorausgesetzt, dass der header-name einen importierbaren Header bezeichnet (für MSVC wird eine Header-Unit durch die Verwendung von /headerUnit zu einem importierbaren Header gemacht ). Dieser Schalter kann über C/C++ -> Alle Optionen -> Zusätzliche Optionen aktiviert werden und Hinzufügen von /translateInclude . In zukünftigen Versionen werden Benutzer die Wahl haben, bestimmte Kopfzeilen auszuwählen, die einer Übersetzung unterzogen werden sollen, anstelle eines Alles-oder-Nichts-Schalters.

Modulverknüpfung

C++-Module verlangen mehr vom Toolset als das einfache Parsen (Front-End). C++20 führt eine neue Variante der Verknüpfung ein, „Modulverknüpfung“ [basic.link]/2.2. Ein Proof-of-Concept, bei dem nur Front-End-Namensverfälschung verwendet wird, und die Implementierung der Modulverknüpfung, die während der Ära der Modules Technical Specification (TS) entwickelt wurde, hat sich als unvollkommen und im Maßstab ineffizient erwiesen. Ab Visual Studio 2019 16.8 arbeiten der Compiler und der Linker zusammen, um die Modulverknüpfungssemantik zu erzwingen (ohne die Front-End-Namensverstümmelung). Die neue Linker-Arbeit bedeutet, dass Benutzer Code mit benannten Modulen freier schreiben können, ohne sich um mögliche Probleme mit Namenskollisionen kümmern zu müssen, während sie stärkere Odr-Garantien erhalten, die von keiner anderen Spracheinrichtung geboten werden.

Starke Eigentümerschaft

Das MSVC-Toolset hat auch eine starke Eigentümerschaft übernommen Modell zum Zweck der Programmverknüpfung. Das starke Eigentumsmodell bringt Gewissheit und vermeidet Kollisionen von Verknüpfungsnamen, indem es den Linker ermächtigt, exportierte Entitäten an ihre eigenen Module anzuhängen. Diese Fähigkeit ermöglicht es MSVC, undefiniertes Verhalten auszuschließen, das auf die Verknüpfung verschiedener Module (möglicherweise Revisionen desselben Moduls) zurückzuführen ist, die ähnliche Deklarationen verschiedener Entitäten im selben Programm melden.

Betrachten Sie zum Beispiel das folgende Beispiel, das formal undefiniertes Verhalten ist (in der Praxis):

m.ixx

export module m;
export
int munge(int a, int b) {
    return a + b;
}

n.ixx

export module n;
export
int munge(int a, int b) {
    return a - b;
}

libM.cpp Auch eine Header-Datei, die libm_munge vorwärts deklariert

import m;

int libm_munge(int a, int b) {
    return munge(a, b);
}

main.cpp

#include "libM.h"
import n; // Note: do not import 'm'.
int main() {
    if (munge(1, 2) != -1)
        return 1;
    if (libm_munge(1, 2) != 3) // Note uses Module 'm' version of 'munge'.
        return 1;
}

In der Praxis und im Allgemeinen würde man Code nicht gezielt so schreiben, aber es ist in der Praxis unter Code-Migration, -Evolution und -Wartung schwer zu vermeiden. Vor einer starken Semantik des Modulbesitzes wäre ein Programm wie dieses nicht möglich (mit zwei externen Verknüpfungsnamen von munge ). Starke Eigenverantwortung kauft diese neue odr Garantie. Es gibt ein großartiges Dokument „A Module System for C++“, in dem die Gründe für eine starke Eigentümerschaft detailliert beschrieben werden.

Projektsystem

Der wahrscheinlich wichtigste Teil der Verwendung von C++-Modulen ist ein Build-System, das die Anforderungen von C++-Modulen erfüllen kann und gleichzeitig eine Erfahrung für Benutzer ohne steile Lernkurve bietet. Das VC-Projektsystemteam hat eng mit dem Compiler-Toolset-Team zusammengearbeitet, um Erfahrungen mit der Unterstützung automatischer Module und Header-Units zu sammeln, die den Arbeitsaufwand der Benutzer für deren Einrichtung minimieren.

Die Dateien mit den Erweiterungen .ixx oder .cppm gelten als „Modulschnittstellen“-Quelle. Aber letztendlich wird es von CompileAs gesteuert Eigentum:

Wenn Sie eine Header-Unit für eine .h-Datei erstellen möchten, müssen Sie ihren Elementtyp in „C/C++-Compiler“ ändern, da sich .h-Dateien standardmäßig in der Gruppe „C/C++-Header“ befinden und nicht an die weitergeleitet werden Compiler. „C/C++-Compiler“-Dateien mit der Erweiterung .h werden standardmäßig als „Header-Units“ betrachtet.

Die Projekterstellung durchsucht automatisch die Module- und Header-Unit-Dateien (entsprechend ihrer Compile As-Einstellung) nach anderen Modul- und Header-Unit-Abhängigkeiten im selben Projekt und erstellt sie in der richtigen Abhängigkeitsreihenfolge.

Um auf ein Modul oder eine Header-Unit zu verweisen, die von einem anderen Projekt erstellt wurde, fügen Sie einfach eine Referenz zu diesem Projekt hinzu. Alle „öffentlichen“ Module und Header-Units aus referenzierten Projekten sind automatisch für die Referenzierung im Code verfügbar.

Ein Projekt kann steuern, welche Module und Header (einschließlich der als Header-Einheiten erstellten) als „öffentlich“ betrachtet werden, indem die folgenden Eigenschaften geändert werden:

Dieses kurze Video gibt einen kurzen Einblick in den Arbeitsablauf. Die einzige manuelle Arbeit bestand darin, den C++-Sprachstandard auf /std:c++latest zu setzen .

Compiler-Switch-Überholung

Die experimentelle Phase vieler /module:* Schalter mit Präfix ist vorbei, also haben wir sie unter einem neuen Namen in ein dauerhaftes Zuhause verlegt:

Alt Neu
/module:interface /interface
/module:internalPartition /internalPartition
/module:reference /reference
/module:search /ifcSearchDir
/module:stdIfcDir /stdIfcDir
/module:output /ifcOutput
/module:ifcOnly /ifcOnly
/module:exportHeader /exportHeader
/module:showResolvedHeader /showResolvedHeader
/module:validateChecksum[-] /validateIfcChecksum[-]

Build-Systeme und Benutzer, die das 16.8-Toolset verwenden möchten, sollten die neuen Änderungen beachten.

IntelliSense

Visual C++ wäre nicht … visuell ohne IntelliSense. In 16.8 fügen wir vollständige Unterstützung für die Verwendung von IntelliSense in Modulen hinzu, sowohl zum Schreiben von Modulschnittstellen (.ixx) als auch zum Abrufen von IntelliSense aus importierten Modulen und Header-Einheiten. Die IntelliSense-Unterstützung für importierte Module wird in Vorschau 3 nicht verfügbar sein, aber wir planen, sie in einer kommenden Vorschau zu aktivieren. Bitte bleiben Sie dran für unsere CppCon-Demo, die die IntelliSense-Nutzung beinhalten wird!

Das Toolset-Team hat hart daran gearbeitet, sicherzustellen, dass das vom Compiler ausgegebene C++-Modulformat gut dokumentiert und für die Verwendung in der IntelliSense-Engine stabil ist. MSVC ist für die Erstellung der IFC-Datei verantwortlich, die dann von der IDE verwendet wird. Die Fähigkeit anderer Tools, das MSVC-IFC-Format zu verwenden, ist entscheidend für ein gesundes Ökosystem, und die IntelliSense-Engine, die die IFC-Ausgabe verwendet, ist der erste Schritt in diese Richtung.

Schließung

Wir empfehlen Ihnen dringend, loszugehen und zu versuchen, Visual Studio 2019 mit Modulen zu verwenden. 16.8 Preview 3 wird über die Downloadseite von Visual Studio 2019 verfügbar sein!

Wie immer freuen wir uns über Ihr Feedback. Senden Sie Kommentare per E-Mail an [email protected] oder über Twitter @visualc. Fühlen Sie sich auch frei, mir auf Twitter @starfreakclone zu folgen.

Wenn Sie auf andere Probleme mit MSVC in VS 2019 stoßen, teilen Sie uns dies bitte über die Option „Problem melden“ mit, entweder über das Installationsprogramm oder die Visual Studio-IDE selbst. Für Vorschläge oder Fehlerberichte lassen Sie es uns über DevComm wissen.