【環境構築】MacのC++でstdc++.hやちょっとだけ他言語と比較してみる

## ハマったこと
C++の`bits/stdc++.h`をincludeしたかった。
STLをひとつひとつincludeするのは面倒なので、全部入り神モジュールである`stdc++.h`を入れたい。

2つ必要なことがあった。
1️⃣ファイル本体が必要
2️⃣静的解析がエラーを吐く

環境はMacでvimです。

### 1️⃣ファイル本体が必要
ようはstdc++.hというファイルが、欲しいincludeを死ぬほど大量にincludeしまくってくれてるファイルということ。
だから、欲しいincludeを全部あらかじめincludeしておいたファイルを、1枚だけincludeすれば楽だよねっていうだけ。とくに魔法みたいなことはしてない。

`stdc++.h`はどこにあるか？
gccの最新を入れると一緒についてくる、gccを入れたくない場合は、ネット上に転がっているファイルを取ってくればいい。
または自分で好きにカスタムすればいい（多すぎるincludeを自前で用意したいならの話）

```gccをいれる.sh
brew install gcc
```

```ここにあるはず.バージョン注意
/opt/homebrew/Cellar/gcc/13.2.0/include/c++/13/aarch64-apple-darwin22/
```
`bits`フォルダの下に`stdc++.h`というファイルがあればOK。

また、一階層上に
```
/opt/homebrew/Cellar/gcc/13.2.0/include/c++/13/bits
```
にも`bits`フォルダがあり、基本的にincludeはここにあるファイルを参照する。

> ※ちゃんとこのgccを使えている(`which gcc`結果が`/usr/local`などでなくbrewで入れた`/opt/homebrew`系であること、リンク貼ったりしたなら別)

例えば
```main.cpp
#include <bits/stdc++.h>
int main() {return 0;}
```

としたとき、`/opt/homebrew/Cellar/gcc/13.2.0/include/c++/13`の下から`bits`フォルダ内の`stdc++.h`ファイルを探す。
gccコンパイル時は、ちゃんと`aarch64-apple-darwin22`の中のほうまで探して見つけてくれる。

実際のビルドでどういった挙動でincludeを探しているか確認したければ
```ビルドの挙動確認.sh
g++ -v main.cpp
```
とすれば見られる。
余談だが、C++のバージョンを指定してビルドしたければ`g++ -std=c++20 main.cpp`とすればよい。


### 2️⃣静的解析がエラーを吐く
ここで問題。
MacBookPro + vim9.0 + coc.nvim + clangd
だと、2つエラー。
1. clangdの解析では、`bits/stdc++.h`がないと怒られる
1. 実はこれを解決したあとも、`source_location`がどうたらでエラーが出る

では解決しよう。
coc.nvimのextensionで入れるclangdという言語サーバについて
もろもろの設定をカスタムしたい時に、vim-jpのtwitterコミュニティで質問、自己解決したのがこちら。

https://x.com/serna37serna37/status/1720075422766149798?s=20

このように、clangdの設定はさまざま方法があり
1. `~/Library/Preferences/clangd/config.yaml`に記載
1. 各プロジェクトごとに`compile_flags.txt`に記載（compile_commands.jsonがあればjsonのみ読まれる）
1. 各プロジェクトごとに`.clang-format`に記載
1. vimでcoc.nvimなら、ものによって`:CocConfig`のjsonでもいける

それぞれ、記載できる設定値は公式を参照されたし。
ここでは、インデント幅を4、inlay-hints無効、includeパスを追加の3つだけ紹介する。

https://clangd.llvm.org/config

#### インデント幅を4
各プロジェクトフォルダ直下の`.clang-format`に以下を記載
```yaml
IndentWidth: 4
```
#### inlay-hints無効
私は共通設定でオフにしたいので
`~/Library/Preferences/clangd/config.yaml`に以下を記載
```yaml
InlayHints:
  Enabled: No
```
coc-settings.jsonなら
```.json
{
  "inlayHint.enable": false
}
```

#### includeパス
各プロジェクトフォルダ直下の`compile_flags.txt`に以下を記載（ついでにバージョンも指定したれ）
```txt
-std=c++20
-I/opt/homebrew/Cellar/gcc/13.2.0/include/c++/13/aarch64-apple-darwin22
```

で、これで終わりじゃなく
`source_location`がどうたらでエラーが出るんです。
Macだとこのモジュールだめみたいで、`stdc++.h`がこれを含んでるからやめろ！といっている。
よって、`stdc++.h`の中のこのincludeをコメントアウトすれば、clangdからのエラーが出なくなる。
```編集しよう.sh
vi /opt/homebrew/Cellar/gcc/13.2.0/include/c++/13/aarch64-apple-darwin22/bits/stdc++.h
```
91行目に書いてありました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/453310/6b44bd37-7669-b022-70c4-8005a70a563b.png)

こちらも参照されたし

https://qiita.com/shinya_ohtani/items/e462d085b6a2205a32cf

また、`#if _GLIBCXX_HOSTED`という記述が`stdc++.h`にあるが
これによって、コンパイルは通るがLSの静的解析ではエラーとなるため
ちゃんとif文の外に必要なものを用意する必要もある。
```diff:サンプルstdc++.h
// C++ includes used for precompiling -*- C++ -*-

// Copyright (C) 2003-2023 Free Software Foundation, Inc.
//

// 略

+ #include <algorithm>
+ #include <iomanip>
+ #include <iostream>
+ // 略
+ // ... if文でないエリアに、必要なものを追加する
// 17.4.1.2 Headers

// C
#ifndef _GLIBCXX_NO_ASSERT
#include <cassert>
#endif
#include <cctype>
#include <cfloat>
#include <ciso646>
// 略

// 115行目あたり ↓これの中はLS解析で読まれないっぽい
#if _GLIBCXX_HOSTED
// C
#ifndef _GLIBCXX_NO_ASSERT
#include <cassert>
#endif
#include <cctype>
#include <cerrno>
#include <cfloat>
#include <ciso646>
// ...略
#endif // HOSTED
// ファイル最後尾

```

ここまでで、gccまるごと`stdc++.h`の取得とMac用の修正、clangdの設定を行い
エラーなく1行includeでコーディングできる最強環境を手に入れられた。

## C++は意外とモダンチックにかけるんだぞ
例えばJavaの`拡張for`やJavaScriptの`for of`、Pythonの`for in`のように
`for (int i=0; i<N; ++i)`とかしなくても良い。

配列でなく`vector`まぁようはJavaでいう`List`みたいなもん。

そもそも1行なら`{}`を省略もできる。

ということで、すごいざっくりと基本文法を比較してみる。
**めっちゃ適当なのであくまで雰囲気だけ味わってください**

```main_c++20で確認.cpp
#include <bits/stdc++.h>
using namespace std;

int main() {
    int arr[10]; // 配列でなく
    vector<string> vec(10); // vectorだと拡張for文的なの使える
    for (auto& v : vec) cin >> v; // 1行なら{}不要
    for (auto& v : vec) cout << v << endl;
    for (auto& v : vec) if (v == 2) { // ifあってもこんなふうにいける
        cout << v << endl;
    }
    // pythonみたく&&や||をand orで書ける
    if (true and true) cout << "Yeah" << endl; // ここも1行なら{}不要
    else cout << "noop" << endl;

    // ラムダ
    auto echo = [](int arg) -> int {
        return arg;
    }
    // ラムダで再帰 (C++20までで確認)
    auto factorial = [&](auto self, int a) -> int {
        return (a <= 1) ? 1 : a * self(self, a-1);
    };
    cout << factorial(factorial, 5);
    // C++23以降はthis auto selfで常に自身を見られるらしい
    auto factorial23 = [&](this auto self, int a) -> int {
        return (a <= 1) ? 1 : a * self(a-1);
    };
    cout << factorial23(5);

    return 0;
}
```

```Javaならこのへんの話.java
public static void main(略) {
    List<String> list = new LinkedList<>();
    list.add("");
    for (String v : list) {
        System.out.println(v);
    }
    list.filter(""::equals).forEach(System.out::println);
    List<Integer> ii = Arrays.asList(1,2,3,4,5);
    Stream.of([1,2,3]).filter(...).anyMatch(...); // 略
    if (true) {
        System.out.println("Yeah");
    } else {
        System.out.println("noop");
    }
    System.out.println(true ? "Yeah" : "noop");
    Function<String, String> echo = v => v;
}
```

```jsならこのへん.js
list = [0, 1, "2", 3]
ll = [...Array(10)].map((_,i)=>i+1) // 1から連番
for (v of list) console.log(v);
for (v of list) {
    console.log(v)
}
// 2重ビット否定で文字列が数値に
// 0はfalsy
list.map(v => ~~v).filter(v => v).forEach(v => console.log(v))
if (true) {
    console.log("Yeah")
} else {
    console.log("noop")
}
console.log(true?"Yeah":"noop")
true && console.log("Yeah") // 短絡演算、if節だけを1行で
echolog = v => console.log(v)
recursiveFunc = v => {
    t = v++
    if (v < 100) return t
    recursiveFunc(v)
}
```

```pythonならこのへん.py
list = range(10)
for v in list:
    print(v)
if true:
    print("Yeah")
fil = [v for v in list if v%2==0]
# pythonの三項演算子は好きじゃないので略
oe = lambda x: 'even' if x % 2 == 0 else 'odd'
print(oe(5))
```