従来、C++ではプログラムをヘッダーファイルとソースファイルに分けることが行われてきました。
モジュール時代になると #include
は使わなくなります。分割をしたいときは、モジュールを内部的なモジュールに分割するという形をとることになります。
Module unit
module unit とは、モジュールを構成する翻訳単位です。モジュール宣言を含む翻訳単位は module unit になります。
export module foo;
export
キーワードがある場合、特に module interface unit と言います。ない場合をmodule implementation unitと言います。
ある1つのモジュールについて、 module interface unit となる翻訳単位はただ1つでなければいけません1。そのようなものを primary module interface unit といいます。
module implementation unitの数に制限はありません。
module interface unitは従来のヘッダーファイル、module implementation unitは従来のソースファイルのように使うのが自然だと思われます。
例えば、関数をヘッダーファイルで宣言してソースファイルで実装する場合、そのままmodule interface unitで宣言、module implementation unitで実装というように対応します。
Module partitions
module_name:part_name
の書式でモジュール名をつけ、モジュールを分割できます。
export module foo:part;
このような宣言を含むファイルを module interface partitions と呼びます。
分割したモジュールはそれぞれ独立したモジュールとして振る舞いますが、モジュールが持っているクラスなどのエンティティは共有されます2。
export
をつけない場合、module implementation partitions になります。
module foo:part;
module implementation partitions では、何もエクスポートしてはいけません3。分割したモジュール間ではエクスポートしていなくても宣言を参照できます。
分割したモジュールのインポート
分割したモジュールをインポートするときは、モジュール名を省略します。
module foo;
import :part; // foo:partをインポート
import bar:part; // 文法エラー: 別のモジュールの一部をインポートするのは不可
import foo:part; // 文法エラー: 同じモジュールでも、:の前を書くのは不可
分割したモジュールの再エクスポート
module interface partitions の宣言を外から使えるようにするには、primary module interface unit から再エクスポートする必要があります4。module implementation partitions は再エクスポートできません5。
export module foo;
export import :part; // foo:partをインポートして再エクスポート
import :internal_part; // foo:internal_partをインポート
まとめ
モジュール時代のライブラリの作り方は次のようになっていくと思われます。
従来のファイル構成 | モジュールでの構成 |
---|---|
1ヘッダー | module interface unit |
1ヘッダー+1ソースファイル | (primary) module interface unit + module implementation unit |
複数ヘッダー | primary module interface unit + module interface partition |
複数ヘッダー、複数ソース | primary module interface unit + module implementation unit + module interface/implement partition |
- ヘッダーファイル/ソースファイルという単位の分割はしない
- モジュールを分割することで内部的なものを非公開にする6
Module unitの分類
- 翻訳単位
- Module unit
- Module interface unit
- Primary module interface unit
export module foo;
- Module interface partitions
export module foo:part;
- Primary module interface unit
- Module implementation unit
module foo;
- Module implementation partitions
module foo:part;
- Module implementation partitions
- Module interface unit
- Module unit
Module unitで出来ること
その翻訳単位からエクスポートできるか?
インターフェース | 実装 | |
---|---|---|
モジュールユニット | ✓ | |
モジュールパーティション | ✓ |
その翻訳単位を同一モジュール内でインポートできるか?
インターフェース | 実装 | |
---|---|---|
モジュールユニット | ✓ | |
モジュールパーティション | ✓ | ✓ |
その翻訳単位を別モジュールからインポートできるか?
インターフェース | 実装 | |
---|---|---|
モジュールユニット | ✓ | |
モジュールパーティション |
その翻訳単位を再エクスポートできるか?
インターフェース | 実装 | |
---|---|---|
モジュールユニット | ✓ | |
モジュールパーティション | ✓ |
参考文献
謝辞
この記事はC++20を相談しながら調べる会 #1の結果として書かれました。
-
For a module
foo
, there must be exactly one translation unit whose preamble containsexport module foo;
↩ -
Module interface partitions behave logically like distinct modules, except that they share ownership of contained entities with the module that they form part of. ↩
-
Module implementation partitions cannot contain exported declarations ↩
-
The primary module interface unit for a module is required to transitively import and re-export all of the interface partitions of the module. ↩
-
Module implementation partitions can be imported into the interface of a module, but cannot be exported. ↩
-
分割せずともエクスポートしていないものはC++仕様レベルでは非公開ですが、ソースコード編集後の再コンパイルの掛かりやすさといった仕様外の観点では分割する意味があります。 ↩