0
0

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

Last updated at Posted at 2024-01-11

ハマったこと

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がないと怒られる
  2. 実はこれを解決したあとも、source_locationがどうたらでエラーが出る

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

このように、clangdの設定はさまざま方法があり

  1. ~/Library/Preferences/clangd/config.yamlに記載
  2. 各プロジェクトごとにcompile_flags.txtに記載(compile_commands.jsonがあればjsonのみ読まれる)
  3. 各プロジェクトごとに.clang-formatに記載
  4. 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なら

.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からのエラーが出なくなる。

編集しよう.sh
vi /opt/homebrew/Cellar/gcc/13.2.0/include/c++/13/aarch64-apple-darwin22/bits/stdc++.h

91行目に書いてありました。
image.png

こちらも参照されたし

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

サンプル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))
0
0
1

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
0
0