Memeriksa koleksi pustaka C ++ khusus header (awesome-hpp)

PVS-Studio dan hpp Luar Biasa


Secara kebetulan, kami memeriksa sebagian besar perpustakaan di koleksi "Hpp mengagumkan". Ini adalah proyek C ++ kecil yang hanya terdiri dari file header. Semoga bug yang ditemukan akan membantu membuat pustaka ini sedikit lebih baik. Kami juga akan senang jika penulis mereka mulai menggunakan penganalisis PVS-Studio secara gratis secara teratur.



Saya menyampaikan kepada Anda ikhtisar hasil pengujian berbagai pustaka yang tercantum dalam daftar awesome-hpp (A curated of awesome header-only C ++ libraries).



Saya pertama kali mengetahui tentang daftar ini dari podcast " Cross Platform Mobile Telephony ". Mengambil kesempatan ini, saya merekomendasikan semua programmer C ++ untuk mengenal CppCast . CppCast adalah podcast pertama untuk pengembang C ++ oleh pengembang C ++!



Terlepas dari banyaknya proyek dalam daftar, hanya ada sedikit kesalahan. Terdapat tiga alasan untuk ini:



  • Ini adalah proyek yang sangat kecil. Banyak yang secara harfiah terdiri dari satu file header.
  • Kami belum memeriksa semua proyek. Ada masalah dengan kompilasi beberapa di antaranya dan kami memutuskan untuk melewatkannya.
  • Seringkali, untuk memahami apakah ada kesalahan dalam kelas / fungsi template atau tidak, itu harus dibuat. Karenanya, banyak kesalahan dapat dideteksi oleh penganalisis hanya dalam proyek nyata, saat pustaka digunakan secara aktif. Kami baru saja memasukkan file header dalam file .cpp kosong dan diperiksa, yang membuat pemeriksaan tidak efektif.


Namun demikian, dalam proses mempelajari peringatan-peringatan itu, jumlah mereka cukup untuk menulis artikel ini dan beberapa artikel tambahan.



Catatan untuk kolega saya
, - . . awesome-hpp, :



  • , C++11, C++14 C++17;
  • " , ";
  • " — , ";
  • ;
  • (. CSV Parser);
  • , . — , - :);
  • , .




Catatan untuk pengembang perpustakaan. Mereka yang tertarik dapat menggunakan penganalisis PVS-Studio secara gratis untuk memeriksa proyek sumber terbuka. Untuk mendapatkan lisensi untuk proyek sumber terbuka Anda, harap isi formulir ini .



Sekarang mari kita lihat apa yang ditemukan di beberapa perpustakaan.



Kesalahan yang ditemukan



Perpustakaan terindah



Deskripsi singkat dari perpustakaan iutest :
iutest adalah kerangka kerja untuk menulis tes 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);
  ....
}


Peringatan PVS-Studio: V1023 Penunjuk tanpa pemilik ditambahkan ke wadah 'kolam' dengan metode 'emplace_back'. Kebocoran memori akan terjadi jika terjadi pengecualian. entt.hpp 17114



Kode ini berpotensi bocor memori. Jika penampung memerlukan realokasi dan tidak dapat mengalokasikan memori untuk larik baru, ia akan mengeluarkan pengecualian dan penunjuk akan hilang.



Mungkin, untuk pengujian, situasi ini tidak mungkin dan tidak kritis. Namun, saya memutuskan untuk menyebutkan kekurangan ini untuk tujuan pendidikan :).



Opsi yang benar:



pools.emplace_back(std::make_unique<pool_handler<Event>>{})


Tempat lain yang serupa: V1023 Penunjuk tanpa pemilik ditambahkan ke wadah 'kolam' dengan metode 'emplace_back'. Kebocoran memori akan terjadi jika terjadi pengecualian. entt.hpp 17407



Perpustakaan Jsoncons



Penjelasan singkat tentang perpustakaan jsoncons :
C ++, pustaka khusus header untuk membuat format data seperti JSON dan JSON, dengan JSON Pointer, JSON Patch, JSONPath, JMESPath, CSV, MessagePack, CBOR, BSON, UBJSON.
Kesalahan pertama



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;
}


Peringatan PVS-Studio: V629 Pertimbangkan untuk memeriksa ekspresi '1 << k'. Pergeseran bit dari nilai 32-bit dengan perluasan berikutnya ke tipe 64-bit. bigint.hpp 744



Kesalahan ini telah dibahas secara rinci dalam artikel " Mengapa penting untuk melakukan analisis statis pustaka sumber terbuka yang Anda tambahkan ke proyek ". Singkatnya, untuk mendapatkan nilai topeng yang benar, Anda perlu menulis seperti ini:



uint64_t mask = (static_cast<uint64_t>(1) << k) - 1;


Atau seperti ini:



uint64_t mask = (1ull << k) - 1;


Kesalahan yang sama persis seperti yang pertama dapat dilihat di sini: V629 Pertimbangkan untuk memeriksa ekspresi '1 << k'. Pergeseran bit dari nilai 32-bit dengan perluasan berikutnya ke tipe 64-bit. bigint.hpp 779



Kesalahan kedua



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_)
        {
  ....
}


Peringatan PVS-Studio: V571 Pemeriksaan berulang. Kondisi 'if (begin_! = Last_)' telah diverifikasi pada baris 1138. unicode_traits.hpp 1140



Strange retest . Ada kecurigaan bahwa ada kesalahan ketik di sini dan kondisi kedua akan terlihat berbeda.



Perpustakaan Clipp



Penjelasan singkat tentang perpustakaan clipp :
clipp - antarmuka baris perintah untuk C ++ modern. Penanganan argumen baris perintah yang mudah digunakan, kuat, dan ekspresif untuk C ++ 11/14/17 yang terdapat dalam satu file header.
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;
}


Peringatan PVS-Studio: V547 Expression's [0] == '-' 'selalu salah. clipp.h 303



Sebenarnya, ini bukan kesalahan, tetapi hanya kode yang berlebihan. Pemeriksaan minus dilakukan dua kali.



Perpustakaan SimpleIni



Deskripsi singkat tentang pustaka SimpleIni :
Pustaka lintas platform yang menyediakan API sederhana untuk membaca dan menulis file konfigurasi gaya INI. Ini mendukung file data dalam ASCII, MBCS dan Unicode.
#if defined(SI_NO_MBSTOWCS_NULL) || (!defined(_MSC_VER) && !defined(_linux))


Peringatan PVS-Studio: V1040 Kemungkinan salah ketik dalam ejaan nama makro yang telah ditentukan sebelumnya. Makro '_linux' mirip dengan '__linux'. SimpleIni.h 2923



Kemungkinan besar, ada satu garis bawah yang hilang di nama makro _linux dan nama __linux harus digunakan . Namun, di POSIX makro ini tidak digunakan lagi dan lebih baik menggunakan __linux__ .



Perpustakaan Parser CSV



Deskripsi singkat tentang pustaka CSV Parser :
Pustaka C ++ modern untuk membaca, menulis, dan menganalisis file CSV (dan sejenisnya).
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())); // <=
  ....
}


Peringatan PVS-Studio: V769 Penunjuk 'buffer.get ()' dalam ekspresi 'line_buffer - buffer.get ()' sama dengan nullptr. Nilai yang dihasilkan tidak masuk akal dan tidak boleh digunakan. csv.hpp 4957



Situasi menarik yang membutuhkan pertimbangan yang cermat. Oleh karena itu, saya memutuskan untuk menulis catatan kecil yang terpisah tentang ini. Selain itu, saat bereksperimen dengan kode serupa, saya mengidentifikasi kelemahan di PVS-Studio itu sendiri :). Dalam beberapa kasus, itu diam, meskipun harus mengeluarkan peringatan.



Singkatnya, apakah kode ini berfungsi atau tidak tergantung pada urutan argumen dievaluasi. Urutan evaluasi argumen bergantung pada kompilator.



Perpustakaan PPrint



Penjelasan singkat tentang perpustakaan PPRINT :.
Printer Cantik untuk C ++ Modern.
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");
  }
  ....
}


Peringatan PVS-Studio: V523 Pernyataan 'then' sama dengan pernyataan 'else'. pprint.hpp 715



Sangat aneh bahwa tindakan yang sama dilakukan terlepas dari kondisinya. Tidak ada penjelasan khusus juga. Ini semua sangat mirip dengan kesalahan Salin-Tempel.



Peringatan serupa:



  • V523 Pernyataan 'then' setara dengan pernyataan 'else'. pprint.hpp 780
  • V523 Pernyataan 'then' setara dengan pernyataan 'else'. pprint.hpp 851
  • V523 Pernyataan 'then' setara dengan pernyataan 'else'. pprint.hpp 927
  • V523 Pernyataan 'then' setara dengan pernyataan 'else'. pprint.hpp 1012


Perpustakaan strf



Penjelasan singkat tentang perpustakaan Strf :
Pustaka pemformatan C ++ cepat yang mendukung konversi encoding.
Kesalahan pertama



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_;
  }
  ....
};


Peringatan PVS-Studio: V591 Fungsi non-void harus mengembalikan nilai. numpunct.hpp 402



Kita lupa menulis "return * this;" di akhir fungsi.



Kesalahan serupa kedua



template <int Base>
class no_grouping final
{
  constexpr STRF_HD no_grouping& operator=(const no_grouping& other) noexcept
  {
    decimal_point_ = other.decimal_point_;
  }
  ....
}


Peringatan PVS-Studio: V591 Fungsi non-void harus mengembalikan nilai. numpunct.hpp 528.



Pustaka indikator



Deskripsi singkat tentang pustaka Indikator :
Indikator Aktivitas untuk C ++ Modern.
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); }


Peringatan PVS-Studio: V524 Aneh bahwa tubuh fungsi 'move_down' sepenuhnya setara dengan tubuh fungsi 'move_up'. indicator.hpp 983



Saya tidak yakin apakah ini bug. Tapi kodenya sangat mencurigakan. Kemungkinan besar fungsi move_up telah disalin dan namanya diubah menjadi move_down . Tapi mereka lupa menghilangkan minusnya. Layak untuk memeriksa kode ini.



Catatan. Jika kodenya benar, Anda perlu memahami bahwa itu menyesatkan tidak hanya penganalisis kode, tetapi juga pemrogram pihak ketiga yang ingin menggunakan atau mengembangkan kode ini. Berguna untuk menambahkan komentar ke kode ini.



Perpustakaan manif



Penjelasan singkat tentang perpustakaan manif :
manif adalah pustaka teori C ++ 11 Lie header-only untuk estimasi status yang ditargetkan pada aplikasi robotika.
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(); // <=
}


Peringatan PVS-Studio: V591 Fungsi non-void harus mengembalikan nilai. lie_group_base.h 347 Fungsi



non-konstanta diimplementasikan dengan benar, tetapi fungsi konstanta tidak. Bahkan menarik bagaimana itu terjadi ...



Perpustakaan FakeIt



Deskripsi singkat tentang pustaka FakeIt :
FakeIt adalah kerangka kerja tiruan sederhana untuk C ++. Ini mendukung GCC, Clang dan MS Visual C ++. FakeIt ditulis dalam C ++ 11 dan dapat digunakan untuk menguji proyek C ++ 11 dan 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;
};


Peringatan PVS-Studio: V522 Mungkin ada dereferensi dari 'matcher' penunjuk null potensial. fakeit.hpp 6720



The matcher pointer yang diinisialisasi dengan nilai yang dikembalikan oleh dynamic_cast Operator . Dan operator ini dapat mengembalikan nullptr, dan ini adalah skenario yang sangat mungkin terjadi. Jika tidak, akan lebih efisien menggunakan static_cast daripada dynamic_cast . Terdapat dugaan adanya kesalahan ketik pada kondisi tersebut dan sebenarnya harus ditulis:







if (matcher)
  _matching = matcher->matches(actualArg);


Perpustakaan GuiLite



Penjelasan singkat tentang perpustakaan GuiLite :
Pustaka GUI header-only terkecil (4 KLOC) untuk semua platform.
#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);
  ....
}


Peringatan PVS-Studio: V529 Odd titik koma ';' setelah operator 'while'. GuiLite.h 3413



Kesalahan dalam makro tidak menyebabkan beberapa masalah. Tapi itu masih kesalahan, jadi saya memutuskan untuk menjelaskannya di artikel.



Direncanakan untuk menggunakan pola klasik do {...} while (....) di makro . Hal ini memungkinkan Anda untuk melakukan beberapa tindakan dalam satu blok dan pada saat yang sama dapat menulis titik koma ';' setelah makro untuk kecantikan, seolah-olah itu adalah pemanggilan fungsi.



Namun dalam makro yang dianggap, mereka tidak sengaja lupa menulis kata kunci do . Alhasil, makro seolah terbagi menjadi dua bagian. Yang pertama adalah blok. Yang kedua adalah loop kosong yang tidak dieksekusi: while (0); ...



Dan apa masalahnya?



Misalnya, makro seperti itu tidak dapat digunakan dalam konstruksi seperti:



if (A)
  CORRECT(y_min, m_wave_bottom, m_wave_top);
else
  Foo();


Kode ini tidak akan dikompilasi karena akan diperluas menjadi:



if (A)
  { ..... }
while(0);
else
  Foo();


Setuju, lebih baik menemukan dan memperbaiki masalah seperti itu pada tahap pengembangan perpustakaan, dan bukan pada tahap menggunakannya. Terapkan analisis kode statis :).





Perpustakaan PpluX



Deskripsi singkat tentang perpustakaan PpluX :
Library C ++ header tunggal untuk Penjadwalan Thread, Rendering, dan sebagainya ...
struct DisplayList {
  DisplayList& operator=(DisplayList &&d) {
    data_ = d.data_;
    d.data_ = nullptr;
  }
  ....
}


Peringatan PVS-Studio: V591 Fungsi non-void harus mengembalikan nilai. px_render.h 398



Perpustakaan Universal



Penjelasan singkat tentang perpustakaan Universal:
Tujuan Bilangan Universal, atau unums, adalah untuk menggantikan titik mengambang IEEE dengan sistem bilangan yang lebih efisien dan konsisten secara matematis dalam lingkungan eksekusi bersamaan.
Kesalahan pertama



template<typename Scalar>
vector<Scalar> operator*(double scalar, const vector<Scalar>& v) {
  vector<Scalar> scaledVector(v);
  scaledVector *= scalar;
  return v;
}


Peringatan PVS-Studio: V1001 Variabel 'scaledVector' ditetapkan tetapi tidak digunakan di akhir fungsi. vector.hpp 124 Salah ketik



. Alih-alih vektor asli v, fungsi tersebut perlu mengembalikan vektor baru, scaledVector .



Salah ketik yang serupa dapat dilihat di sini: V1001 Variabel 'normalizedVector' ditetapkan tetapi tidak digunakan di akhir fungsi. vector.hpp 131



Kesalahan kedua



template<typename Scalar>
class matrix {
  ....
  matrix& diagonal() {
  }
  ....
};


Peringatan PVS-Studio: V591 Fungsi non-void harus mengembalikan nilai. matrix.hpp 109



Kesalahan ketiga



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);  // <=

  ....
}


Peringatan PVS-Studio: V581 Ekspresi bersyarat dari pernyataan 'jika' yang terletak berdampingan adalah identik. Baris centang: 789, 790. value.hpp 790



Kesalahan klasik yang disebabkan oleh Copy-Paste. Mereka mengambil dan menggandakan garis:



if (r1_sign) r1 = twos_complement(r1);


Kami mengubah r1 menjadi r2 di dalamnya :



if (r1_sign) r2 = twos_complement(r2);


Dan mereka lupa mengubah r1_sign . Opsi yang benar:



if (r2_sign) r2 = twos_complement(r2);


Perpustakaan Chobo Single-Header



Penjelasan singkat tentang Chobo Single-Header Libraries :
Koleksi pustaka C ++ 11 header tunggal kecil oleh Chobolabs.
Kesalahan pertama



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);
  }
  ....
}


Peringatan PVS-Studio: V591 Fungsi non-void harus mengembalikan nilai. vector_view.hpp 163



Kesalahan kedua



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];
  }
}


Peringatan PVS-Studio: V591 Fungsi non-void harus mengembalikan nilai. vector_view.hpp 184



Perpustakaan PGM-index



Deskripsi singkat dari pustaka indeks PGM :
Indeks Model Geometris Sepotong (PGM-index) adalah struktur data yang memungkinkan pencarian cepat, pendahulu, pencarian jangkauan, dan pembaruan dalam larik miliaran item menggunakan urutan besarnya lebih sedikit ruang daripada indeks tradisional sambil memberikan jaminan waktu kueri kasus terburuk yang sama ...
Kesalahan pertama



char* str_from_errno()
{
#ifdef MSVC_COMPILER
  #pragma warning(disable:4996)
  return strerror(errno);
#pragma warning(default:4996)
#else
  return strerror(errno);
#endif
}


Peringatan PVS-Studio: V665 Kemungkinan, penggunaan '#pragma warning (default: X)' salah dalam konteks ini. Sebagai gantinya, '#pragma warning (push / pop)' harus digunakan. Periksa baris: 9170, 9172. sdsl.hpp 9172



Salah untuk sementara menonaktifkan peringatan kompilator. Ketidakakuratan seperti itu entah bagaimana dapat dimaafkan untuk kode pengguna. Tapi ini jelas tidak diperbolehkan di pustaka hanya header.



Kesalahan kedua



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;
}


Peringatan PVS-Studio: V629 Pertimbangkan untuk memeriksa ekspresi '1 << log_s'. Pergeseran bit dari nilai 32-bit dengan perluasan berikutnya ke tipe 64-bit. sdsl.hpp 1350



Salah satu opsi yang benar:



mask = ((uint64_t)(1)<<log_s)-1;


Perpustakaan Hnswlib



Penjelasan singkat tentang perpustakaan Hnswlib :
Implementasi C ++ HNSW khusus header dengan pengikatan python. Kode kertas untuk percobaan HNSW 200M SIFT.
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;
  }
  ....
}


Peringatan PVS-Studio: V596 Objek telah dibuat tetapi tidak sedang digunakan. Kata kunci 'lempar' bisa saja hilang: lempar runtime_error (FOO); bruteforce.h 26



Lupa menulis pernyataan throw sebelum std :: runtime_error . Kesalahan lain seperti: V596 Objek telah dibuat tetapi tidak sedang digunakan. Kata kunci 'lempar' bisa saja hilang: lempar runtime_error (FOO); bruteforce. h 161







Perpustakaan tiny-dnn



Deskripsi singkat tentang library tiny-dnn :
tiny-dnn adalah implementasi pembelajaran mendalam C ++ 14. Ini cocok untuk pembelajaran mendalam pada sumber daya komputasi terbatas, sistem tertanam, dan perangkat IoT.
Kesalahan pertama



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
}


Peringatan PVS-Studio: V596 Objek telah dibuat tetapi tidak sedang digunakan. Kata kunci 'lempar' mungkin hilang: lempar nn_error (FOO); device.h 68



nn_error bukanlah fungsi yang membuat pengecualian, tetapi hanya sebuah kelas. Karena itu, benar menggunakannya seperti ini:



throw nn_error("TinyDNN has not been compiled with OpenCL or CUDA support.");


Penyalahgunaan lain dari kelas ini: V596 Objek telah dibuat tetapi tidak sedang digunakan. Kata kunci 'lempar' mungkin hilang: lempar nn_error (FOO); conv2d_op_opencl.h 136



Kesalahan kedua



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);
}


Peringatan PVS-Studio: V665 Kemungkinan, penggunaan '#pragma warning (default: X)' salah dalam konteks ini. Sebagai gantinya, '#pragma warning (push / pop)' harus digunakan. Periksa baris: 139, 146. util.h 146



Perpustakaan DLIB



Penjelasan singkat tentang perpustakaan Dlib :
Dlib adalah toolkit C ++ modern yang berisi algoritme dan alat pembelajaran mesin untuk membuat perangkat lunak kompleks dalam C ++ untuk memecahkan masalah dunia nyata.
Kesalahan pertama



Untuk bersenang-senang, coba temukan kesalahan ini sendiri.



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;
    }
  ....
};


Menemukannya?



Travolta-Unicorn yang bingung


Dia disini:



if ( res & NO_KEYWORD )


Peringatan PVS-Studio: V616 Konstanta bernama 'NO_KEYWORD' dengan nilai 0 digunakan dalam operasi bitwise. fonts.cpp 288



Konstanta bernama NO_KEYWORD memiliki nilai 0. Oleh karena itu, kondisinya tidak berarti. Adalah benar untuk menulis:



if ( res == NO_KEYWORD )


Pemeriksaan salah lainnya ditemukan di sini: V616 Konstanta bernama 'NO_KEYWORD' dengan nilai 0 digunakan dalam operasi bitwise. fonts.cpp 334



Kesalahan kedua



void set(std::vector<tensor*> items)
{
  ....
  epa.emplace_back(new enable_peer_access(*g[0], *g[i]));
  ....
}


Peringatan PVS-Studio: V1023 Penunjuk tanpa pemilik ditambahkan ke wadah 'epa' dengan metode 'emplace_back'. Kebocoran memori akan terjadi jika terjadi pengecualian. tensor_tools.h 1665



Untuk memahami di mana tangkapannya, saya sarankan agar Anda membiasakan diri dengan dokumentasi untuk diagnostik V1023 .



Kesalahan ketiga



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;
  ....
}


Peringatan PVS-Studio: V547 Expression 'samples.size ()> 0' selalu benar. svm.h 360



Ini adalah kode yang sangat, sangat aneh! Jika perulangan dimulai, maka kondisi (samples.size ()> 0) selalu benar. Karenanya, loop dapat disederhanakan:



for (unsigned long i = 0; i < samples.size(); ++i)
{
  ++num_nonzero_elements;
}


Setelah itu, jelas bahwa loop tidak diperlukan sama sekali. Ini bisa ditulis lebih mudah dan lebih efisien:



unsigned long num_nonzero_elements = samples.size();


Tetapi apakah itu direncanakan untuk dilakukan? Kode tersebut jelas perlu dipelajari dengan cermat oleh seorang programmer.



Kesalahan keempat



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;
  }
  ....
}


Peringatan PVS-Studio: V601 Tipe bool secara implisit ditransmisikan ke tipe ganda. console_progress_indicator.h 136 Nilai true ditulis ke anggota



kelas bertipe double . Hmm ... Kesalahan kelima







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
  {
    ....
  } 
}


Peringatan PVS-Studio: V773 Pengecualian muncul tanpa menutup file yang dirujuk oleh pegangan 'ffind'. Kebocoran sumber daya mungkin terjadi. dir_nav_kernel_1.cpp 60



Jika direktori ditemukan, pengecualian akan dilemparkan. Tapi siapa yang akan menutup pegangannya?



Kesalahan keenam



Tempat lain yang sangat aneh.



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;
  ....
}


Peringatan PVS-Studio: V521 Ekspresi seperti itu yang menggunakan operator ',' berbahaya. Pastikan ekspresinya benar. optimization_line_search.h 211



Dijadwalkan untuk menginisialisasi matriks. Tetapi semua aa2 , f_x1 , d0 dan seterusnya hanyalah variabel tipe ganda . Ini berarti bahwa koma tidak memisahkan argumen yang dimaksudkan untuk membuat matriks, tetapi merupakan operator koma biasa yang mengembalikan nilai sisi kanan.



Kesimpulan



Di awal artikel, saya memberikan contoh bagaimana Anda dapat menggabungkan beberapa hal yang berguna sekaligus. Menggunakan penganalisis statis juga berguna pada saat yang sama karena beberapa alasan:



  • Latihan. Dengan mempelajari peringatan penganalisis, Anda dapat mempelajari banyak hal baru dan berguna. Contoh: memset , #pragma warning , emplace_back , sangat selaras .
  • Deteksi dini kesalahan ketik, bug, dan potensi kerentanan.
  • Kode secara bertahap menjadi lebih baik, lebih sederhana, lebih jelas.
  • Anda dapat bangga dan memberi tahu semua orang bahwa Anda menggunakan teknologi modern saat mengembangkan proyek :). Dan ini hanya sebagian humor. Ini adalah keunggulan kompetitif yang nyata.


Satu-satunya pertanyaan adalah bagaimana memulainya, bagaimana menerapkannya tanpa rasa sakit dan bagaimana menggunakannya dengan benar? Artikel berikut akan membantu Anda dalam hal ini:







Jika Anda ingin berbagi artikel ini dengan audiens berbahasa Inggris, silakan gunakan tautan terjemahan: Andrey Karpov. Memeriksa Koleksi Perpustakaan C ++ Hanya-Header (awesome-hpp) .



All Articles