C++ コア ガイドライン:ガイドライン サポート ライブラリ

ガイドライン サポート ライブラリ (GSL) は、C++ コア ガイドラインのガイドラインをサポートするための小さなライブラリです。より良い C++ プログラムを作成するのに役立つはずです。したがって、その主な懸念事項は、メモリの安全性と型の安全性です。それらは、利用可能な GSL のいくつかの実装です。

GSL はヘッダーのみのライブラリであるため、ライブラリの関数と型を非常に簡単に使用できます。最もよく知られている実装は、Github でホストされている Microsoft の実装です:Microsoft/GSL. Microsoft バージョンは C++14 のサポートを必要とし、さまざまなプラットフォームで動作します。主なプラットフォームは次のとおりです。

  • Visual Studio 2015 を使用する Windows
  • Visual Studio 2017 を使用する Windows
  • Clang/LLVM 3.6 を使用した GNU/Linux
  • GCC 5.1 を使用する GNU/Linux

しかし、それだけではありません。GitHub にはさらに多くの実装が存在します。 Martin Moene の GSL-lite 実装を明示的に強調したいと思います。彼の実装は、C++98 と C++03 でも機能します。

詳細に入る前に、私の執筆を難しくしている問題が 1 つあります。それは、優れたドキュメントやチュートリアルがないことです。関数と型が何をサポートする必要があるかを理解するには、ライブラリをインストールして単体テストを分析する必要があります。そのようなドキュメントではないと思います。対照的に、GSL の Microsoft 実装のインストールと使用は、Windows と Linux で非常に簡単でした。

それでは、詳細に飛び込みましょう。 GSL は 5 つのコンポーネントで構成されています。最初の概要は次のとおりです:

  • GSL.view:ビュー
    • span
    • string_span
    • (cw)zstring
  • GSL.所有者
    • 所有者
    • unique_ptr
    • shared_ptr
    • dyn_array
    • stack_array
  • GSL.assert:アサーション
    • Expects()
    • Ensures()
  • GSL.util:ユーティリティ
    • 狭い
    • narrow_cast()
    • not_null
    • ついに
  • GSL.concept:コンセプト
    • Range
    • String
    • Number
    • Sortable
    • Pointer
    • ...

C++11 標準には std::unique_ptr と std::shared_ptr があるため、GSL には独自のスマート ポインター gsl::unique_ptr と gsl::shared_ptr があることに疑問を抱くかもしれません。アイデアは非常に単純です。C++11 をサポートしていないコンパイラで GSL を使用できます。 GSL がサポートする多くの関数と型は、C++20 の一部になる可能性があります。それは、少なくとも概念と主張に当てはまります。さらに、残りの部分も今後の C++ 標準の一部になる可能性が非常に高いです。

コンポーネント

ビューを見てみましょう。

GSL.view:ビュー

ビューは決して所有者ではありません。 gsl::span の場合、連続したメモリの非所有範囲を表します。これは、配列、サイズのあるポインター、または std::vector にすることができます。 gsl::string_span またはゼロで終わる C 文字列についても同じことが言えます:gsl::(cw)zstring. gsl::span を持つ主な理由は、プレーンな配列が関数に渡された場合はポインター。したがって、サイズが失われます。

gsl::span プレーン配列または std::vector のサイズを自動的に推定します。ポインターを使用する場合は、サイズを指定する必要があります。

template <typename T>
void copy_n(const T* p, T* q, int n){}

template <typename T>
void copy(gsl::span<const T> src, gsl::span<T> des){}

int main(){
 
 int arr1[] = {1, 2, 3};
 int arr2[] = {3, 4, 5};
 
 copy_n(arr1, arr2, 3); // (1)
 copy(arr1, arr2); // (2)
 
}

関数 copy_n (1) とは対照的に、関数 copy (2) には要素数を指定していません。したがって、エラーの一般的な原因は gsl::span でなくなりました。

GSL にはさまざまな種類の所有者がいます。

GSL.owner:所有権ポインタ

std::unique_ptr と std::shared_ptr を知っているので、gsl::unique_ptr と gsl::shared_ptr を知っていると思います。そうでない場合は、スマート ポインターに関する私の投稿をご覧ください。

gsl::owner 参照されたオブジェクトの所有権を持つポインターをマークします。スマート ポインターやコンテナーなどのリソース ハンドルを使用できない場合は、gsl::owner を使用する必要があります。所有者に関する重要な点は、リソースを明示的に解放する必要があるということです。 gsl::owner としてマークされていない生のポインターは、C++ コア ガイドラインでは非所有と見なされます。したがって、リソースを解放する必要はありません。

gsl::dyn_array と gsl::stack_array は 2 つの新しい配列型です。

  • gsl::dyn_array 実行時に指定される要素の固定サイズを持つヒープ割り当て配列です。
  • gsl::stack_array 実行時に指定される要素の固定サイズを持つスタック割り当て配列です。

GSL.assert:アサーション

Expects() のおかげで および Ensures() 、関数の事前条件と事後条件を述べることができます。現在、関数本体に配置する必要がありますが、これは後で関数宣言に移動されます。どちらの機能も契約提案の一部です。

これは、Expects() と Ensures() を使用した簡単な例です。

int area(int height, int width)
{
 Expects(height > 0); 
 auto res = height * width;
 Ensures(res > 0);
 return res;
}

GSL.util:ユーティリティ

gsl::narrow_cast と gsl::narrow は 2 つの新しいキャストです。

  • gsl::narrow_cast その意図のみを表現する static_cast です。縮小変換が発生する可能性があります。
  • gsl::narrow static_cast<T>(x) != x の場合、narrowing_error 例外をスローする static_cast です。 .

gsl::not_null nullptr であってはならないポインターをモデル化します。 gsl::not_null ポインターを nullptr に設定すると、コンパイラ エラーが発生します。 std::unique_ptr や std::shared_ptr などのスマート ポインターを gsl::not_null に配置することもできます。通常、関数パラメーターとその戻り値の型には gsl::not_null を使用します。したがって、ポインターが値を保持しているかどうかを確認することを忘れることはできません。

int getLength(gsl::not_null<const char*> p); // p cannot be a nullptr

int getLength(const char* p); // p can be a nullptr

どちらの関数も、その意図を明示的に示しています。 2 つ目は nullptr を受け入れることができます。

ついに スコープの最後で実行される callable を登録できます。

void f(int n)
{
 void* p = malloc(1, n);
 auto _ = finally([p] { free(p); });
 ...
}
 

関数 f の最後に、ラムダ関数 [p] { free(p); } が自動的に呼び出されます。

C++ コア ガイドラインによると、スマート ポインターや STL コンテナーなどの適切なリソース管理を使用できない場合は、finally を最後の手段として検討する必要があります。

GSL.concept:概念

ほとんどの概念は Ranges TS で定義されているため、簡単に説明します。ここに概念に関する私の投稿があります。

最後の言葉

ガイドライン支援ライブラリには感銘を受けました。私が特に気に入っているのは、C++11 準拠のコンパイラを必要としないことです。レガシー コードで使用することもでき、メモリ セーフとタイプ セーフを大幅に向上させることができます。言い忘れましたが、GSL は「同等の手書きの小切手と比較して、ゼロ オーバーヘッドを目指しています。」. それは約束です。

次は?

GSL に少し寄り道した後、C++ コア ガイドラインのルールに戻ります。次の投稿は、関数全般、関数のパラメーター、特にその戻り値についてです。