C++20: Module strukturieren
Wenn das Modul größer wird, sollte es in handliche Komponenten aufgeteilt werden. C++20 bietet dafür zwei Möglichkeiten an: Submodule und Partitionen.
Wenn das Modul größer wird, sollte es in handliche Komponenten aufgeteilt werden. C++20 bietet dafür zwei Möglichkeiten an: Submodule und Partitionen. In diesem Artikel schaue ich mir beide Optionen genauer an.
Diesem Artikel möchte ich noch eine kurze Anmerkung vorausschicken. Der Einfachheit halber werde ich die Trennung von Module Interface Unit und Module Implementation Unit ignorieren. Das heißt, meine Module werden aus einer Datei bestehen. Zusätzlich werde ich keine Namensräume verwenden. Ich habe beide Features bereits in dem vorherigen Artikel: "C++20: Module Interface Unit und Module Implemenation Unit [1]" vorgestellt.
Submodule sind einfach umzusetzen. Daher werde ich mit ihnen beginnen.

Submodule
Ein Modul kann Module importieren und diese wieder zurückexportieren. Im folgenden Beispiel importiert das Modul math
die Submodule math.math
1 und math.math2
.
- Modul
math
// mathModule.ixx
export module math;
export import math.math1;
export import math.math2;
Der Ausdruck export import math.math1
importiert das Modul math.math1
und exportiert es als Bestandteil des Moduls math
zurück.
Der Vollständigkeit halber sind hier die Module math.math1
und math.math2
. Ich verwende einen Punkt, um das Modul von seinen Submodulen zu trennen. Er ist aber nicht notwendig.
- Submodul
math.math1
// mathModule1.ixx
export module math.math1; // (1)
export int add(int fir, int sec) { // (2)
return fir + sec;
}
- Submodul
math.math2
// mathModule2.ixx
export module math.math2; // (1)
export { // (2)
int mul(int fir, int sec) {
return fir * sec;
}
}
Wenn du die Submodule sorgfältig studierst, wirst du einen kleinen Unterschied zwischen der export
-Anweisung (2) in den Modulen math.math1
und math.math2
feststellen. math.math1
verwendet einen export
-Spezifizierer und math.math2
ein sogenannte export
-Gruppe oder export
-Block.
Aus der Sicht der Anwender ist die Verwendung des Moduls math
einfach.
- Client-Programm
// mathModuleClient.cpp
import std.core;
import math;
int main() {
std::cout << std::endl;
std::cout << "add(3, 4): " << add(3, 4) << std::endl;
std::cout << "mul(3, 4): " << mul(3, 4) << std::endl;
}
Das Kompilieren, Linken und Ausführen des Programms lässt sich mit der Microsoft-Implementierung von Modulen wie gewohnt umsetzen:
cl.exe /std:c++latest /c /experimental:module mathModule1.ixx /EHsc /MD // (3)
cl.exe /std:c++latest /c /experimental:module mathModule2.ixx /EHsc /MD // (3)
cl.exe /std:c++latest /c /experimental:module mathModule.ixx /EHsc /MD // (3)
cl.exe /std:c++latest /experimental:module mathModuleClient.cpp mathModule1.obj mathModule2.obj mathModule.obj /EHsc /MD // (4)

Jeder Kompilierschritt (3) erzeugt zwei Artefakte: Die IFC-Datei (interface file) *.ifc
, die implizit in (4) verwendet wird, und die *.obj
-Datei, die explizit in (4) eingesetzt wird.
Ich habe bereits geschrieben, dass ein Submodul lediglich ein Modul ist. Jedes Submodul besitzt eine Modul-Deklaration (1). Konsequenterweise kann ich ein zweites Client-Programm implementieren, das nur das Modul math.math1
benötigt.
- Zweites Client-Programm
// mathModuleClient1.cpp
import std.core;
import math.math1;
int main() {
std::cout << std::endl;
std::cout << "add(3, 4): " << add(3, 4) << std::endl;
}
Für dieses Programm ist es ausreichend, das neue Client-Programm zu kompilieren und zu linken. Das existierende Modul math.math1
lässt sich direkt dafür verwenden:
cl.exe /std:c++latest /experimental:module mathModuleClient1.cpp mathModule1.obj /EHsc /MD

Die Trennung von Modulen in Module und Submodule ist ein einfaches Mittel für Moduldesigner, Anwendern die Möglichkeit zu geben, die Module feingranular zu importieren. Diese Beobachtung gilt aber nicht für Modul-Partitionen.
Modul-Partitionen
Ein Modul lässt sich in Partitionen aufteilen. Jede Partition besteht aus einem Module Interface Unit (partition interface file) und keiner oder mehrerer Module Implementation Units (C++20: Module Interface Unit und Module Implementation Unit [2]). Die Namen, die die Partitionen exportieren, werden durch das primäre Module Interface (primary modul interface oder primare interface file) importiert und zurückexportiert. Der Name einer Partition muss mit dem Namen des Moduls beginnen. Eine Partition kann nicht selbstständig existieren.
Leider ist die Beschreibung einer Modul-Partition deutlich komplizierter als ihre Umsetzung. In den folgenden Zeilen werde ich das Modul math
und seine Submodule math.math1
und math.math2
in Modul-Partitionen transformieren. Bei diesem einfachen Vorgang verwende ich die soeben eingeführten Begriffe der Modul-Partition.
- Das primäre Module Interface
mathPartition.ixx
// mathPartition.ixx
export module math; // (1)
export import :math1; // (2)
export import :math2; // (2)
Das primäre Module Interface besteht aus der Modul-Deklaration (1). Es importiert und exportiert die Paritionen math1
und math2
mithilfe der Doppelpunkte zurück. Der Name der Partition muss mit dem Namen des Moduls beginnen. Konsequenterweise ist dieser daher im Ausdruck (2) nicht anzugeben.
- Modul-Partionen (
mathPartition1.ixx
undmathPartition2.ixx
)
export module math:math1; // (1)
export int add(int fir, int sec) {
return fir + sec;
}
// mathPartition2.ixx
export module math:math2; // (1)
export {
int mul(int fir, int sec) {
return fir * sec;
}
}
Analog zur Modul-Deklaration erklärt (1) eine sogenannte Module Interface Partition. Sie ist auch eine Module Interface Unit. Der Name math
steht für den Namen des Moduls und die Namen math1
und math2
stehen für die der Partition.
- Client-Programm
// mathModuleClient.cpp
import std.core;
import math;
int main() {
std::cout << std::endl;
std::cout << "add(3, 4): " << add(3, 4) << std::endl;
std::cout << "mul(3, 4): " << mul(3, 4) << std::endl;
}
Das Client-Programm ist identisch mit dem, das ich für Submodule verwendet habe. Die gleiche Aussage gilt für die Erzeugung des ausführbaren Programms:
cl.exe /std:c++latest /c /experimental:module mathPartition1.ixx /EHsc /MD
cl.exe /std:c++latest /c /experimental:module mathPartition2.ixx /EHsc /MD
cl.exe /std:c++latest /c /experimental:module mathPartition.ixx /EHsc /MD
cl.exe /std:c++latest /experimental:module mathModuleClient.cpp mathPartition1.obj mathPartition2.obj mathPartition.obj /EHsc /MD

Wie geht's weiter?
Module in C++20 haben noch mehr zu bieten. So führen sie zum Beispiel "Header Units" ein und unterscheiden zwischen dem globalen und privaten Modul-Fragment. Zuletzt möchte ich auf das Linken des Programms eingehen.
C++-Schulungen
Ich freue mich darauf, meine C++-Schulung anzubieten zu können.
Online-Schulung (Deutsch):
Präsenz-Schulung (Deutsch):
Online Seminars (English):
- C++11 and C+14: 13 July 2020 - 17 July 2020 (5 * 1/2 day) [5]
- Clean Code with modern C++: 03 August 2020 - 07 August 2020 (5 * 1/2 day) [6]
Mehr Informationen zu meinen Schulungen gibt es auf meiner deutschen (www.ModernesCpp.de [7]) oder englischen Schulungsseite (www.ModerenesCpp.net [8]).
Ich habe die Preise meine Online-Schulungen während der Corona-Krise deutlich reduziert. Wem der Preis noch zu hoch ist, der kann direkt mit mir (schulung@ModernesCpp.de [9]) Kontakt aufnehmen. Dies Angebot gilt natürlich auch für Firmen.
( [10])
URL dieses Artikels:
https://www.heise.de/-4770234
Links in diesem Artikel:
[1] https://heise.de/-4727382
[2] https://heise.de/-4727382
[3] https://www.modernescpp.de/index.php/c/2-c/26-embedded-programmierung-mit-modernem-c-online
[4] https://www.modernescpp.de/index.php/c/2-c/24-embedded-programmierung-mit-modernem-c20200306121340
[5] https://www.modernescpp.net/index.php/c/2-c/30-c-11-and-c-14
[6] https://www.modernescpp.net/index.php/c/2-c/28-clean-code-with-modern-c
[7] https://www.modernescpp.de/
[8] https://www.modernescpp.net/
[9] mailto:schulung@ModernesCpp.de
[10] mailto:rainer@grimm-jaud.de
Copyright © 2020 Heise Medien