C++20 モジュールの概要
2019-06-26
C++ MIX #4
Tetsuro Matsumura
目次
1. モジュール、インポート、
エクスポート
2. モジュール実装
3. モジュールパーティション
4. グローバルモジュール
5. 後方互換性のための機能
6. モジュールリンケージ
7. モジュールのビルド
1
Modules
2
C++20では、ヘッダーファイルに代わる仕組みとしてモジュールが導入され、
プリプロセッサを用いずにプログラムを分割できる。 (P1103R3)
それぞれの場合について、モジュールによる分割へ移行するとどうなるか、
サンプルコードと共に紹介する。
 ヘッダーファイル1つの場合
 ヘッダーファイル1つとソースファイル1つの場合
 複数のヘッダーファイルの場合
 複数のヘッダーファイルとソースファイルの場合
※ モジュールをすべて実装した処理系はまだ無いので動かない可能性があります
ヘッダーファイル1つの場合
3
// datetime.h
#ifndef LIB_DATETIME_H
#define LIB_DATETIME_H
namespace lib {
class Date {
private:
int year=0,month=0,day=0;
public:
// ...
};
}
#endif
// datetime.cpp
export module lib.datetime;
export namespace lib {
class Date {
private:
int year=0,month=0,day=0;
public:
// ...
};
}
// main.cpp
#include "datetime.h“
int main() {
lib::Date cppmix4{2019, 6, 26};
}
// main.cpp
import lib.datetime;
int main() {
lib::Date cppmix4{2019, 6, 26};
}
インポート宣言
モジュール宣言
エクスポート宣言
モジュール宣言とモジュールユニット
4
モジュール宣言は翻訳単位の先頭で一回だけ書くことができる。
export module lib.datetime;
モジュール宣言を含む翻訳単位をモジュールユニットという。
 モジュール名にはドットが使えるが、特別な意味はない。
モジュールユニットの分類
5
モジュール宣言の書式によって4つに分類できる(詳しくは後述)
export module lib.datetime;
モジュールインターフェースユニット
module lib.datetime;
モジュール実装ユニット
export module lib.datetime:date;
モジュールインターフェース
パーティション
module lib.datetime:date;
モジュール実装パーティション
エクスポート宣言
6
モジュールインターフェースユニット内では、宣言をエクスポートできる。
export (宣言)
export { (宣言)… }
エクスポートできる宣言は、変数・関数・型・名前空間・テンプレート・インポートなど。
(構文上は、どんな宣言でもexportは付けられる)
export {…}で囲むと、まとめてエクスポートできる(スコープは作らない)。
インポート宣言
7
インポートした翻訳単位でエクスポートしている宣言は見えるようになる。
 マクロは取り込まれない
 using namespaceは取り込まれない (using namespace std し放題)
// main.cpp
import lib.datetime;
int main() {
lib::Date cppmix4{2019, 6, 26};
}
インポート宣言によってモジュールインターフェースユニットをインポートする。
8
補足: 「見える」と「到達可能」およびODRの緩和
宣言が見える(visible)とは、その宣言が名前探索で見つかることをいう。
宣言が到達可能(reachable)とは、その宣言の意味論的な性質(semantic property)が使え
ることをいう。
• 例えば、クラス定義はクラスの完全性という性質があり、クラス定義に到達可能なとき、
クラスは完全である。
モジュールインターフェースユニットをインポートすると、
すべての宣言が到達可能となり、エクスポートしている宣言は見えるようになる。
このように、見える宣言は到達可能であるが、逆は成立しない。
C++20では定義が複数あっても、同時に到達可能にならなければODR違反ではない。
(注:すべての定義は宣言である)
再エクスポート
9
このようなモジュールをインポートすると、
再エクスポートしているモジュールも同時にインポートする。
export module lib;
export import lib.datetime;
インポート宣言をエクスポートすることを再エクスポートという。
ヘッダーファイル1つとソースファイル1つの場合
10
// datetime.h
#ifndef LIB_DATETIME_H
#define LIB_DATETIME_H
class Date {
private:
int year=0,month=0,day=0;
public:
Date() = default;
Date(int year, int month, int day);
};
#endif
// datetime.cpp
export module lib.datetime;
export class Date {
private:
int year=0,month=0,day=0;
public:
Date() = default;
Date(int year, int month, int day);
};
// datetime_impl.cpp
module lib.datetime;
Date::Date(int y, int m, int d)
: year(y)
, month(m)
, day(d)
{}
// datetime.cpp
#include "datetime.h"
Date::Date(int y, int m, int d)
: year(y)
, month(m)
, day(d)
{}
インターフェースと実装
11
モジュールはインターフェースと実装に分けることができる。
モジュール実装ユニットは、モジュールインターフェースを暗黙的にインポートする。
モジュールインターフェースユニットはモジュールにつきただ1つ存在する。
モジュール実装ユニットの個数は任意。
モジュール実装ユニットではエクスポートができない。
export module lib.datetime;
モジュールインターフェースユニット
→ ヘッダーファイルに相当
export可
module lib.datetime;
モジュール実装ユニット
→ ソースファイルに相当
export不可
module lib.datetime;
module lib.datetime;
複数のヘッダーファイルの場合
12
// date.h
#ifndef LIB_DATE_H
#define LIB_DATE_H
class Date {
private:
int year=0,month=0,day=0;
public:
// ...
};
#endif
// date.cpp
export module lib.datetime:date;
export class Date {
private:
int year=0,month=0,day=0;
public:
// ...
};
// datetime.h
#include "date.h"
#include "time.h"
// datetime.cpp
export module lib.datetime;
export import :date;
export import :time;
モジュールパーティション
13
モジュールは部分的なモジュール(パーティション)に分けることができる。
モジュールパーティションは別のモジュールからインポートできず、
同一モジュール内でのみインポートできる。
 このとき、エクスポートしていない宣言も見えるようになる。
export module lib.datetime;
export import :date;
(プライマリー)
モジュールインターフェースユニット
export module lib.datetime:date;
モジュールインターフェース
パーティション
14
補足: パーティションの再エクスポート
プライマリーモジュールインターフェースユニットは、すべてのモジュールインター
フェースパーティションを再エクスポートしなければならない。そうでない場合はill-
formedだが、診断はされない。
これにより、モジュールインターフェースパーティションでエクスポートした宣言は、外
部からも見えるようになる。
複数のヘッダーファイルとソースファイルの場合
15
// date.h
#ifndef LIB_DATE_H
#define LIB_DATE_H
class Date {
private:
int year=0,month=0,day=0;
public:
// ...
};
#endif
// date.cpp
export module lib.datetime:date;
export class Date {
private:
int year=0,month=0,day=0;
public:
// ...
};
// datetime.h
#include "date.h"
#include "time.h"
// datetime.cpp
export module lib.datetime;
export import :date;
export import :time;
// date.cpp
#include "date.h"
// ...
// date_impl.cpp
module lib.datetime:date_impl;
// ...
モジュール実装パーティション
16
モジュールパーティションかつモジュール実装でもある翻訳単位も作れる。
モジュール実装パーティションは再エクスポートできない。
export module lib.datetime:date;
モジュールインターフェース
パーティション
export可
module lib.datetime:date;
モジュール実装パーティション
export不可
モジュール内外の依存関係
17
export module lib.datetime;
(プライマリー)モジュールインターフェースユニット
module lib.datetime;
モジュール実装ユニット
export module lib.datetime:date;
モジュールインターフェース
パーティション
module lib.datetime:impl;
モジュール実装パーティション
暗黙的インポート
インポート
インポート・
再エクスポート
外部からのインポート
パーティションはいつ使う?
18
モジュールパーティションの存在は外部からはわからないので、
 ソースファイルが長いので分けたい場合
 ユーザーにモジュールが分かれていることを意識させたくない場合
単独でも使用できるなら、独立したモジュールにしたほうが良い。
その他の機能の紹介
19
グローバルモジュール
20
名前のあるモジュールに属していない宣言は、グローバルモジュールに属している。
グローバルモジュールは名前が無く、インポートできない。
グローバルモジュールではエクスポート宣言はできない。
// datetime.cpp
export module lib.datetime;
// ...
// main.cpp
import lib.datetime;
int main() {
lib::Date cppmix4{2019, 6, 26};
}
lib.datetimeモジュール
グローバルモジュール
後方互換性のための機能
21
従来のヘッダーファイルによって提供されるライブラリのための機能が追加された。
 グローバルモジュールフラグメント
モジュール本体への影響が少ないようにインクルードできる機能
 ヘッダーユニット
ヘッダーファイルをモジュールとしてインポートできる機能
(一部のヘッダーファイルのみ)
グローバルモジュールフラグメント
22
モジュール宣言の前にグローバルモジュールの実装を書ける機能。
グローバルモジュールフラグメント内の宣言は、後続のモジュールには属さない。
グローバルモジュールフラグメントでは、
プリプロセッサディレクティブ以外は書くことができない。
module;
#include <iostream>
export module foo;
#include "lib.h"
void f() { std::cout << "foo" << std::endl; }
グローバル
モジュール
fooモジュール
ヘッダーユニット
23
特定のヘッダーファイルをモジュールとしてインポートできる機能。
効果は#includeとほぼ同じ。
どのヘッダーファイルをインポートできるのかは処理系定義である。
インポートできるヘッダーファイルを#includeすると、importに置き換わる。
export module foo;
import <iostream>;
void f() { std::cout << "foo" << std::endl; }
モジュールリンケージ
24
C++20では、新たにモジュールリンケージが追加された。
(グローバルモジュール以外の)モジュール内で
エクスポートしていない名前はモジュールリンケージを持つ。
エクスポートしている名前は外部リンケージを持つ。
モジュールリンケージを持つ名前は同一モジュール内で参照できる。
モジュールのビルド
25
モジュールのビルドの仕方 (※処理系定義)
26
一般に、モジュールは依存関係の順にコンパイルしなければならない。
#include と違って、どのソースファイルからでもコンパイルできるわけではない。
コンパイルしたモジュールは何らかの中間表現で保存され、再利用される。
モジュールをインポートする前にコンパイル済みモジュールを生成する必要がある。
モジュールは #include を置き換える仕組みであり、
(DLLなどの意味で)ライブラリの分割には関与しない。
ただし、モジュールをライブラリ分割にも活用するような処理系はありえる。
(例: MSVC)
モジュールインターフェースユニットで dllexport すると、
インポートしたとき dllimport になる
27
補足: モジュールのコンパイル条件が違う場合
モジュールは事前にコンパイルしておく必要があるので、
プリプロセスの結果は事前コンパイル時の結果に固定される。
(処理系依存)
処理系によっては、モジュールをコンパイルしたときのオプションがコンパイル済みモ
ジュールに記録され、異なるオプションでコンパイルしているときはインポートできない。
(例: MSVC)
このような処理系では、コンパイル済みモジュールをオプション別のディレクトリに格納
し、パスを切り替えるなどの運用が必要。
モジュールはプリコンパイルドヘッダーを標準化したものではないが、
処理系の実装上は似たようなものになっていることがある。
28
補足: モジュールでビルドは速くなるか
モジュールによってビルドが速くなる要因は次の通り。
• #includeと違ってコードが展開されないので、解析が速くなる
一方、ビルドが遅くなる要因もある。
• モジュールの依存関係順にコンパイルする必要があるので、並列化しにくくなる
実際はどちらが効くかによって変わる。P1441R1 でベンチマークが行われている。
• ジョブ数が増えるに従ってモジュールの方が遅くなるが、一般的なPCではモジュールの
方が速いと考えて問題なさそうである。
29
まとめ
 #includeに代わる仕組みとしてモジュールが導入された
 プリプロセッサを用いずにプログラムを分割できる
 ファイル構成はそれほど変わらない
ヘッダーファイル ≃ モジュールインターフェースユニット
ソースファイル ≃ モジュール実装ユニット

More Related Content

PDF
続・モジュール / Introduction to C++ modules (part 2)
PPTX
C# 8.0 非同期ストリーム
PDF
コンテナの作り方「Dockerは裏方で何をしているのか?」
PDF
いまさら聞けないarmを使ったNEONの基礎と活用事例
PDF
UE4とUnrealC++について
PPTX
Effective Modern C++ 勉強会 Item 22
PDF
PDF
今日からできる!簡単 .NET 高速化 Tips
続・モジュール / Introduction to C++ modules (part 2)
C# 8.0 非同期ストリーム
コンテナの作り方「Dockerは裏方で何をしているのか?」
いまさら聞けないarmを使ったNEONの基礎と活用事例
UE4とUnrealC++について
Effective Modern C++ 勉強会 Item 22
今日からできる!簡単 .NET 高速化 Tips

What's hot (20)

PDF
『FINAL FANTASY VII REMAKE』におけるプロファイリングと最適化事例 UNREAL FEST EXTREME 2021 SUMMER
PDF
Building the Game Server both API and Realtime via c#
PDF
C++の話(本当にあった怖い話)
PDF
ヒストリア HelixCore(Perforce) 運用レギュレーションドキュメント
PDF
Docker Compose 徹底解説
PDF
UE4 MultiPlayer Online Deep Dive 実践編2 (ソレイユ株式会社様ご講演) #UE4DD
PDF
CPU / GPU高速化セミナー!性能モデルの理論と実践:実践編
PDF
C++ マルチスレッドプログラミング
PDF
constexpr関数はコンパイル時処理。これはいい。実行時が霞んで見える。cpuの嬌声が聞こえてきそうだ
PDF
コルーチンでC++でも楽々ゲーム作成!
PDF
ARM CPUにおけるSIMDを用いた高速計算入門
PDF
規格書で読むC++11のスレッド
PDF
emscriptenでC/C++プログラムをwebブラウザから使うまでの難所攻略
PDF
CEDEC 2018 最速のC#の書き方 - C#大統一理論へ向けて性能的課題を払拭する
PDF
Xbyakの紹介とその周辺
PDF
Reactive extensions入門v0.1
PDF
C++ マルチスレッド 入門
PDF
コンテナ未経験新人が学ぶコンテナ技術入門
PDF
【Unite Tokyo 2019】Unityだったら簡単!マルチプレイ用ゲームサーバ開発 ~実践編~
PDF
GPU最適化入門
『FINAL FANTASY VII REMAKE』におけるプロファイリングと最適化事例 UNREAL FEST EXTREME 2021 SUMMER
Building the Game Server both API and Realtime via c#
C++の話(本当にあった怖い話)
ヒストリア HelixCore(Perforce) 運用レギュレーションドキュメント
Docker Compose 徹底解説
UE4 MultiPlayer Online Deep Dive 実践編2 (ソレイユ株式会社様ご講演) #UE4DD
CPU / GPU高速化セミナー!性能モデルの理論と実践:実践編
C++ マルチスレッドプログラミング
constexpr関数はコンパイル時処理。これはいい。実行時が霞んで見える。cpuの嬌声が聞こえてきそうだ
コルーチンでC++でも楽々ゲーム作成!
ARM CPUにおけるSIMDを用いた高速計算入門
規格書で読むC++11のスレッド
emscriptenでC/C++プログラムをwebブラウザから使うまでの難所攻略
CEDEC 2018 最速のC#の書き方 - C#大統一理論へ向けて性能的課題を払拭する
Xbyakの紹介とその周辺
Reactive extensions入門v0.1
C++ マルチスレッド 入門
コンテナ未経験新人が学ぶコンテナ技術入門
【Unite Tokyo 2019】Unityだったら簡単!マルチプレイ用ゲームサーバ開発 ~実践編~
GPU最適化入門
Ad

C++20 モジュールの概要 / Introduction to C++ modules (part 1)