コードをデバッグする際にはデバッガを使うこともあれば、単純にprintf
(cout
,cerr
) を埋め込んでデバッグすることもあると思う。
この記事では icecream-cpp というprintfデバッグの際に非常に便利なライブラリを紹介する。
Pythonにも同名のライブラリ(紹介記事はこちら)があるが、そのPythonのicecreamのようなものをC++用に書いたライブラリがicecream-cppである。
リポジトリ : https://github.com/renatoGarcia/icecream-cpp
使い方
例えば以下のように変数の内容をcerr
に出力してデバッグするという経験は誰でも日常的にしていると思う。
int i = 40;
// 何かしらの処理の後、iの内容を出力
std::cerr << "i: " << i << std::endl;
どの変数かわかるように変数名を出力して、そのあとで変数の中身を出力しているが、この辺りは定型句なので毎回書くのは若干面倒だ。変数が単純な型ではなく、vector
などのコンテナのときに中身をイテレートする処理を書いたりすると、だいぶ面倒だ。
Icecreamはこのような面倒なことを代わりにやってくれて、変数の名前と中身をいい感じに標準エラーに出力してくれる。やり方は非常に簡単で、出力したい変数に対してIC(...)
というマクロを呼ぶだけ。
# include <icecream.hpp>
int main() {
int i = 40;
IC(i); // 自分でcerrを書く代わりに、IC(...)を呼ぶだけ。
return 0;
}
実行結果は以下のようになる。
ic| i: 40
他の型の場合
他の型でもうまく表示してくれる。primitive型、std::string
の文字列、STLコンテナなどうまく表示してくれる。
std::string s = "Hello world";
char c = 'X';
std::vector<double> v = {0.2, 0.5, 1.0};
IC(s,c,v);
std::map<std::string, double> m = { {"foo", 1.0}, {"bar", 2.0}};
IC(m);
std::set<int> my_set = {3,5,7,1};
IC(my_set);
ic| s: "Hello world", c: 'X', v: [0.2, 0.5, 1]
ic| m: [("bar", 2), ("foo", 1)]
ic| my_set: [1, 3, 5, 7]
自分で定義したクラスについても表示できる。
class MyClass {
public:
int i;
double d;
};
MyClass mc;
mc.i = 10; mc.d = 3.14;
IC(mc);
ic| mc: {i: 10, d: 3.14}
(ただし、2021年1月時点ではユーザー定義したクラスのメンバーがコンテナのときにうまく表示できないようだ。おそらくバグだと思うが、この件はgithubでissueを立てて報告している)
また operator<<(ostream&, T)
が定義してある型であれば、それを使って出力される。
コンテキストの表示
IC
マクロを引数なしで実行すると、実行時のコンテキスト(メソッド名とソースの行番号が表示される。)例えば、if文でどちらが実行されているかデバッグするためにprintfを使う代わりにICを使うと便利。
if (b == 2) {
IC()
....
}
ic| x.cpp:61 in "int main()"
出力時のプレフィックスを設定する
デフォルトでは ic|
という文字列がプレフィックスとして出力されるが、この部分をカスタマイズして情報を追加することができる。
例えば、どのスレッドで実行しているかを出したいときは以下のようにすると、それ以降の出力でスレッドの情報が出力される。他にもタイムスタンプを出したり、MPI並列化されているプログラムならばランク番号を出したりしても良いかもしれない。
icecream::ic.prefix("thread ", std::this_thread::get_id, " | ");
thread 0x107c02dc0 | i: 40
出力時にコンテキストも表示する
以下のように設定すると、それ以降の出力でコンテキストの情報も出力される。
icecream::ic.include_context(true);
ic| x.cpp:65 in "int main()"- i2: 40
インストール方法
インストール方法は公式のリポジトリのreadmeでいくつか紹介されている。
しかし、ヘッダ1ファイルをインクルードするだけなので、プロジェクトのgitリポジトリにサブモジュールとして追加してしまうのが一番簡単かつ移植性が高いと思われる。
git submodule add git@github.com:renatoGarcia/icecream-cpp.git icecream
でicecream-cppのリポジトリをサブモジュールとして追加し、あとはinclude pathにサブモジュールのパスを追加すればよい。cmakeを使っている場合は、以下の1行を追加する。
include_directories(${CMAKE_SOURCE_DIR}/icecream)
あとは、ヘッダファイルを読み込むだけで利用できる。
# include <icecream.hpp>