前回の投稿では、C++20 のモジュールについて紹介しました。この投稿では、既存のモジュールの使用方法を示します。
この投稿を始める前に、モジュールへの最初の投稿で終わったことを簡単にまとめさせてください。
短い要約
モジュール インターフェイス ユニットとモジュール実装ユニット、およびそれを使用するクライアントから構成されるモジュール math1 を作成しました。これが 3 つのソース ファイルです。
モジュール インターフェース ユニット
// math1.cppm export module math1; export int add(int fir, int sec);
モジュール実装ユニット
// math1.cpp module math1; int add(int fir, int sec){ return fir + sec; }
クライアント
// main1.cpp import math1; int main(){ add(2000, 20); }
現在のclangおよびcl.exeコンパイラでプログラムをコンパイルしました。コンパイル行が少し短いため、今後は cl.exe コンパイラを使用します。前回の投稿で約束したように、プログラムの出力をお見せしましょう。
標準モジュールの使用
モジュール math2 では、モジュール インターフェイス ユニットもモジュール実装ユニットも変更されていません。
モジュール インターフェース ユニット
// math2.cppm export module math2; export int add(int fir, int sec);
モジュール実装ユニット
// math2.cpp module math2; int add(int fir, int sec){ return fir + sec; }
クライアント
// main2.cpp //#include <iostream> import std.core; import math2; int main(){ std::cout << std::endl; std::cout << "add(2000, 20): " << add(2000, 20) << std::endl; }
モジュール std.core のおかげで、追加の結果を表示できます。
ヘッダー
Visual Studio 2017 の C++ モジュール
std.regex
ヘッダー<regex>
の内容を提供しますstd.filesystem
ヘッダー<experimental/filesystem>
の内容を提供しますstd.memory
ヘッダー<memory>
の内容を提供しますstd.threading
ヘッダー<atomic>
の内容を提供します 、<condition_variable>
、<future>
、<mutex>
、<shared_mutex>
、<thread>
std.core
C++ 標準ライブラリの他のすべてを提供します
モジュールは、ヘッダーよりも高度な抽象化を提供します。これにより、それらを使用するのが非常に快適になります。さらに、モジュールのどの名前をエクスポートするかどうかを指定できます。
輸出と非輸出
次のモジュール math3 は、前のものより少し複雑です。これがインターフェースです。
モジュール インターフェース ユニット
// math3.cppm import std.core; export module math3; int add(int fir, int sec); export int mult(int fir, int sec); export void doTheMath();
モジュール インターフェイス ユニットには、エクスポート モジュール宣言、export module math3; が含まれています。モジュール宣言は、いわゆるモジュール範囲を開始します . export で宣言された、モジュールの範囲の後の名前のみがエクスポートされます。そうでない場合、名前はモジュールの外では見えないため、モジュールのリンケージがあります。これは特に関数 add に当てはまりますが、関数 mult と doTheMath には当てはまりません。
モジュール実装ユニット
// math3.cpp module math3; int add(int fir, int sec){ return fir + sec; } int mult(int fir, int sec){ return fir * sec; } void doTheMath(){ std::cout << "add(2000, 20): " << add(2000, 20) << std::endl; }
モジュール実装ユニットに追加するものは何もありません。メインプログラムはもっと面白いです。
クライアント
// main3.cpp // #include <iostream> // (1) // #include <numeric> // (1) // #include <string> // (1) // #include <vector> // (1) import std.core; // (2) import math3; int main(){ std::cout << std::endl; // std::cout << "add(2000, 20): " << add(2000, 20) << std::endl; // (3) std::vector<int> myVec = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10}; std::string doc = "std::accumulate(myVec.begin(), myVec.end(), mult): "; auto prod = std::accumulate(myVec.begin(), myVec.end(), 1, mult); std::cout << doc << prod << std::endl; doTheMath(); }
ご覧のとおり、私の場合、モジュールは非常に快適です。行 (1) で 4 つのヘッダーを使用する代わりに、行 (2) の単純な import std.core で問題ありません。それだけでした。これがプログラムの出力です。
さて、質問に:行 (3) で関数 add を使用するとどうなりますか。要約すると、add はエクスポートされないため、モジュール リンケージがあります。
コンパイラは、関数 add がメイン プログラムで使用されていると報告しますが、add という名前は表示されません。
詳細h2>
まず、さまざまな方法でエクスポートできます。
エクスポート
math3.cppm などのエクスポート指定子を使用して名前をエクスポートするのは面倒です。
エクスポート指定子
// math3.cppm import std.core; export module math3; int add(int fir, int sec); export int mult(int fir, int sec); export void doTheMath()エクスポート指定子の代わりに、エクスポートされたグループを使用できます。
エクスポートされたグループ
// math3.cppm import std.core; export module math3; int add(int fir, int sec);3 番目のバリエーションは、エクスポートされた名前空間を使用することです。
export {
int mult(int fir, int sec); void doTheMath();
}
エクスポートされた名前空間
// math3.cppm import std.core; export module math3;3 つのバリエーションはすべて意味的に同等です。
namespace math3 {
int add(int fir, int sec);
}
export namespace math3 {
int mult(int fir, int sec); void doTheMath();
}
モジュールを再エクスポートするのも非常に快適かもしれません
モジュールの再エクスポート
別のモジュールからインポートしたものをエクスポートしたい場合があります。インポートされたモジュールをエクスポートしない場合、インポートされたモジュールにはモジュール リンケージがあり、その名前はモジュールの外では見えません。以下は具体的な例です。
見えるものと見えないもの
モジュール math.core と math.core2 を新しいモジュール math にインポートして使用したいとします。ここにmath.coreとmath.core2のモジュールインターフェースユニットがあります。
- 再エクスポートされたモジュール
// module interface unit of math.core export math.core export int mult(int fir, int sec);
// module interface unit of math.core2 export math.core2 export int add(int fir, int sec);
次に、これが新しいモジュールの数学です。
- 新しいモジュールの数学
// module interface unit of math export module math; import math.core; // not exported with mult export import math.core2; // exported with add // module implementation unit of math mult(1100, 2); // fine add(2000, 20); // fine
ご覧のとおり、モジュール math でエクスポートされた名前とエクスポートされていない名前を使用してもまったく問題ありません。ただし、モジュール math.core はエクスポートされません。モジュール math を使用するクライアントのみが違いを確認できます。
- クライアント
// Client import math mult(1100, 2); // ERROR add(2000, 20); // fine
関数 mult にはモジュール リンケージがあるため、モジュールの外からは見えません。関数 add のみが表示されます。
モジュールの再パッケージ化
モジュールを再パッケージ化する快適な方法があります。エクスポートされたグループに入れるだけです。
export module math; export{ import math.core; import math.core2; import math.basics; }
これにより、モジュール math をインポートするクライアントのすべての名前が表示されます。
次は?
次回の記事では、C++ コア ガイドラインの最後のメイン トピックである、標準ライブラリの規則について説明します。信じられないかもしれませんが、プロの C++ 開発者の多くは標準テンプレート ライブラリ (STL) を使用していません。これは、特に STL のアルゴリズムに当てはまります。