Ist eine Anwendung umfangreicher, wird sie in logische Blöcke unterteilt, z.B. ein Block für die Eingaben, einen für Berechnungen und einen für die Ausgaben. Jeder dieser Blöcke besteht aus einem Implementierungsteil, der Quellcode-Datei .cpp, und dem Schnittstellenteil, der Header-Datei .h. Der Schnittstellenteil enthält nur die Deklarationen der zu veröffentlichen globalen Funktionen sowie die extern-Deklarationen der zu veröffentlichen Daten.
Das nachfolgende Beispiel zeigt anhand der kleinen Bibliothek bibl die Aufteilung des Codes in den Schnittstellenteil und Implementierungsteil.
// Datei bibl.h
// Schnittstellen der Bibliothek bibl.h
// Zu veroeffentliche Daten und Funktionen
extern const char *const pText;
float CircleArea(float rad);
// Datei bibl.cpp
// Implementierung der Bibliothek bibl.cpp
#include "bibl.h"
// Globales const-Datum initialisieren
const char *const pText = "bibl";
// Lokale Konstante
constexpr float PI = 3.1416f;
// Lokale Funktion
float Quad(float val)
{
return val * val;
}
// Globale Funktion
float CircleArea(float rad)
{
return PI * Quad(rad);
}
// Datei main.cpp
// Anwendung
#include <iostream>
#include <format>
#include "bibl.h" // Bibliotheks-Schnittstellen einbinden
int main()
{
// Bibliotheksname ausgeben
std::cout << std::format("Bibliothek: {}\n",pText);
// Kreisflaeche berechnen
float radius = 2.2f;
std::cout << std::format("Kreisradius: {}, ergibt Kreisflaeche: {}\n", radius, CircleArea(radius));
}
Einer der Nachteile der Aufteilung des Codes in einen Schnittstellenteil und Implementierungsteil ist, dass bei Änderungen an den Schnittstellen zwei Dateien anzupassen sind.
Des Weiteren werden die Schnittstellendateien beim Einbinden in eine Quellcode-Datei jedes Mal neu vom Compiler übersetzt und dies kann bei umfangreichen Schnittstellendateien erhebliche Zeit beanspruchen. Um u.a. diese Nachteile zu umgehen, wurden mit C++20 die sogenannten Module eingeführt.
Ein Modul kann sowohl den Schnittstellenteil wie auch Implementierungsteil in einer Datei enthalten. Eine Modul-Datei sieht zunächst aus wie eine 'normale' Quellcode-Datei. Erst durch die die Anweisung
export module ModName;
wird daraus eine Modul-Datei, wobei ModName der frei wählbare Name des Moduls ist. Vor dieser Anweisung dürfen nur Kommentare und die Definition des globalen Moduls (wird gleich noch erläutert) stehen.
Damit der Compiler weiß, welche Daten und Funktionen aus der Modul-Datei zu veröffentlichen sind, wird vor dem Datentyp des Datums bzw. vor dem Returntyp der Funktion das Schlüsselwort export gestellt.
Die kleine Bibliothek als Modul würde damit wie folgt aussehen:
// Implementierung der Bibliothek bibl.cxx als Modul
export module Bibl;
// Zu veroeffentliches const-Datum initialisieren
export const char *pText = "bibl";
// Nicht zu veroeffentliche Konstante
constexpr float PI = 3.1416f;
// Lokale Funktion
float Quad(float val)
{
return val * val;
}
// Zu veroeffentliche Funktion
export float CircleArea(float rad)
{
return PI * Quad(rad);
}
Modul-Dateien müssen vor den sonstigen Quellcode-Dateien vom Compiler übersetzt werden, damit er die zu exportierenden Namen extrahieren kann. Je nach Compiler erfolgt dies bis zum jetzigen Zeitpunkt unterschiedlich:
Im Projektmappen-Explorer die Eigenschaften der Modul-Datei öffnen. Anschließend auf die Eigenschaft C/C++ – Erweitert klicken und auf der rechten Seite unter Kompilierungsart die Option Als C++-Modulcode kompilieren auswählen.
Hier sind keine weiteren Einstellungen notwendig, da diese bereits bei der Konfiguration des Compilers unter CodeBlocks entsprechend vorgenommen wurden. Sie müssen nur explizit im Workspace durch Auswahl der Option Build file die Modul-Datei vor der Quellcode-Datei übersetzen.
Um auf die exportierten Daten und Funktionen aus einer Modul-Datei zuzugreifen, ist das Modul mit der Anweisung
import ModName;
zu importieren.
// Anwendung
#include <iostream>
#include <format>
// Modul einbinden
import Bibl;
int main()
{
// Bibliotheksname ausgeben
std::cout << std::format("Bibliothek: {}\n",pText);
// Kreisflaeche berechnen
float radius = 2.2f;
std::cout << std::format("Kreisradius: {}, ergibt Kreisflaeche: {}\n", radius, CircleArea(radius));
}
Stehen in einer Modul-Datei #include-Anweisungen, müssen diese im globalen Modul eingebunden werden. Das globale Modul wird durch die Anweisung
module;
definiert und muss vor der export module Anweisung stehen. Im globalen Modul dürfen nur #include-Anweisungen und Kommentare stehen.
// Globales Modul mit include-Anweisungen
module;
#include <cmath>
// Implementierung der Bibliothek bibl.cxx als Modul
export module Bibl;
// Lokale Konstanten
const float GConst = 9.81f; // G-Konstante
const float DEG2RAD = 2.f * 3.1416f / 360.f; // Umrechnung Deg->Rad
// Wurfweite = (Geschwindigkeit)^2 * sin(2*Winkel) / G-Konstante
export float Distance(float velocity, float angel)
{
float res = velocity * velocity * std::sin(2.0f*angel*DEG2RAD) / GConst; return res;
}