C++ Modulesの分割

従来、C++ではプログラムをヘッダーファイルとソースファイルに分けることが行われてきました。

モジュール時代になると `#include` は使わなくなります。分割をしたいときは、モジュールを内部的なモジュールに分割するという形をとることになります。

# Module unit

*module unit* とは、モジュールを構成する翻訳単位です。モジュール宣言を含む翻訳単位は module unit になります。

```cpp:モジュール宣言
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で実装というように対応します。

[^1]: For a module `foo`, there must be exactly one translation unit whose preamble contains `export module foo;`

# Module partitions

`module_name:part_name` の書式でモジュール名をつけ、モジュールを分割できます。

```cpp:module&nbsp;interface&nbsp;partitionsの宣言
export module foo:part;
```

このような宣言を含むファイルを *module interface partitions* と呼びます。

分割したモジュールはそれぞれ独立したモジュールとして振る舞いますが、モジュールが持っているクラスなどのエンティティは共有されます[^2]。

[^2]: Module interface partitions behave logically like distinct modules, except that they share ownership of contained entities with the module that they form part of.

`export` をつけない場合、*module implementation partitions* になります。

```cpp:module&nbsp;implementation&nbsp;partitionsの宣言
module foo:part;
```

module implementation partitions では、何もエクスポートしてはいけません[^3]。分割したモジュール間ではエクスポートしていなくても宣言を参照できます。

[^3]: Module implementation partitions cannot contain exported declarations

# 分割したモジュールのインポート

分割したモジュールをインポートするときは、モジュール名を省略します。

```cpp
module foo;
import :part;    // foo:partをインポート
import bar:part; // 文法エラー: 別のモジュールの一部をインポートするのは不可
import foo:part; // 文法エラー: 同じモジュールでも、:の前を書くのは不可
```

# 分割したモジュールの再エクスポート

module **interface** partitions の宣言を外から使えるようにするには、primary module interface unit から再エクスポートする必要があります[^4]。module **implementation** partitions は再エクスポートできません[^5]。

[^4]: The primary module interface unit for a module is required to transitively import and re-export all of the interface partitions of the module.

[^5]: Module implementation partitions can be imported into the interface of a module, but cannot be exported.

```cpp
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]

[^6]: 分割せずともエクスポートしていないものはC++仕様レベルでは非公開ですが、ソースコード編集後の再コンパイルの掛かりやすさといった仕様外の観点では分割する意味があります。

## Module unitの分類

* 翻訳単位
    * Module unit
        * Module interface unit  
            * Primary module interface unit  
              `export module foo;`
            * Module interface partitions  
              `export module foo:part;`
        * Module implementation unit  
          `module foo;`
            * Module implementation partitions  
              `module foo:part;`

## Module unitで出来ること

### その翻訳単位からエクスポートできるか？

|    |インターフェース|実装|
|--------------|----|----|
|モジュールユニット    |✓| |
|モジュールパーティション|✓| |

### その翻訳単位を同一モジュール内でインポートできるか？

|    |インターフェース|実装|
|--------------|----|----|
|モジュールユニット    |✓| |
|モジュールパーティション|✓|✓|

### その翻訳単位を別モジュールからインポートできるか？

|    |インターフェース|実装|
|--------------|----|----|
|モジュールユニット    |✓| |
|モジュールパーティション|  |  |

### その翻訳単位を再エクスポートできるか？

|    |インターフェース|実装|
|--------------|----|----|
|モジュールユニット    |✓| |
|モジュールパーティション|✓|  |

# 参考文献

* [P1103R3 Merging Modules](http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2019/p1103r3.pdf)

# 謝辞

この記事は[C++20を相談しながら調べる会 #1](https://cpp20survey.connpass.com/event/124051/)の結果として書かれました。