C++ Core GuidelinesとGuidelines Support Library

# はじめに

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

# C++ Core Guidelines: Abstract和訳

[原文はこちら](https://github.com/isocpp/CppCoreGuidelines/blob/master/CppCoreGuidelines.md#abstract)

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

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

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

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

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

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

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

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

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

# GSL

- [GSL: Guideline Support Library](https://github.com/Microsoft/GSL) by Microsoft
- [GSL Lite: Guideline Support Library for C++98, C++11 up](https://github.com/martinmoene/gsl-lite) by martinmoene

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


# C++ Core Guidelines

## Narrowing conversionを避ける

[ES.46: Avoid lossy (narrowing, truncating) arithmetic conversions](https://github.com/isocpp/CppCoreGuidelines/blob/master/CppCoreGuidelines.md#Res-narrowing)

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

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

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

## `assert`よりも`Expects`と`Ensures`を使う

[I.6: Prefer Expects() for expressing preconditions](https://github.com/isocpp/CppCoreGuidelines/blob/master/CppCoreGuidelines.md#Ri-expects)
[I.8: Prefer Ensures() for expressing postconditions](https://github.com/isocpp/CppCoreGuidelines/blob/master/CppCoreGuidelines.md#i8-prefer-ensures-for-expressing-postconditions)

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

```cpp
int area(int height, int width)
{
    Expects(height > 0 && width > 0);
    // ...
}
```

```cpp
void f()
{
    char buffer[MAX];
    // ...
    memset(buffer, 0, MAX);
    Ensures(buffer[0] == 0);
}
```

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

`Expects`と`Ensures`の挙動はマクロ

- `GSL_TERMINATE_ON_CONTRACT_VIOLATION`：（基本的に）`std::terminate`が呼ばれる
- `GSL_THROW_ON_CONTRACT_VIOLATION`：`gsl::fail_fast`が投げられる
- `GSL_UNENFORCED_ON_CONTRACT_VIOLATION`：コンパイラに条件を満たすとして最適化するよう指定する。

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

## `span`や`at`を使用する

[ES.42: Keep use of pointers simple and straightforward](https://github.com/isocpp/CppCoreGuidelines/blob/master/CppCoreGuidelines.md#es42-keep-use-of-pointers-simple-and-straightforward)
[SL.con.3: Avoid bounds errors](https://github.com/isocpp/CppCoreGuidelines/blob/master/CppCoreGuidelines.md#slcon3-avoid-bounds-errors)

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

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

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

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

### `span`

[ES.42: Keep use of pointers simple and straightforward](https://github.com/isocpp/CppCoreGuidelines/blob/master/CppCoreGuidelines.md#es42-keep-use-of-pointers-simple-and-straightforward)

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

```cpp
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](https://github.com/isocpp/CppCoreGuidelines/blob/master/CppCoreGuidelines.md#Ri-nullptr)

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

```cpp
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の可能性を必ず考慮する
```