ハマったこと
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を自前で用意したいならの話)
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
系であること、リンク貼ったりしたなら別)
例えば
#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を探しているか確認したければ
g++ -v main.cpp
とすれば見られる。
余談だが、C++のバージョンを指定してビルドしたければg++ -std=c++20 main.cpp
とすればよい。
2️⃣静的解析がエラーを吐く
ここで問題。
MacBookPro + vim9.0 + coc.nvim + clangd
だと、2つエラー。
- clangdの解析では、
bits/stdc++.h
がないと怒られる - 実はこれを解決したあとも、
source_location
がどうたらでエラーが出る
では解決しよう。
coc.nvimのextensionで入れるclangdという言語サーバについて
もろもろの設定をカスタムしたい時に、vim-jpのtwitterコミュニティで質問、自己解決したのがこちら。
このように、clangdの設定はさまざま方法があり
-
~/Library/Preferences/clangd/config.yaml
に記載 - 各プロジェクトごとに
compile_flags.txt
に記載(compile_commands.jsonがあればjsonのみ読まれる) - 各プロジェクトごとに
.clang-format
に記載 - vimでcoc.nvimなら、ものによって
:CocConfig
のjsonでもいける
それぞれ、記載できる設定値は公式を参照されたし。
ここでは、インデント幅を4、inlay-hints無効、includeパスを追加の3つだけ紹介する。
インデント幅を4
各プロジェクトフォルダ直下の.clang-format
に以下を記載
IndentWidth: 4
inlay-hints無効
私は共通設定でオフにしたいので
~/Library/Preferences/clangd/config.yaml
に以下を記載
InlayHints:
Enabled: No
coc-settings.jsonなら
{
"inlayHint.enable": false
}
includeパス
各プロジェクトフォルダ直下のcompile_flags.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からのエラーが出なくなる。
vi /opt/homebrew/Cellar/gcc/13.2.0/include/c++/13/aarch64-apple-darwin22/bits/stdc++.h
こちらも参照されたし
また、#if _GLIBCXX_HOSTED
という記述がstdc++.h
にあるが
これによって、コンパイルは通るがLSの静的解析ではエラーとなるため
ちゃんとif文の外に必要なものを用意する必要もある。
// 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行なら{}
を省略もできる。
ということで、すごいざっくりと基本文法を比較してみる。
めっちゃ適当なのであくまで雰囲気だけ味わってください
#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;
}
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;
}
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)
}
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))