C++
C++20

C++ Modulesの分割

従来、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 の書式でモジュール名をつけ、モジュールを分割できます。


module interface partitionsの宣言

export module foo:part;


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

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

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


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;



      • Module implementation unit

        module foo;


        • Module implementation partitions

          module foo:part;








参考文献


謝辞

この記事はC++20を相談しながら調べる会 #1の結果として書かれました。





  1. For a module foo, there must be exactly one translation unit whose preamble contains export module foo; 



  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. 



  3. Module implementation partitions cannot contain exported declarations 



  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. 



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