3
4

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

C++ Core GuidelinesとGuidelines Support Library

Last updated at Posted at 2018-08-11

はじめに

C++ Core GuidelinesとGuidelines Support Library(GSL)を読んで最新のC++について勉強しているところです。ガイドラインにおいてGSLの積極的利用が推奨されているので、GSLの使い方と共にメモっておきます。(随時更新)

C++ Core Guidelines: Abstract和訳

原文はこちら

この文書はC++を上手く使うための指針の集合です。この文書は人々が最新のC++を有効に使うのを手助けすることを目的としています。ここで言う「最新のC++」とは、C++17、C++14、C++11を指しています。言い換えれば、あなたが今からコードを書き始めるとして、5年後にそのコードをどのように見えるようにしたいですか。10年後はどうですか。

この指針は、比較的高いレベルの論点、例えばインターフェース、リソース管理、メモリ管理、並行性、に焦点を合わせています。そのような指針はアプリケーションアーキテクチャとライブラリデザインに影響します。この指針に従えば、コードは静的に型安全で、リソースリークもなく、現在一般的に捉えられるものよりもおおくのプログラミングの論理的エラーを捉えることができるようになります。しかも速く動きます。―(この指針に沿って)物事を正しく行っても問題ないのです。

私達は低レベルの論点、例えば命名規則と字下げ形式、についてはそこまで関心を持っていません。しかし、プログラマーの助けとならないトピックは対象外です。

最初の指針の集合は(様々な形の)安全性と簡潔性について強調しています。それらは厳しすぎるかもしれません。私達は現実世界での必要性により良く対応させるためにもっと多くの例外を導入しなければならないと思っています。また私達はもっと多くの規則を必要としています。

いくつかの規則はあなたの期待やあるいは経験にさえ反しているかもしれません。もし私達があなたにコーディングスタイルを変えるよう示唆していなかったら、私達は間違っています。どうかこの指針を実証または反証してください。特に、我々は測定やより適切な例に基づいていくつかの指針を支持したいと考えています。

いくつかの規則は明らかかあるいはささいなことであると思うかもしれません。どうか、指針の目的の一つが、経験の浅い人や異なる分野・(プログラミング)言語からきた人の(最新のC++の)素早い習得の手助けにあることを忘れないでください。

多くの指針は解析ツールによって補助されるようデザインされています。指針に対する違反は関連する指針への参照あるいはリンクとともに知らされるでしょう。私達はあなたがコードを書く前にこれらのルールを全て憶えているとは考えていません。これらの指針は人にも読むことができるツールの仕様書として考えることができます。

これらの指針はコードベースへの段階的な導入を意図されています。私達はそうするツールを作る予定であり、他の人たちもそうすることを期待しています。

改善のためのコメントや指摘はいつでも歓迎しています。私達は、私達の理解の深化と(C++)言語とそのライブラリの進歩に合わせて、この文書を修正・拡張していく予定です。

GSL

の2つが利用可能なようです。MicrosoftのものはC++14が必要ですが、GSL LiteはC98にも対応しています。
以下ではMicrosoftのGSLの場合で説明します。

C++ Core Guidelines

Narrowing conversionを避ける

ES.46: Avoid lossy (narrowing, truncating) arithmetic conversions

Narrowing conversionを明示的に行う場合はnarrow_castを使い、narrowing conversionによって情報が失われたときに例外を投げる場合はnarrowを使用する。

double d = 1.2;
int i = narrow_cast<int>(d);  // doubleからintに変換され情報が失われてもよいことを明示
int i = narrow<int>(d);       // doubleからintに変換されるときに情報が失われてはならないことを明示

narrowによって情報が失われた場合、gsl::narrowing_errorが投げられる。

assertよりもExpectsEnsuresを使う

I.6: Prefer Expects() for expressing preconditions
I.8: Prefer Ensures() for expressing postconditions

前提条件を確認する場合はExpectsを、事後条件を確認する場合はEnsuresを使う。
以下はガイドラインで示されている例:

int area(int height, int width)
{
    Expects(height > 0 && width > 0);
    // ...
}
void f()
{
    char buffer[MAX];
    // ...
    memset(buffer, 0, MAX);
    Ensures(buffer[0] == 0);
}

どちらの場合も条件を満たさなければgsl::fail_fastが投げられる。

ExpectsEnsuresの挙動はマクロ

  • GSL_TERMINATE_ON_CONTRACT_VIOLATION:(基本的に)std::terminateが呼ばれる
  • GSL_THROW_ON_CONTRACT_VIOLATIONgsl::fail_fastが投げられる
  • GSL_UNENFORCED_ON_CONTRACT_VIOLATION:コンパイラに条件を満たすとして最適化するよう指定する。

によって制御できる。
ちなみにCMake 3.12以降を使っていればadd_compile_definitionsを使って簡単にマクロを定義できるので便利。

spanatを使用する

ES.42: Keep use of pointers simple and straightforward
SL.con.3: Avoid bounds errors

std::vectorstd::array[]演算子は境界の確認を行わないので使わないこと。
代わりに[]演算子においても境界の確認が行われるgsl::span

  • 範囲for
  • メンバー関数at()
  • フリー関数gsl::at()

を使って要素へアクセスする。
あるいはstd::vectorのラッパークラスを作り、[]演算子の実装においてExpectsを使って境界確認を行ってもよい。
管理しなければならなくなる割に価値が低いので、gsl::at()を使うべきでしょう。

現在研究用に作成しているライブラリでは、インデックスを使ってアクセスする場合は全てgsl::at()を使うようにしました。こうするとエラーメッセージが統一されるのでわかりやすく、またマクロ一つで簡単に最適化ができるため非常に便利です。

span

ES.42: Keep use of pointers simple and straightforward

固定長配列や動的配列へ安全にアクセスすることが可能となり、またSTLコンテナ同様のメンバ関数・演算子が利用できる。

using namespace std;
using namespace gsl;

void f(span<int> s) {
  for (auto&& e : s)
    cout << e << endl;
}

int main() {
  {
    int arr[4] = {1, 2, 3, 4};
    span<int> s = arr;                   // 固定長配列を参照するspan
    f(arr);                              // arrからspanが自動的に生成される

    span<int> s2 = {&arr[0], 2};         // arrの先頭から大きさ2の配列へのspan = {arr[0], arr[1]}
    span<int> s3 = {&arr[0], &arr[2]};   // 上と同じだが{first, last}のペアでspanを作成
  }

  {
    array<int, 4> arr = {1, 2, 3, 4};
    span<int> s2 = arr;                   // arrayを参照するspan
  }
}

他のライブラリと配列をやり取りしなければならないときにインターフェースとして重宝しています。

nullであってはならないポインタにはnot_nullを使う

I.12: Declare a pointer that must not be null as not_null

以下はガイドラインで示されている例:

int length(const char* p);            // 悪い: pとしてnullptrを渡していいか不明

int length(not_null<const char*> p);  // ベター: pがnullでないと仮定できる

int length(const char* p);            // not_nullが使われていない場合、pがnullの可能性を必ず考慮する
3
4
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
4

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?