まとめ
- gccでuninitializedなdoubleを参照渡ししてもwarningが出ない
- cppcheckを使うと検知できることもあるが、検知できないこともある
- clang-tidy, clang analyzerでは検知できない
発端
以下のコード。
test2はコンパイルエラーになるが、testはパスしてしまう。
valgrindなら検出できるが、コンパイルエラーで検知してほしい。
# include <algorithm>
# if 1
int test() {
int a = 3;
int b;
return std::max<int>(a, b);
}
# else
int test2() {
int b;
return b;
}
# endif
コンパイル結果 (-Werror -Wall)
https://godbolt.org/z/D3L2oC
試行錯誤
std::maxのせいではないか?
std::maxではなく、自前で似たような関数を作ってみると、-Wallで検知できる。
# include <algorithm>
# if 1
int mymax(int a, int b) {
return a > b ? a : b;
}
int test() {
int a = 3;
int b;
return std::max<int>(a, b);
}
int test3() {
int a = 3;
int b;
return mymax(a, b);
}
# else
int test2() {
int b;
return b;
}
# endif
コンパイル結果
https://godbolt.org/z/a6kmmm
const 参照渡しだと通ってしまった
# include <algorithm>
# if 1
int mymax(const int &a, const int& b) {
return a > b ? a : b;
}
int test() {
int a = 3;
int b;
return std::max<int>(a, b);
}
int test3() {
int a = 3;
int b;
return mymax(a, b);
}
# else
int test2() {
int b;
return b;
}
# endif
コンパイル結果
https://godbolt.org/z/mXlKwm
const ポインタ渡しだとパスした
int max(const int *a, const int *b) {
return *a > *b ? *a : *b;
}
int test() {
int a;
int b = 3;
return max(&a, &b);
}
コンパイル結果
https://godbolt.org/z/CtRYmQ
参照渡しでコンパイルエラーにするにはどうすればいいか?
-O2をつけてみたら、エラーになった
std::max: https://godbolt.org/z/elwQsT
const参照: https://godbolt.org/z/3-M74q
constポインタ: https://godbolt.org/z/BcbtE_
結論、最適化オプションをつけていれば問題なかった
と思ったが、doubleだとパスする
const double &mymax(const double &a,
const double &b) {
return a > b ? a : b;
}
double test(double x) {
double a = 3;
double b;
return mymax(a, b);
}
コンパイル結果
https://godbolt.org/z/EodiVt
発端はdoubleだったので、困る
値渡しだと?
double mymax(double a, double b) {
return a > b ? a : b;
}
double test(double x) {
double a = 3;
double b;
return mymax(a, b);
}
エラーになる。
コンパイル結果
https://godbolt.org/z/luJrGq
どうする?
cppcheckで検知できました。
http://cppcheck.sourceforge.net/
cmakeにcppcheckを組み込む方法
基本
cppcheckのエラー時にcmakeのビルドをエラーにする方法
背景
cppcheckはデフォルトだとエラーでもexit statusは0になる。
--error-exitcode=X オプションを使うとexit statusをエラー時のXにできる。
しかし、これを指定してもcmake経由で使うとエラーにならない。
解決策
以下のプルリクを発見。
https://gitlab.kitware.com/cmake/cmake/merge_requests/2459
2018/10/12にmasterにマージされている。
多分最新版のcmakeを使えばOK
(具体的にどのバージョンから含まれているかは未調査)
必要なマクロを定義する
cppcheckは普通のコンパイラで暗黙に定義されているマクロが無いので、OSごとに分岐しているコードなどで、どのOSにも該当しないケースでこけることがある。
以下でかいけつ
https://srz-zumix.blogspot.com/2012/03/cppcheck.html
特定のエラーや特定のファイルを除外する
悲報
発端となった実際のコードにcppcheckをかけたら、なぜかuninitialized doubleの参照渡しを検知できませんでした・・・
この用途ではcppcheckは使えませんでしたが、他のバグを検出できたのでよしとします。
-> gcc + -O2 + -ffast-mathで検知できました。詳細はコメント参照
他のstatic analyzer
uninitialized doubleの参照渡しを検知できるstatic analyzerを探します。
clang-tidy
検知できなかった
clang-analyzer
検知できなかった
sonarqube
調べていない