C++20:モジュール インターフェイス ユニットとモジュール実装ユニット

モジュール インターフェイス ユニットとモジュール実装ユニットのおかげで、モジュールを定義するときにインターフェイスを実装から分離できます。その方法をお見せしましょう。

前回の投稿 C++20:A Simple math Modul で約束したように、私の Clang Odyssee について少し寄り道したいと思います。私の回り道は、参照された投稿で私が書いたすべてのコンパクトな復習です.

マイ クラン オデッセイ

まず、私以外の誰かを責めたくありません。 CppCon 2017 での Boris Kolpackov の「Building C++ Modules」または Corentin Jabot の「モジュールはツールの機会ではない」という講演に基づいて、ベンダーはモジュール定義に次の拡張機能を提案しているという印象を受けました。

  • Windows:ixx
  • Clang:cppm
  • GCC:提案なし

Clang コンパイラの場合、私は完全に間違っていました。これは私の単純な数学モジュールで、Clang コンパイラでコンパイルしようとしました。

// math.cppm

export module math;

export int add(int fir, int sec){
 return fir + sec;
} 

Microsoft と Linux で Clang 9 と Clang 10 を使用してモジュールをコンパイルしようとしました。ソースからビルドされた最新の Clang 11 コンパイラでコンパイルしようとしました。これが私の多くの試みの1つです。

このコマンドラインは、モジュール math.pcm を作成する必要があります。コマンドラインで指定した -std=c++20 -fmodules-ts エラーメッセージは次のように述べています:モジュールインターフェイスのコンパイルには「-std =c ++ 20」または「-fmodules-ts」が必要です . 2 つのフラグのすべてのバリエーションを作成し、グローバル モジュール フラグメントをモジュール定義に追加し、追加のフラグで Clang コンパイラを呼び出しましたが、結果は常に同じでした。

それから、アーサー・オドワイヤーとローランド・ボックに助けを求めました。 Arthur モジュールの場合、Clang で問題なく動作しました:「Hello World with C++2a modules」。 Roland は Clang 11 を再構築し、私のモジュール定義で動作しました。

Roland と私は文字通り、同じ Clang コンパイラと同じモジュール定義を持っていました。キャラクターごとに、彼のコマンドラインと私のコマンドラインを比較したところ、何かに気づきました.

Mine: clang++ -std=c++20 - -fmodules-ts -stdlib=libc++ -c math.cppm -Xclang -emit-module-interface -o math.pcm
Roland: clang++ -std=c++20 - -fmodules-ts -stdlib=libc++ -c math.cpp -Xclang -emit-module-interface -o math.pcm

Roland は彼のモジュールに math.cpp cpp を与え、Arthur もそうしました。モジュール定義に接尾辞 cppm を与えないでください .

これで、モジュールのコンパイルと使用は簡単になりました。

このオデッセイを締めくくるには、ここに client.cpp ファイルと、Clang コマンド ラインに必要なフラグをいくつか示します。

// client.cpp

import math;

int main() {
 
 add(2000, 20);
 
}

clang++ -std=c++2a -stdlib=libc++ -c math.cpp -Xclang -emit-module-interface -o math.pcm // (1)
clang++ -std=c++2a -stdlib=libc++ -fprebuilt-module-path=. client.cpp math.pcm -o client // (2)

<オール>
  • モジュール math.pcm を作成します。接尾辞 pcm はプリコンパイル済みモジュールを表します。プリコンパイル済みモジュールを作成するには、フラグの組み合わせ -Xclang -emit-module-interface が必要です。
  • モジュール math.pcm を使用する実行可能なクライアントを作成します。フラグ -fprebuilt-module-path を使用して、モジュールへのパスを指定する必要があります。
  • モジュールの計算は簡単でした。もう少し洗練してみましょう。

    モジュール構造のガイドライン

    モジュール構造の最初のガイドラインは次のとおりです:

    module; // global module fragment
    
    #include <headers for libraries not modularized so far>
    
    export module math; // module declartion 
    
    import <importing of other modules> 
    
    <non-exported declarations> // names with only visibiliy inside the module
    
    export namespace math {
    
     <exported declarations> // exported names 
    
    }
    

    このガイドラインには 2 つの目的があります。これにより、モジュールの単純化された構造と、これから書く内容についてのアイデアが得られます。では、このモジュール構造の新機能は何ですか?

    • モジュールをインポートできます。インポートされたモジュールにはモジュール リンケージがあり、モジュールの外では見えません。この観察は、非輸出申告にも適用されます。
    • エクスポートされた名前を、モジュールなどの同じ名前を持つ名前空間 math に入れます。
    • モジュールには宣言された名前しかありません。インターフェイスの分離とモジュールの実装について書きましょう。

    モジュール インターフェイス ユニット、およびモジュール実装ユニット

    前述のガイドラインに従って、前回の投稿 C++20:A Simple math Modul のモジュール math の最終バージョンをリファクタリングしたいと思います。

    モジュール インターフェース ユニット

    // mathInterfaceUnit.ixx
    
    module; 
    
    import std.core; 
    
    export module math; 
    
    export namespace math {
    
     int add(int fir, int sec);
     
     int getProduct(const std::vector<int>& vec);
    
    }
    

    • モジュール インターフェイス ユニットには、エクスポート モジュール宣言が含まれています:export module math.
    • add と getProduct という名前がエクスポートされます。
    • モジュールは、モジュール インターフェース ユニットを 1 つだけ持つことができます。

    モジュール実装ユニット

    // mathImplementationUnit.cpp
    
    module math;
    
    import std.core;
    
    int add(int fir, int sec){
     return fir + sec;
    }
    
    int getProduct(const std::vector<int>& vec) {
     return std::accumulate(vec.begin(), vec.end(), 1, std::multiplies<int>());
    }
    

    • モジュール実装ユニットには、非エクスポート モジュール宣言が含まれています:module math;
    • モジュールには、複数のモジュール実装ユニットを含めることができます。

    メイン プログラム

    // client3.cpp
    
    import std.core;
    
    import math;
    
    int main() {
     
     std::cout << std::endl; 
     
     std::cout << "math::add(2000, 20): " << math::add(2000, 20) << std::endl;
     
     std::vector<int> myVec{1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
     
     std::cout << "math::getProduct(myVec): " << math::getProduct(myVec) << std::endl;
     
     std::cout << std::endl;
     
    }
    

    • ユーザーの観点からは、名前空間の計算のみが追加されました。

    実行可能ファイルのビルド

    実行可能ファイルを手動でビルドするには、いくつかの手順が含まれます。

    cl.exe /std:c++latest /c /experimental:module mathInterfaceUnit.ixx /EHsc /MD // (1)
    cl.exe /std:c++latest /c /experimental:module mathImplementationUnit.cpp /EHsc /MD // (2)
    cl.exe /std:c++latest /c /experimental:module client3.cpp /EHsc /MD // (3)
    cl.exe client3.obj mathInterfaceUnit.obj mathImplementationUnit.obj // (4)
    

    <オール>
  • オブジェクト ファイル mathInterfaceUnit.obj とモジュール インターフェイス ファイル math.ifc を作成します。
  • オブジェクト ファイル mathImplementationUnit.obj を作成します。
  • オブジェクト ファイル client3.obj を作成します。
  • 実行可能な client3.exe を作成します。
  • Microsoft コンパイラの場合、例外処理モデル (/EHsc) とマルチスレッド ライブラリ (/MD) を指定する必要があります。さらに、フラグ /std:c++latest を使用してください。

    最後に、プログラムの出力は次のとおりです。

    次は?

    次の投稿では、モジュールの数学を新しい機能で拡張します。まず、モジュールをインポートして 1 つのユニットにエクスポートします。次に、モジュール内でのみ表示される名前を使用します。