C++17 を試そう（最新の GCC を使うための環境構築方法（GCC 6.1 版））

# 本稿について
Linux ディストリビューションで C++17(C++1z) に対応し始めた GCC 6.1 を野良ビルドしてインストールするための手順です．
「std::invoke 使わせろ」とか「もういちいち -std=gnu++14 するの面倒なんだけど」とか，新しいコンパイラを使いたいんだけど root のパスワード知らないし．．．という人もどうぞ．

# インストール先ディレクトリ
今回ビルドするファイル全てを /opt/gcc/6.1 にまとめて入れることとします．
よって，ビルド後のライブラリ一式は /opt/gcc/6.1/lib に，GCC の実行ファイルは /opt/gcc/6.1/bin に入ります．
root になれない人は適当なディレクトリで構いませんが，ldconfig が使えないので環境変数 `LD_LIBRARY_PATH` などで `lib` や `lib64` ディレクトリを指定する必要があります．`~/.zshrc` などに書いておきましょう．（具体的な設定は後述）

# 準備するもの
[Prerequisites for GCC](https://gcc.gnu.org/install/prerequisites.html) を見てもらえれば良いのですが，一般的な Linux のシステムでは大半がすでに入っています．

追加で必要なモノは大体以下のものでしょう．

* C++98 が使えるビルドチェイン
* GNU Multiple Precision Library(GMP) 4.3.2+
* MPFR Library 2.4.2+
* MPC Library 0.8.1+
* isl Library 0.14+（0.15+ が良い）

# 必要なアーカイブ
 * GCC
    * https://gcc.gnu.org/
    * 今回使用するのは [GCC 6.1.0](http://ftp.tsukuba.wide.ad.jp/software/gcc/releases/gcc-6.1.0/gcc-6.1.0.tar.bz2)
 * GMP
    * https://gmplib.org/
    * 今回使用するのは [GMP 6.1.1](https://gmplib.org/download/gmp/gmp-6.1.1.tar.lz)
 * MPFR
    * http://www.mpfr.org/
    * 今回使用するのは [MPFR 3.1.4](http://www.mpfr.org/mpfr-current/mpfr-3.1.4.tar.xz)
 * MPC
    * http://www.multiprecision.org/
    * 今回使用するのは [MPC 1.0.3](ftp://ftp.gnu.org/gnu/mpc/mpc-1.0.3.tar.gz)
 * isl
    * http://isl.gforge.inria.fr/
    * 今回使用するのは [isl 0.17](http://isl.gforge.inria.fr/isl-0.17.tar.xz)


# インストールの流れ
1. ldconfig の設定
2. ライブラリのビルド＆インストール
3. GCC のビルド＆インストール

今回は 16 個の論理プロセッサを積んだマシンを使っているので `-j16` していますが，ここは環境に合わせてください．

# ldconfig の設定
各種ライブラリをインストールするにあたり，ldconfig から共有ライブラリを見つけてもらえるようにする必要があります．
/etc/ld.so.conf.d/ 内にディレクトリエントリを作り，これからインストールするライブラリ一式を入れるディレクトリが ldconfig から見えるようにしましょう．

root になれない方は ldconfig の設定ができないので，本手順の代わりにこれから configure で指定する `--prefix` に指定するディレクトリ内の `lib` と `lib64` ディレクトリを環境変数 `LD_LIBRARY_PATH` に設定してください．

```
$ sudo vi /etc/ld.so.conf.d/gcc61.conf
```
``` text:/etc/ld.so.conf.d/gcc61.conf
/opt/gcc/6.1/lib
/opt/gcc/6.1/lib64
```

```zsh:LD_LIBRARY_PATHの設定（rootになれない人向け）
GCC61_HOME=/path/to/prefix
export LD_LIBRARY_PATH=${GCC61_HOME}/lib:${GCC61_HOME}/lib64
```

まだ ldconfig しなくても良いです．

# 各種ライブラリのビルドとインストール
依存関係があるので順番を守りましょう．
また，各ライブラリのインストール後には `ldconfig` を実行しましょう．
root 持っていない方は `--prefix` に自分が書き込めるディレクトリを指定してください．この場合はもちろん `sudo` は不要です．

## GMP
```
$ tar --lzip -xf gmp-6.1.1.tar.lz
$ cd gmp-6.1.1
$ ./configure --prefix=/opt/gcc/6.1
$ make -j16
$ sudo make install
$ sudo ldconfig
$ make clean
```

## MPFR
```
$ tar Jxf mpfr-3.1.4.tar.xz
$ cd mpfr-3.1.4
$ ./configure --prefix=/opt/gcc/6.1 --with-gmp=/opt/gcc/6.1
$ make -j16
$ sudo make install
$ sudo ldconfig
$ make clean
```

## MPC
```
$ tar zxf mpc-1.0.3.tar.gz
$ cd mpc-1.0.3
$ ./configure --prefix=/opt/gcc/6.1 --with-gmp=/opt/gcc/6.1 --with-mpfr=/opt/gcc/6.1
$ make -j16
$ sudo make install
$ sudo ldconfig
$ make clean
```

## isl
```
$ tar Jxf isl-0.17.tar.xz
$ cd isl-0.17
$ ./configure --prefix=/opt/gcc/6.1 --with-gmp-prefix=/opt/gcc/6.1
$ make -j16
$ sudo make install
$ sudo ldconfig
$ make clean
```

isl のインストール後に ldconfig で下記のようなメッセージが出ますが，無視して良いです．

```
/sbin/ldconfig.real: /opt/gcc/6.1/lib/libisl.so.15.2.0-gdb.py is not an ELF file - it has the wrong magic bytes at the start.
```

# GCC のビルドとインストール
C++17 を使うためには C++ があれば良いのですが，僕は C と Fortran，ついでに LTO サポートも欲しいので `--enable-languages` でこれらを指定します．

```
$ tar jxf gcc-6.1.0.tar.bz2
$ cd gcc-6.1.0
$ ./configure --prefix=/opt/gcc/6.1 --with-gmp=/opt/gcc/6.1 --with-mpc=/opt/gcc/6.1 --with-mpfr=/opt/gcc/6.1 --with-isl=/opt/gcc/6.1 --disable-multilib --enable-languages=c,c++,fortran,lto
$ make -j16
$ sudo make install
$ sudo ldconfig
$ make clean
```

ここでも ldconfig 時に下記のようなメッセージが出ますが，無視して良いです．

```
/sbin/ldconfig.real: /opt/gcc/6.1/lib64/libstdc++.so.6.0.22-gdb.py is not an ELF file - it has the wrong magic bytes at the start.
```

### 【参考】GCC のビルドって時間がかかるんだけど．．．
ビルド時間を短縮するとかいう理由のためだけに `--disable-bootstrap` を付けることを勧める記事をよく見ますが，メジャーバージョン（4.x → 4.y など）が上がるときは bootstrap ビルドをしておいた方が安全です．ABI の変更など致命的な問題が影響することがあります．
マイナバージョンを上げるとき（4.6.x → 4.6.y など）はまぁ bootstrap による確認は不要でしょう．

# さぁできた
サンプルコードを書いて，`std::invoke` できるか確認してみましょう．
（`std::invoke` を叩きたいだけなので，色々手抜きしています）

```cpp:sample.cpp
#include <iostream>
#include <functional>

class Hoge
{
private:
  int base;

public:
  Hoge(int x)
  {
    this->base = x;
  }

  int count_up()
  {
    return ++this->base;
  }

  void change_base(int x)
  {
      this->base = x;
  }
};

int count_up()
{
  int base = 100;
  return ++base;
}


int main(int argc, char **argv)
{
  Hoge h(0);
  std::cout << h.count_up() << std::endl;

  std::cout << std::invoke(&count_up) << std::endl;

  Hoge h1(100);
  std::cout << std::invoke(&Hoge::count_up, h1) << std::endl;

  Hoge h2(200);
  std::cout << std::invoke(&Hoge::count_up, h2) << std::endl;

  auto &h3 = h1;
  h3.change_base(300);
  std::cout << std::invoke(&Hoge::count_up, h1) << std::endl;
  std::cout << std::invoke(&Hoge::count_up, h3) << std::endl;

  auto h4 = h1;
  h4.change_base(400);
  std::cout << std::invoke(&Hoge::count_up, h1) << std::endl;
  std::cout << std::invoke(&Hoge::count_up, h4) << std::endl;


  return 0;
}
```

```text:ビルド
$ /opt/gcc/6.1/bin/g++ sample.cpp --std=gnu++17
```

```text:実行結果
$ ./a.out
1
101
101
201
301
302
303
401
```

# まとめ
新しいコンパイラや実行環境は気軽に試すことができるので，どんどん使って筋トレしましょう．