C/C++メモ

すぐに忘れるため、いろいろメモする。これは最適解ではない。

開発環境

エディタはVSCodeでもNeovimでもCLionでもいい。

LSPにはclangdを使う。VSCodeの場合はMicrosoftのC/C++拡張を早急に消し飛ばしてclangdを入れる。

コンパイラの優先度はclang >= gcc >>MSVC。

CMake

compile_commands.json

clangdがプロジェクトの構成(インクルードするディレクトリとか)を把握するためにはcompile_commands.jsonが必要。これは設定すればCMakeが出力してくれる。

CMakeLists.txtに下記のを追記しておくことで、常にcompile_commands.jsonを生成してくれるようになる。お行儀がいいかは知らない。

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は私が気に入らない警告は除外している。
file name
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でラップする場合は知らないが、普通のラムダ関数なら入れ子にしても最適化してくれる。

test.cpp
template<class Callback>
void 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地獄でも速度にあまり影響はない。うれしい。