C/C++メモ
すぐに忘れるため、いろいろメモする。これは最適解ではない。
開発環境
エディタはVSCodeでもNeovimでもCLionでもいい。
LSPにはclangdを使う。VSCodeの場合はMicrosoftのC/C++拡張を早急に消し飛ばしてclangdを入れる。
コンパイラの優先度はclang >= gcc >>MSVC。
CMake
compile_commands.json
clangdがプロジェクトの構成(インクルードするディレクトリとか)を把握するためにはcompile_commands.jsonが必要。cmakeのコマンドオプションに-DCMAKE_EXPORT_COMPILE_COMMANDSを追加すると生成される。
また、nix上の環境でcompile_commands.jsonを生成すると、「nixのシェルのPATHにはあるが、ホストのPATHにはないのでヘッダが見つからない」という状況が起こり、滝のようなエラーが出る。そのため、PATHに含まれてるのも明示的に出力する必要がある。CMakeLists.txtに↓のコードを追加することで含まれるようになる。
set(CMAKE_EXPORT_COMPILE_COMMANDS ON CACHE INTERNAL "")
if(CMAKE_EXPORT_COMPILE_COMMANDS)
set(CMAKE_CXX_STANDARD_INCLUDE_DIRECTORIES
${CMAKE_CXX_IMPLICIT_INCLUDE_DIRECTORIES})
endif()
それとWindows環境のデフォルトではcompile_commands.jsonが生成されないので注意。Ninjaを入れてジェネレータにNinjaを指定することで生成される。
警告を出す
コンパイラごとにオプションが違うため、分岐させる。特にClangは私が気に入らない警告は除外している。
if (CMAKE_CXX_COMPILER_ID MATCHES "Clang")
target_compile_options(stock_emu_lib PRIVATE
-Wall -Weverything
-Wno-c++98-compat -Wno-padded -Wno-exit-time-destructors -Wno-global-constructors
)
elseif (CMAKE_CXX_COMPILER_ID MATCHES "GNU")
target_compile_options(stock_emu_lib PRIVATE
-Wall
)
elseif (CMAKE_CXX_COMPILER_ID MATCHES "MSVC")
target_compile_options(stock_emu_lib PRIVATE
/Wall
)
コンパイラーは賢い
想像以上にコンパイラの最適化は賢い。一般人の思いつくような懸念点はたいていコンパイラはなんとかしてくれる。
ラムダの入れ子
std::functionでラップする場合は知らないが、普通のラムダ関数なら入れ子にしても最適化してくれる。
templatevoid f(Callback&& c_) { auto nest_lambda = [call_f1_nest = std::move(c)]() { call_f1_nest(); call_f2(); }; g(std::function (nest_lambda)); } int main() { f([](){ f1(); }); }
例えば上のコードでは「「f1を呼び出すlambda」を呼び出して、f2を呼び出すlambda」をgに渡している。 これは最適化によって「f1を呼び出してf2を呼び出すlambda」をgに渡すのとだいたい同じコードが生成される。(Clang, gccの最適化オプション3, 5で確認)
変数キャプチャがあっても同じく動作する。要はcallback地獄でも速度にあまり影響はない。うれしい。
クソ高速化
C/C++では、クソみたいな高速化テクが存在する。たいていボトルネックでない箇所なので、あまり重要ではない。
isspace()は独自実装したほうが25%早い
isspace()関数はC標準ライブラリにある関数で、一般にはTab、改行、スペースなどに対して非ゼロ(=true)を返す。
のだが、この関数は忌まわしきlocaleを参照する。そのため、(0x09 <= i && 0x0d) || i == 0x20のような実装で置き換えると約25%早くなる。ちなみにこれはインライン化や最適化オプションをつけていない状態での計測なので、実際はもっと早くなるかもしれない。