
運命のひねりによって、「Awesomehpp」コレクションのほとんどのライブラリをチェックしました。これらは、ヘッダーファイルのみで構成される小さなC ++プロジェクトです。うまくいけば、見つかったバグがこれらのライブラリを少し良くするのに役立つでしょう。また、著者が定期的にPVS-Studioアナライザーを無料で使い始めていただければ幸いです。
awesome-hpp(素晴らしいヘッダーのみのC ++ライブラリのキュレートされたリスト)リストにリスト されているさまざまなライブラリをテストした結果の概要に注目します。
このリストについては、「Cross PlatformMobileTelephony」ポッドキャストから最初に知りました。この機会を利用して、すべてのC ++プログラマーにCppCastに精通することをお勧めします。CppCastは、C ++開発者によるC ++開発者向けの最初のポッドキャストです。
リストには多数のプロジェクトがありますが、エラーはほとんどありませんでした。これには3つの理由があります。
- これらは非常に小さなプロジェクトです。多くは文字通り単一のヘッダーファイルで構成されています。
- すべてのプロジェクトをチェックしたわけではありません。それらのいくつかのコンパイルに問題があり、私たちはそれらをスキップすることにしました。
- 多くの場合、テンプレートクラス/関数にエラーがあるかどうかを理解するために、それらをインスタンス化する必要があります。したがって、ライブラリがアクティブに使用されている場合、実際のプロジェクトでのみアナライザによって多くのエラーを検出できます。空の.cppファイルにヘッダーファイルを含めてチェックしただけなので、チェックが無効になります。
それにもかかわらず、それらを研究する過程で、この記事といくつかの追加の記事を書くのに十分な警告がありました。
同僚へのメモ
ライブラリ開発者へのメモ。興味のある方は、PVS-Studioアナライザーを無料で使用してオープンソースプロジェクトをチェックできます。オープンソースプロジェクトのライセンスを取得するには、このフォームに記入してください。
最後に、いくつかのライブラリで見つかったものを見てみましょう。
見つかったエラー
Iutestライブラリ
iutest ライブラリの簡単な説明:
iutestは、C ++テストを作成するためのフレームワークです。
template<typename Event>
pool_handler<Event> & assure() {
....
return static_cast<pool_handler<Event> &>(it == pools.cend() ?
*pools.emplace_back(new pool_handler<Event>{}) : **it);
....
}
PVS-Studio警告:V1023所有者のないポインターが「emplace_back」メソッドによって「プール」コンテナーに追加されます。例外が発生した場合、メモリリークが発生します。entt.hpp 17114
このコードは、メモリをリークする可能性があります。コンテナが再割り当てを必要とし、新しい配列にメモリを割り当てることができない場合、例外がスローされ、ポインタが失われます。
おそらく、テストの場合、この状況は起こりそうになく、重大ではありません。しかし、私は教育目的でこの欠点に言及することにしました:)。
正しいオプション:
pools.emplace_back(std::make_unique<pool_handler<Event>>{})
別の同様の場所:V1023所有者のないポインターが「emplace_back」メソッドによって「pools」コンテナーに追加されます。例外が発生した場合、メモリリークが発生します。entt.hpp 17407
Jsonconsライブラリ
jsoncons ライブラリの簡単な説明:
JSONポインタ、JSONパッチ、JSONPath、JMESPath、CSV、MessagePack、CBOR、BSON、UBJSONを使用して、JSONおよびJSONのようなデータ形式を構築するためのC ++ヘッダーのみのライブラリ。最初の間違い
static constexpr uint64_t basic_type_bits = sizeof(uint64_t) * 8;
uint64_t* data()
{
return is_dynamic() ? dynamic_stor_.data_ : short_stor_.values_;
}
basic_bigint& operator<<=( uint64_t k )
{
size_type q = (size_type)(k / basic_type_bits);
....
if ( k ) // 0 < k < basic_type_bits:
{
uint64_t k1 = basic_type_bits - k;
uint64_t mask = (1 << k) - 1; // <=
....
data()[i] |= (data()[i-1] >> k1) & mask;
....
}
reduce();
return *this;
}
PVS-Studioの警告:V629「1 << k」式の検査を検討してください。32ビット値のビットシフトとそれに続く64ビットタイプへの拡張。bigint.hpp 744
このエラーについては、「プロジェクトに追加するオープンソースライブラリの静的分析を実行することが重要である理由」の記事ですでに詳しく説明されています。つまり、正しいマスク値を取得するには、次のように記述する必要があります。
uint64_t mask = (static_cast<uint64_t>(1) << k) - 1;
またはこのように:
uint64_t mask = (1ull << k) - 1;
最初のエラーとまったく同じエラーがここに表示されます。V629「1 << k」式の検査を検討してください。32ビット値のビットシフトとそれに続く64ビットタイプへの拡張。bigint.hpp 7792
番目のエラー
template <class CharT = typename std::iterator_traits<Iterator>::value_type>
typename std::enable_if<sizeof(CharT) == sizeof(uint16_t)>::type
next() UNICONS_NOEXCEPT
{
begin_ += length_;
if (begin_ != last_)
{
if (begin_ != last_)
{
....
}
PVS-Studioの警告:V571定期的なチェック。'if(begin _!= Last_)'条件は、1138行目ですでに検証されています。unicode_traits.hpp1140
奇妙な再テスト。ここにいくつかのタイプミスがあり、2番目の条件は何か違うように見えるはずであるという疑いがあります。
Clippライブラリ
clipp ライブラリの簡単な説明:
clipp-最新のC ++のコマンドラインインターフェイス。単一のヘッダーファイルに含まれるC ++ 11/14/17の使いやすく、強力で表現力豊かなコマンドライン引数処理。
inline bool
fwd_to_unsigned_int(const char*& s)
{
if(!s) return false;
for(; std::isspace(*s); ++s);
if(!s[0] || s[0] == '-') return false;
if(s[0] == '-') return false;
return true;
}
PVS-Studioの警告:V547式の[0] == '-' 'は常にfalseです。clipp.h 303
実は、これはエラーではなく、単に冗長なコードです。マイナスチェックは2回実行されます。
SimpleIniライブラリ
SimpleIni ライブラリの簡単な説明:
INIスタイルの構成ファイルを読み書きするためのシンプルなAPIを提供するクロスプラットフォームライブラリ。ASCII、MBCS、Unicodeのデータファイルをサポートしています。
#if defined(SI_NO_MBSTOWCS_NULL) || (!defined(_MSC_VER) && !defined(_linux))
PVS-Studioの警告:V1040事前定義されたマクロ名のスペルにタイプミスがある可能性があります。'_linux'マクロは '__linux'に似ています。SimpleIni.h 2923は、
ほとんどの場合、の名前_linuxのマクロは1アンダースコアが欠落していると名前__linuxを使用する必要があります。ただし、POSIXでは、このマクロは非推奨であり、__ linux__を使用することをお勧めします。
CSVパーサーライブラリ
CSVパーサー ライブラリの簡単な説明:
CSV(および同様の)ファイルを読み取り、書き込み、分析するための最新のC ++ライブラリ。
CSV_INLINE void CSVReader::read_csv(const size_t& bytes) {
const size_t BUFFER_UPPER_LIMIT = std::min(bytes, (size_t)1000000);
std::unique_ptr<char[]> buffer(new char[BUFFER_UPPER_LIMIT]);
auto * HEDLEY_RESTRICT line_buffer = buffer.get();
line_buffer[0] = '\0';
....
this->feed_state->feed_buffer.push_back(
std::make_pair<>(std::move(buffer), line_buffer - buffer.get())); // <=
....
}
PVS-Studioの警告:V769'line_buffer --buffer.get() '式の' buffer.get() 'ポインターがnullptrに等しい。結果の値は無意味であり、使用しないでください。csv.hpp4957
慎重に検討する必要がある興味深い状況。したがって、私はこれについて別の小さなメモを書くことにしました。さらに、同様のコードを試しているときに、PVS-Studio自体に欠陥が見つかりました:)。警告を発するはずですが、サイレントな場合もあります。
つまり、このコードが機能するかどうかは、引数が評価される順序によって異なります。引数が評価される順序は、コンパイラによって異なります。
PPrintライブラリ
ライブラリPPRINTの簡単な説明:。
現代のC ++用のきれいなプリンター。
template <typename Container>
typename std::enable_if<......>::type print_internal(......) {
....
for (size_t i = 1; i < value.size() - 1; i++) {
print_internal(value[i], indent + indent_, "", level + 1);
if (is_container<T>::value == false)
print_internal_without_quotes(", ", 0, "\n");
else
print_internal_without_quotes(", ", 0, "\n");
}
....
}
PVS-Studio警告:V523「then」ステートメントは「else」ステートメントと同等です。pprint.hpp715
条件に関係なく同じアクションが実行されるのは非常に奇妙です。特別な解説もありません。これはすべて、コピーと貼り付けのエラーと非常によく似ています。
同様の警告:
- V523「then」ステートメントは「else」ステートメントと同等です。pprint.hpp 780
- V523「then」ステートメントは「else」ステートメントと同等です。pprint.hpp 851
- V523「then」ステートメントは「else」ステートメントと同等です。pprint.hpp 927
- V523「then」ステートメントは「else」ステートメントと同等です。pprint.hpp 1012
Strfライブラリ
Strf ライブラリの簡単な説明:
エンコーディング変換をサポートする高速C ++フォーマットライブラリ。最初の間違い
template <int Base>
class numpunct: private strf::digits_grouping
{
....
constexpr STRF_HD numpunct& operator=(const numpunct& other) noexcept
{
strf::digits_grouping::operator=(other);
decimal_point_ = other.decimal_point_;
thousands_sep_ = other.thousands_sep_;
}
....
};
PVS-Studio警告:V591非void関数は値を返す必要があります。numpunct.hpp402
関数の最後に「return * this;」と書くのを忘れました。
2番目の同様のエラー
template <int Base>
class no_grouping final
{
constexpr STRF_HD no_grouping& operator=(const no_grouping& other) noexcept
{
decimal_point_ = other.decimal_point_;
}
....
}
PVS-Studio警告:V591非void関数は値を返す必要があります。numpunct.hpp528。
インジケーターライブラリ
インジケーター ライブラリの簡単な説明:
最新のC ++のアクティビティインジケーター。
static inline void move_up(int lines) { move(0, -lines); }
static inline void move_down(int lines) { move(0, -lines); } // <=
static inline void move_right(int cols) { move(cols, 0); }
static inline void move_left(int cols) { move(-cols, 0); }
PVS-Studio警告:V524「move_down」関数の本体が「move_up」関数の本体と完全に同等であるのは奇妙です。Indicators.hpp983
これがバグかどうかはわかりません。しかし、コードは非常に疑わしいです。move_up関数がコピーされ、その名前がmove_downに変更された可能性があります。しかし、彼らはマイナスを取り除くのを忘れていました。このコードをチェックする価値があります。
注意。コードが正しい場合は、コードアナライザーだけでなく、このコードを使用または開発したいサードパーティのプログラマーにも誤解を与えることを理解する必要があります。このコードにコメントを追加すると便利です。
マニフライブラリ
マニフ ライブラリの簡単な説明:
manifは、ロボット工学アプリケーションを対象とした状態推定用のヘッダーのみのC ++ 11Lie理論ライブラリです。
template <typename _Derived>
typename LieGroupBase<_Derived>::Scalar*
LieGroupBase<_Derived>::data()
{
return derived().coeffs().data();
}
template <typename _Derived>
const typename LieGroupBase<_Derived>::Scalar*
LieGroupBase<_Derived>::data() const
{
derived().coeffs().data(); // <=
}
PVS-Studio警告:V591非void関数は値を返す必要があります。lie_group_base.h 347
非定数関数は正しく実装されていますが、定数関数は正しく実装されていません。それがどのように起こったかはさらに興味深いです...
FakeItライブラリ
FakeIt ライブラリの簡単な説明:
FakeItは、C ++用の単純なモックフレームワークです。GCC、Clang、MS Visual C ++をサポートしています。FakeItはC ++ 11で記述されており、C ++ 11プロジェクトとC ++プロジェクトの両方のテストに使用できます。
template<typename ... arglist>
struct ArgumentsMatcherInvocationMatcher :
public ActualInvocation<arglist...>::Matcher {
....
template<typename A>
void operator()(int index, A &actualArg) {
TypedMatcher<typename naked_type<A>::type> *matcher =
dynamic_cast<TypedMatcher<typename naked_type<A>::type> *>(
_matchers[index]);
if (_matching)
_matching = matcher->matches(actualArg);
}
....
const std::vector<Destructible *> _matchers;
};
PVS-Studio警告:V522潜在的なヌルポインター「マッチャー」の逆参照がある可能性があります。fakeit.hpp 6720マッチャー
ポインタは、dynamic_cast演算子によって返される値で初期化されます。そして、この演算子はnullptrを返す可能性があり、これは非常にありそうなシナリオです。それ以外の場合は、dynamic_castの代わりにstatic_castを使用する方が効率的です。
状態にタイプミスがある疑いがあり、実際には次のように書く必要があります。
if (matcher)
_matching = matcher->matches(actualArg);
GuiLiteライブラリ
GuiLite ライブラリの簡単な説明:
すべてのプラットフォーム用の最小のヘッダーのみのGUIライブラリ(4 KLOC)。
#define CORRECT(x, high_limit, low_limit) {\
x = (x > high_limit) ? high_limit : x;\
x = (x < low_limit) ? low_limit : x;\
}while(0)
void refresh_wave(unsigned char frame)
{
....
CORRECT(y_min, m_wave_bottom, m_wave_top);
....
}
PVS-Studio警告:V529奇数セミコロン ';' 'while'演算子の後。 GuiLite.h 3413
マクロのエラーは、問題を引き起こしません。しかし、それでも間違いなので、記事で説明することにしました。
マクロで(....)ながら、古典的なパターンdo {...}を使用することが計画されていました。これにより、1つのブロックで複数のアクションを実行すると同時に、関数呼び出しであるかのように、美しさのためにマクロの後にセミコロン ';'を書き込むことができます。
しかし、検討対象のマクロでは、誤ってdoキーワードを書くのを忘れていました。その結果、マクロは、いわば2つの部分に分割されます。最初はブロックです。 2つ目は、空の非実行ループです。while(0); ..。
そして、問題は何ですか?
たとえば、このようなマクロは、次のような構造では使用できません。
if (A)
CORRECT(y_min, m_wave_bottom, m_wave_top);
else
Foo();
このコードは次のように展開されるため、コンパイルされません。
if (A)
{ ..... }
while(0);
else
Foo();
同意します。このような問題は、使用段階ではなく、ライブラリ開発の段階で見つけて修正することをお勧めします。静的コード分析を適用します:)。

PpluXライブラリ
PpluX ライブラリの簡単な説明:
スレッドのスケジューリング、レンダリングなどのための単一ヘッダーC ++ライブラリ..。
struct DisplayList {
DisplayList& operator=(DisplayList &&d) {
data_ = d.data_;
d.data_ = nullptr;
}
....
}
PVS-Studio警告:V591非void関数は値を返す必要があります。px_render.h 398
ユニバーサルライブラリ
ユニバーサルライブラリの簡単な説明:
Universal Numbers(unums)の目標は、IEEEフローティングポイントを、同時実行環境でより効率的で数学的に一貫性のある数値システムに置き換えることです。最初の間違い
template<typename Scalar>
vector<Scalar> operator*(double scalar, const vector<Scalar>& v) {
vector<Scalar> scaledVector(v);
scaledVector *= scalar;
return v;
}
PVS-Studio警告:V1001'scaledVector '変数が割り当てられていますが、関数の最後では使用されていません。vector.hpp124タイプミス
。元のベクトルvの代わりに、関数は新しいベクトル、scaledVectorを返す必要があります。
同様のタイプミスがここに見られます:V1001'normalizedVector '変数が割り当てられていますが、関数の終わりでは使用されていません。vector.hpp 1312
番目のエラー
template<typename Scalar>
class matrix {
....
matrix& diagonal() {
}
....
};
PVS-Studio警告:V591非void関数は値を返す必要があります。matrix.hpp 1093
番目のエラー
template<size_t fbits, size_t abits>
void module_subtract_BROKEN(
const value<fbits>& lhs, const value<fbits>& rhs, value<abits + 1>& result)
{
if (lhs.isinf() || rhs.isinf()) {
result.setinf();
return;
}
int lhs_scale = lhs.scale(),
rhs_scale = rhs.scale(),
scale_of_result = std::max(lhs_scale, rhs_scale);
// align the fractions
bitblock<abits> r1 =
lhs.template nshift<abits>(lhs_scale - scale_of_result + 3);
bitblock<abits> r2 =
rhs.template nshift<abits>(rhs_scale - scale_of_result + 3);
bool r1_sign = lhs.sign(), r2_sign = rhs.sign();
//bool signs_are_equal = r1_sign == r2_sign;
if (r1_sign) r1 = twos_complement(r1);
if (r1_sign) r2 = twos_complement(r2); // <=
....
}
PVS-Studio警告:V581互いに並んで配置されている「if」ステートメントの条件式は同じです。チェック行:789、790。value.hpp790
コピーアンドペーストによって引き起こされる古典的なエラー。彼らは次の行を取り、乗算しました。
if (r1_sign) r1 = twos_complement(r1);
その中でr1をr2に変更しました:
if (r1_sign) r2 = twos_complement(r2);
そして、彼らはr1_signを変更するのを忘れていました。正しいオプション:
if (r2_sign) r2 = twos_complement(r2);
Choboシングルヘッダーライブラリ
Choboシングルヘッダーライブラリの 簡単な説明:
Chobolabsによる小さなシングルヘッダーC ++ 11ライブラリのコレクション。最初の間違い
template <typename T, typename U, typename Alloc = std::allocator<T>>
class vector_view
{
....
vector_view& operator=(vector_view&& other)
{
m_vector = std::move(other.m_vector);
}
....
}
PVS-Studio警告:V591非void関数は値を返す必要があります。vector_view.hpp 1632
番目のエラー
template <typename UAlloc>
vector_view& operator=(const std::vector<U, UAlloc>& other)
{
size_type n = other.size();
resize(n);
for (size_type i = 0; i < n; ++i)
{
this->at(i) = other[i];
}
}
PVS-Studio警告:V591非void関数は値を返す必要があります。vector_view.hpp 184
ライブラリPGM-インデックス
PGMインデックス ライブラリの簡単な説明:
Piecewise Geometric Modelインデックス(PGM-index)は、従来のインデックスよりも数桁少ないスペースを使用して、同じ最悪の場合のクエリ時間の保証を提供しながら、数十億のアイテムの配列で高速ルックアップ、先行、範囲検索、および更新を可能にするデータ構造です..。最初の間違い
char* str_from_errno()
{
#ifdef MSVC_COMPILER
#pragma warning(disable:4996)
return strerror(errno);
#pragma warning(default:4996)
#else
return strerror(errno);
#endif
}
PVS-Studio警告:V665おそらく、このコンテキストでは「#pragma警告(デフォルト:X)」の使用法が正しくありません。代わりに、「#pragma警告(プッシュ/ポップ)」を使用する必要があります。チェック行:9170、9172。sdsl.hpp9172
コンパイラの警告を一時的に無効にするのが正しくありません。このような不正確さは、ユーザーコードにはどういうわけか許されます。ただし、これはヘッダーのみのライブラリでは絶対に許可されていません。
2番目の間違い
template<class t_int_vec>
t_int_vec rnd_positions(uint8_t log_s, uint64_t& mask,
uint64_t mod=0, uint64_t seed=17)
{
mask = (1<<log_s)-1; // <=
t_int_vec rands(1<<log_s ,0);
set_random_bits(rands, seed);
if (mod > 0) {
util::mod(rands, mod);
}
return rands;
}
PVS-Studioの警告:V629「1 << log_s」式の検査を検討してください。32ビット値のビットシフトとそれに続く64ビットタイプへの拡張。sdsl.hpp 1350
正しいオプションの1つ:
mask = ((uint64_t)(1)<<log_s)-1;
Hnswlibライブラリ
Hnswlib ライブラリの簡単な説明:
Pythonバインディングを使用したヘッダーのみのC ++ HNSW実装。HNSW 200MSIFT実験用の紙のコード。
template<typename dist_t>
class BruteforceSearch : public AlgorithmInterface<dist_t> {
public:
BruteforceSearch(SpaceInterface <dist_t> *s, size_t maxElements) {
maxelements_ = maxElements;
data_size_ = s->get_data_size();
fstdistfunc_ = s->get_dist_func();
dist_func_param_ = s->get_dist_func_param();
size_per_element_ = data_size_ + sizeof(labeltype);
data_ = (char *) malloc(maxElements * size_per_element_);
if (data_ == nullptr)
std::runtime_error(
"Not enough memory: BruteforceSearch failed to allocate data");
cur_element_count = 0;
}
....
}
PVS-Studio警告:V596オブジェクトは作成されましたが、使用されていません。'throw'キーワードが欠落している可能性があります:throw runtime_error(FOO); bruteforce.h 26 std :: runtime_errorの
前にthrowステートメントを書くのを忘れました。 別のそのようなエラー:V596オブジェクトは作成されましたが、使用されていません。'throw'キーワードが欠落している可能性があります:throw runtime_error(FOO); bruteforce.h 161
tiny-dnnライブラリ
tiny-dnnライブラリの 簡単な説明:
tiny-dnnは、深層学習のC ++ 14実装です。限られた計算リソース、組み込みシステム、IoTデバイスでの深い学習に適しています。最初の間違い
class nn_error : public std::exception {
public:
explicit nn_error(const std::string &msg) : msg_(msg) {}
const char *what() const throw() override { return msg_.c_str(); }
private:
std::string msg_;
};
inline Device::Device(device_t type, const int platform_id, const int device_id)
: type_(type),
has_clcuda_api_(true),
platform_id_(platform_id),
device_id_(device_id) {
....
#else
nn_error("TinyDNN has not been compiled with OpenCL or CUDA support.");
#endif
}
PVS-Studio警告:V596オブジェクトは作成されましたが、使用されていません。'throw'キーワードが欠落している可能性があります:throw nn_error(FOO); device.h 68
nn_errorは例外をスローする関数ではなく、単なるクラスです。したがって、次のように使用するのが正しいです。
throw nn_error("TinyDNN has not been compiled with OpenCL or CUDA support.");
このクラスのもう1つの誤用:V596オブジェクトは作成されましたが、使用されていません。'throw'キーワードが欠落している可能性があります:throw nn_error(FOO); conv2d_op_opencl.h 1362
番目のエラー
inline std::string format_str(const char *fmt, ...) {
static char buf[2048];
#ifdef _MSC_VER
#pragma warning(disable : 4996)
#endif
va_list args;
va_start(args, fmt);
vsnprintf(buf, sizeof(buf), fmt, args);
va_end(args);
#ifdef _MSC_VER
#pragma warning(default : 4996)
#endif
return std::string(buf);
}
PVS-Studio警告:V665おそらく、このコンテキストでは「#pragma警告(デフォルト:X)」の使用法が正しくありません。代わりに、「#pragma警告(プッシュ/ポップ)」を使用する必要があります。チェックライン:139、146。util.h 146
Dlibライブラリ
Dlib ライブラリの簡単な説明:
Dlibは、現実世界の問題を解決するためにC ++で複雑なソフトウェアを作成するための機械学習アルゴリズムとツールを含む最新のC ++ツールキットです。最初のエラー
楽しみのために、このエラーを自分で見つけてみてください。
class bdf_parser
{
public:
enum bdf_enums
{
NO_KEYWORD = 0,
STARTFONT = 1,
FONTBOUNDINGBOX = 2,
DWIDTH = 4,
DEFAULT_CHAR = 8,
CHARS = 16,
STARTCHAR = 32,
ENCODING = 64,
BBX = 128,
BITMAP = 256,
ENDCHAR = 512,
ENDFONT = 1024
};
....
bool parse_header( header_info& info )
{
....
while ( 1 )
{
res = find_keywords( find | stop );
if ( res & FONTBOUNDINGBOX )
{
in_ >> info.FBBx >> info.FBBy >> info.Xoff >> info.Yoff;
if ( in_.fail() )
return false; // parse_error
find &= ~FONTBOUNDINGBOX;
continue;
}
if ( res & DWIDTH )
{
in_ >> info.dwx0 >> info.dwy0;
if ( in_.fail() )
return false; // parse_error
find &= ~DWIDTH;
info.has_global_dw = true;
continue;
}
if ( res & DEFAULT_CHAR )
{
in_ >> info.default_char;
if ( in_.fail() )
return false; // parse_error
find &= ~DEFAULT_CHAR;
continue;
}
if ( res & NO_KEYWORD )
return false; // parse_error: unexpected EOF
break;
}
....
};
それを見つけた?

彼女はここにいます:
if ( res & NO_KEYWORD )
PVS-Studio警告:V616値0の「NO_KEYWORD」という名前の定数がビット単位の操作で使用されます。fonts.cpp 288
名前付き定数NO_KEYWORDの値は0です。したがって、条件は無意味です。次のように書くのが正しいでしょう:
if ( res == NO_KEYWORD )
別の誤ったチェックがここにあります。V616値0の定数という名前の「NO_KEYWORD」がビット単位の操作で使用されます。fonts.cpp 3342
番目のエラー
void set(std::vector<tensor*> items)
{
....
epa.emplace_back(new enable_peer_access(*g[0], *g[i]));
....
}
PVS-Studio警告:V1023所有者のないポインターが「emplace_back」メソッドによって「epa」コンテナーに追加されます。例外が発生した場合、メモリリークが発生します。tensor_tools.h 1665
キャッチがどこにあるかを理解するために、V1023診断のドキュメントに精通することを提案します。
3番目の間違い
template <
typename detection_type,
typename label_type
>
bool is_track_association_problem (
const std::vector<
std::vector<labeled_detection<detection_type,label_type> > >& samples
)
{
if (samples.size() == 0)
return false;
unsigned long num_nonzero_elements = 0;
for (unsigned long i = 0; i < samples.size(); ++i)
{
if (samples.size() > 0)
++num_nonzero_elements;
}
if (num_nonzero_elements < 2)
return false;
....
}
PVS-Studioの警告:V547式 'samples.size()> 0'は常にtrueです。svm.h 360
これは非常に奇妙なコードです!ループが開始されると、条件(samples.size()> 0)は常に真になります。したがって、ループを簡略化できます。
for (unsigned long i = 0; i < samples.size(); ++i)
{
++num_nonzero_elements;
}
その後、ループはまったく必要ないことが明らかになります。それははるかに簡単かつ効率的に書くことができます:
unsigned long num_nonzero_elements = samples.size();
しかし、それは行われる予定でしたか?コードは明らかにプログラマーによる注意深い研究に値します。
4番目の間違い
class console_progress_indicator
{
....
double seen_first_val;
....
};
bool console_progress_indicator::print_status (
double cur, bool always_print)
{
....
if (!seen_first_val)
{
start_time = cur_time;
last_time = cur_time;
first_val = cur;
seen_first_val = true; // <=
return false;
}
....
}
PVS-Studio警告:V601boolタイプは暗黙的にdoubleタイプにキャストされます。console_progress_indicator.h 136値trueは
、double型のクラスのメンバーに書き込まれます。うーん... 5番目の間違い
void file::init(const std::string& name)
{
....
WIN32_FIND_DATAA data;
HANDLE ffind = FindFirstFileA(state.full_name.c_str(), &data);
if (ffind == INVALID_HANDLE_VALUE ||
(data.dwFileAttributes&FILE_ATTRIBUTE_DIRECTORY) != 0)
{
throw file_not_found("Unable to find file " + name);
}
else
{
....
}
}
PVS-Studio警告:V773「ffind」ハンドルによって参照されるファイルを閉じずに例外がスローされました。リソースリークが発生する可能性があります。dir_nav_kernel_1.cpp 60
ディレクトリが見つかると、例外がスローされます。しかし、誰がハンドルを閉じますか?
6番目の間違い
もう一つの非常に奇妙な場所。
inline double poly_min_extrap(double f0, double d0,
double x1, double f_x1,
double x2, double f_x2)
{
....
matrix<double,2,2> m;
matrix<double,2,1> v;
const double aa2 = x2*x2;
const double aa1 = x1*x1;
m = aa2, -aa1,
-aa2*x2, aa1*x1;
v = f_x1 - f0 - d0*x1,
f_x2 - f0 - d0*x2;
....
}
PVS-Studio警告:V521 '、'演算子を使用したこのような式は危険です。式が正しいことを確認してください。Optimization_line_search.h211
マトリックスを初期化するようにスケジュールされています。ただし、これらすべてのaa2、f_x1、d0などは、double型の変数にすぎません。これは、コンマがマトリックスの作成を目的とした引数を区切るのではなく、右側の値を返す通常のコンマ演算子であることを意味します。
結論
記事の冒頭で、いくつかの便利なものを一度に組み合わせる方法の例を示しました。静的アナライザーの使用は、いくつかの理由で同時に役立ちます。
- トレーニング。アナライザーの警告を調べることで、多くの新しくて便利なことを学ぶことができます。例:memset、#pragma warning、emplace_back、厳密に整列。
- タイプミス、バグ、潜在的な脆弱性の早期発見。
- コードは徐々に良くなり、単純になり、理解しやすくなっています。
- プロジェクトを開発するときは、誇りを持って、最新のテクノロジーを使用していることを全員に伝えることができます:)。そして、これは部分的にユーモアです。これは真の競争上の利点です。
唯一の問題は、どのように開始するか、それを簡単に実装する方法、そしてそれを正しく使用する方法です。次の記事はこれに役立ちます:
- CおよびC ++コード用のPVS-Studioアナライザーによって生成された興味深い警告をすばやく表示するにはどうすればよいですか?
- 静的コードアナライザーをレガシープロジェクトに実装し、チームの意欲を削ぐ方法。
- 静的分析をプロセスに導入します。バグを探すのではありません。

この記事を英語を話す聴衆と共有したい場合は、翻訳リンクを使用してください:AndreyKarpov。ヘッダーのみのC ++ライブラリコレクションのチェック(awesome-hpp)。