Überprüfen einer Sammlung von Nur-Header-C ++ - Bibliotheken (awesome-hpp)

PVS-Studio und Awesome hpp


Durch einen Schicksalsschlag haben wir die meisten Bibliotheken in der Sammlung "Awesome hpp" überprüft. Dies sind kleine C ++ - Projekte, die nur aus Header-Dateien bestehen. Hoffentlich helfen die gefundenen Fehler dabei, diese Bibliotheken ein wenig besser zu machen. Wir freuen uns auch, wenn ihre Autoren den PVS-Studio-Analysator regelmäßig kostenlos nutzen.



Ich mache Sie auf einen Überblick über die Ergebnisse der Überprüfung verschiedener Bibliotheken aufmerksam, die in der Liste awesome-hpp (Eine kuratierte Liste fantastischer C ++ - Bibliotheken nur für Header) aufgeführt sind.



Diese Liste habe ich zum ersten Mal aus dem Podcast " Cross Platform Mobile Telephony " kennengelernt . Bei dieser Gelegenheit empfehle ich allen C ++ - Programmierern, sich mit CppCast vertraut zu machen . CppCast ist der erste Podcast für C ++ - Entwickler von C ++ - Entwicklern!



Trotz der großen Anzahl von Projekten auf der Liste gab es nur sehr wenige Fehler. Dafür gibt es drei Gründe:



  • Dies sind sehr kleine Projekte. Viele bestehen buchstäblich aus einer einzigen Header-Datei.
  • Wir haben nicht alle Projekte überprüft. Es gab Probleme bei der Zusammenstellung einiger von ihnen und wir beschlossen, sie zu überspringen.
  • Um zu verstehen, ob Fehler in Vorlagenklassen / -funktionen vorliegen oder nicht, müssen diese häufig instanziiert werden. Dementsprechend können viele Fehler vom Analysator nur in einem realen Projekt erkannt werden, wenn die Bibliothek aktiv verwendet wird. Wir haben gerade die Header-Dateien in eine leere CPP-Datei aufgenommen und überprüft, wodurch die Überprüfung unwirksam wird.


Während des Studiums der Warnungen gab es jedoch genug davon, um diesen Artikel und einige weitere zu schreiben.



Hinweis an meine Kollegen
, - . . awesome-hpp, :



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




Hinweis für Bibliotheksentwickler. Interessenten können den PVS-Studio Analyzer kostenlos nutzen, um Open Source-Projekte zu überprüfen. Um eine Lizenz für Ihr Open Source-Projekt zu erhalten, füllen Sie bitte dieses Formular aus .



Schauen wir uns nun endlich an, was in einigen Bibliotheken gefunden wurde.



Fehler gefunden



Iutest Bibliothek



Kurzbeschreibung der aktuellsten Bibliothek :
iutest ist ein Framework zum Schreiben von C ++ - Tests.
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- Warnung : V1023 Ein Zeiger ohne Eigentümer wird durch die Methode 'emplace_back' zum Container 'pools' hinzugefügt. Im Ausnahmefall tritt ein Speicherverlust auf. entt.hpp 17114



Dieser Code kann zu Speicherverlusten führen. Wenn der Container eine Neuzuweisung benötigt und keinen Speicher für ein neues Array zuweisen kann, wird eine Ausnahme ausgelöst und der Zeiger geht verloren.



Für Tests ist diese Situation möglicherweise unwahrscheinlich und nicht kritisch. Ich habe mich jedoch entschlossen, dieses Manko zu Bildungszwecken zu erwähnen :).



Richtige Option:



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


Ein weiterer ähnlicher Ort: V1023 Ein Zeiger ohne Eigentümer wird durch die Methode 'emplace_back' zum Container 'pools' hinzugefügt. Im Ausnahmefall tritt ein Speicherverlust auf. entt.hpp 17407



Jsoncons Bibliothek



Eine kurze Beschreibung der jsoncons- Bibliothek :
Eine C ++ - Nur-Header-Bibliothek zum Erstellen von JSON- und JSON-ähnlichen Datenformaten mit JSON-Zeiger, JSON-Patch, JSONPath, JMESPath, CSV, MessagePack, CBOR, BSON, UBJSON.
Der erste Fehler



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- Warnung : V629 Überprüfen Sie den Ausdruck '1 << k'. Bitverschiebung des 32-Bit-Werts mit anschließender Erweiterung auf den 64-Bit-Typ. bigint.hpp 744



Dieser Fehler wurde bereits im Artikel " Warum es wichtig ist, statische Analysen von Open Source-Bibliotheken durchzuführen, die Sie Ihrem Projekt hinzufügen " ausführlich besprochen . Kurz gesagt, um die richtigen Maskenwerte zu erhalten, müssen Sie wie folgt schreiben:



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


Oder so:



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


Der exakt gleiche Fehler wie beim ersten ist hier zu sehen: V629 Überprüfen Sie den Ausdruck '1 << k'. Bitverschiebung des 32-Bit-Werts mit anschließender Erweiterung auf den 64-Bit-Typ. bigint.hpp 779



Zweiter Fehler



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- Warnung : V571 Wiederkehrende Prüfung. Die Bedingung 'if (begin_! = Last_)' wurde bereits in Zeile 1138 überprüft. Unicode_traits.hpp 1140



Seltsamer Wiederholungstest . Es besteht der Verdacht, dass hier ein Tippfehler vorliegt und die zweite Bedingung etwas anders aussehen sollte.



Clipp Bibliothek



Eine kurze Beschreibung der Clipp- Bibliothek :
clipp - Befehlszeilenschnittstellen für modernes C ++. Einfach zu bedienende, leistungsstarke und aussagekräftige Befehlszeilenargumentbehandlung für C ++ 14.11.17 in einer einzigen Header-Datei.
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- Warnung : V547 Ausdruck 's [0] ==' - '' ist immer falsch. clipp.h 303



Nun, in der Tat ist dies kein Fehler, sondern einfach redundanter Code. Die Minusprüfung wird zweimal durchgeführt.



SimpleIni-Bibliothek



Eine kurze Beschreibung der SimpleIni- Bibliothek :
Eine plattformübergreifende Bibliothek, die eine einfache API zum Lesen und Schreiben von Konfigurationsdateien im INI-Stil bietet. Es unterstützt Datendateien in ASCII, MBCS und Unicode.
#if defined(SI_NO_MBSTOWCS_NULL) || (!defined(_MSC_VER) && !defined(_linux))


PVS-Studio- Warnung : V1040 Möglicher Tippfehler bei der Schreibweise eines vordefinierten Makronamens . Das Makro '_linux' ähnelt '__linux'. SimpleIni.h 2923



Wahrscheinlich fehlt dem Namen des _linux- Makros ein Unterstrich, und der Name __linux sollte verwendet werden . In POSIX ist dieses Makro jedoch veraltet und es ist besser, __linux__ zu verwenden .



CSV-Parser-Bibliothek



Eine kurze Beschreibung der CSV-Parser- Bibliothek :
Eine moderne C ++ - Bibliothek zum Lesen, Schreiben und Analysieren von CSV-Dateien (und ähnlichen Dateien).
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- Warnung : V769 Der Zeiger 'buffer.get ()' im Ausdruck 'line_buffer - buffer.get ()' entspricht nullptr. Der resultierende Wert ist sinnlos und sollte nicht verwendet werden. csv.hpp 4957



Eine interessante Situation, die sorgfältige Überlegungen erfordert. Deshalb habe ich beschlossen, eine separate kleine Notiz darüber zu schreiben. Außerdem habe ich beim Experimentieren mit ähnlichem Code einen Fehler in PVS-Studio selbst gefunden :). In einigen Fällen ist es still, obwohl es Warnungen ausgeben sollte.



Kurz gesagt, ob dieser Code funktioniert oder nicht, hängt von der Reihenfolge ab, in der die Argumente ausgewertet werden. Die Reihenfolge, in der die Argumente ausgewertet werden, hängt vom Compiler ab.



PPrint-Bibliothek



Kurzbeschreibung der Bibliothek PPRINT :.
Hübscher Drucker für modernes 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- Warnung : V523 Die Anweisung 'then' entspricht der Anweisung 'else'. pprint.hpp 715



Es ist sehr seltsam, dass dieselbe Aktion unabhängig von der Bedingung ausgeführt wird. Es gibt auch keinen speziellen erklärenden Kommentar. Dies alles ist dem Fehler Kopieren-Einfügen sehr ähnlich.



Ähnliche Warnungen:



  • V523 Die Anweisung 'then' entspricht der Anweisung 'else'. pprint.hpp 780
  • V523 Die Anweisung 'then' entspricht der Anweisung 'else'. pprint.hpp 851
  • V523 Die Anweisung 'then' entspricht der Anweisung 'else'. pprint.hpp 927
  • V523 Die Anweisung 'then' entspricht der Anweisung 'else'. pprint.hpp 1012


Strf Bibliothek



Eine kurze Beschreibung der Strf- Bibliothek :
Eine schnelle C ++ - Formatierungsbibliothek, die die Codierungskonvertierung unterstützt.
Der erste Fehler



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- Warnung : Die Funktion V591 Non-void sollte einen Wert zurückgeben. numpunct.hpp 402



Wir haben vergessen, am Ende der Funktion "return * this;" zu schreiben.



Der zweite ähnliche Fehler



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- Warnung : Die Funktion V591 Non-void sollte einen Wert zurückgeben. numpunct.hpp 528.



Indikatorbibliothek



Kurzbeschreibung der Indikatorbibliothek :
Aktivitätsindikatoren für modernes 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- Warnung : V524 Es ist merkwürdig, dass der Hauptteil der Funktion 'move_down' dem Hauptteil der Funktion 'move_up' vollständig entspricht. Indikatoren.hpp 983



Ich bin nicht sicher, ob dies ein Fehler ist. Aber der Code ist sehr verdächtig. Es ist sehr wahrscheinlich, dass die Funktion move_up kopiert und ihr Name in move_down geändert wurde . Aber sie haben vergessen, das Minus zu entfernen. Es lohnt sich, diesen Code zu überprüfen.



Hinweis. Wenn der Code korrekt ist, müssen Sie verstehen, dass er nicht nur Codeanalysatoren, sondern auch Programmierer von Drittanbietern, die diesen Code verwenden oder entwickeln möchten, in die Irre führt. Es ist nützlich, diesem Code Kommentare hinzuzufügen.



Manif Bibliothek



Eine kurze Beschreibung der Manifest- Bibliothek :
manifest ist eine reine Header-C ++ 11 Lie-Theoriebibliothek zur Zustandsschätzung für Robotikanwendungen.
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- Warnung : Die Funktion V591 Non-void sollte einen Wert zurückgeben. lie_group_base.h 347 Die



nicht konstante Funktion ist korrekt implementiert, die konstante Funktion jedoch nicht. Es ist sogar interessant, wie es passiert ist ...



FakeIt-Bibliothek



Eine kurze Beschreibung der FakeIt- Bibliothek :
FakeIt ist ein einfaches Mocking-Framework für C ++. Es unterstützt GCC, Clang und MS Visual C ++. FakeIt ist in C ++ 11 geschrieben und kann zum Testen von C ++ 11- und C ++ - Projekten verwendet werden.
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- Warnung : V522 Möglicherweise wird ein potenzieller Nullzeiger 'Matcher' dereferenziert. fakeit.hpp 6720



Der Matcher- Zeiger wird mit dem vom Operator dynamic_cast zurückgegebenen Wert initialisiert . Dieser Operator kann nullptr zurückgeben, und dies ist ein sehr wahrscheinliches Szenario. Andernfalls ist es effizienter, static_cast anstelle von dynamic_cast zu verwenden . Es besteht der Verdacht, dass der Zustand einen Tippfehler enthält, und tatsächlich sollte er geschrieben werden:







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


GuiLite-Bibliothek



Eine kurze Beschreibung der GuiLite- Bibliothek :
Die kleinste Nur-Header-GUI-Bibliothek (4 KLOC) für alle Plattformen.
#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- Warnung : V529 Ungerades Semikolon ';' nach 'while'-Operator. GuiLite.h 3413



Ein Fehler in einem Makro führt nicht zu einem Problem. Aber es ist immer noch ein Fehler, deshalb habe ich beschlossen, ihn im Artikel zu beschreiben.



Es war geplant, das klassische Muster do {...} while (....) im Makro zu verwenden . Auf diese Weise können Sie mehrere Aktionen in einem Block ausführen und gleichzeitig ein Semikolon ';' nach dem Makro für Schönheit schreiben, als wäre es ein Funktionsaufruf.



In dem betrachteten Makro haben sie jedoch versehentlich vergessen, das Schlüsselwort do zu schreiben . Infolgedessen schien das Makro in zwei Teile geteilt zu sein. Der erste ist der Block. Die zweite ist eine leere, nicht laufende Schleife: while (0); ...



Und was ist das Problem?



Ein solches Makro kann beispielsweise nicht in einer Konstruktion wie der folgenden verwendet werden:



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


Dieser Code wird nicht kompiliert, da er erweitert wird in:



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


Stimmen Sie zu, es ist besser, ein solches Problem in der Phase der Bibliotheksentwicklung zu finden und zu beheben, und nicht in der Phase seiner Verwendung. Statische Code-Analyse anwenden :).





PpluX-Bibliothek



Kurzbeschreibung der PpluX- Bibliothek :
Einzelne Header-C ++ - Bibliotheken für die Thread-Planung, das Rendern usw.
struct DisplayList {
  DisplayList& operator=(DisplayList &&d) {
    data_ = d.data_;
    d.data_ = nullptr;
  }
  ....
}


PVS-Studio- Warnung : Die Funktion V591 Non-void sollte einen Wert zurückgeben. px_render.h 398



Universelle Bibliothek



Eine kurze Beschreibung der Universalbibliothek:
Das Ziel von Universal Numbers oder Unums ist es, IEEE-Gleitkomma durch ein Zahlensystem zu ersetzen, das in Umgebungen mit gleichzeitiger Ausführung effizienter und mathematisch konsistenter ist.
Der erste Fehler



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


PVS-Studio- Warnung : V1001 Die Variable 'scaledVector' wird zugewiesen, aber am Ende der Funktion nicht verwendet. vector.hpp 124 Tippfehler



. Anstelle des ursprünglichen Vektors v muss die Funktion einen neuen Vektor, scaledVector , zurückgeben .



Ein ähnlicher Tippfehler ist hier zu sehen: V1001 Die Variable 'normalizedVector' wird zugewiesen, aber am Ende der Funktion nicht verwendet. vector.hpp 131



Zweiter Fehler



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


PVS-Studio- Warnung : Die Funktion V591 Non-void sollte einen Wert zurückgeben. matrix.hpp 109



Dritter Fehler



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- Warnung : V581 Die bedingten Ausdrücke der nebeneinander angeordneten if-Anweisungen sind identisch. Überprüfen Sie die Zeilen: 789, 790. value.hpp 790



Ein klassischer Fehler , der durch Kopieren und Einfügen verursacht wird. Sie nahmen und multiplizierten die Linie:



if (r1_sign) r1 = twos_complement(r1);


Wir haben darin r1 in r2 geändert :



if (r1_sign) r2 = twos_complement(r2);


Und sie haben vergessen, r1_sign zu ändern . Richtige Option:



if (r2_sign) r2 = twos_complement(r2);


Chobo-Einzelkopfbibliotheken



Eine kurze Beschreibung der Chobo Single-Header-Bibliotheken :
Eine Sammlung kleiner C ++ 11-Bibliotheken mit einem Header von Chobolabs.
Der erste Fehler



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- Warnung : Die Funktion V591 Non-void sollte einen Wert zurückgeben. vector_view.hpp 163



Zweiter Fehler



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- Warnung : Die Funktion V591 Non-void sollte einen Wert zurückgeben. vector_view.hpp 184



Bibliothek PGM-Index



Kurzbeschreibung der PGM-Indexbibliothek :
Der Piecewise Geometric Model Index (PGM-Index) ist eine Datenstruktur, die eine schnelle Suche, Vorgänger-, Bereichssuche und Aktualisierung in Arrays von Milliarden von Elementen mit um Größenordnungen weniger Speicherplatz als herkömmliche Indizes ermöglicht und gleichzeitig die gleichen Garantien für die Abfragezeit im ungünstigsten Fall bietet ...
Der erste Fehler



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- Warnung : V665 Möglicherweise ist die Verwendung der '# Pragma-Warnung (Standard: X)' in diesem Zusammenhang falsch. Stattdessen sollte die '# Pragma-Warnung (Push / Pop)' verwendet werden. Überprüfen Sie die Zeilen: 9170, 9172. sdsl.hpp 9172



Falsche Warnung zum vorübergehenden Deaktivieren des Compilers. Solche Ungenauigkeiten sind für den Benutzercode irgendwie verzeihbar. Dies ist jedoch in Nur-Header-Bibliotheken definitiv nicht zulässig.



Zweiter Fehler



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- Warnung : V629 Überprüfen Sie den Ausdruck '1 << log_s'. Bitverschiebung des 32-Bit-Werts mit anschließender Erweiterung auf den 64-Bit-Typ. sdsl.hpp 1350



Eine der richtigen Optionen:



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


Hnswlib Bibliothek



Eine kurze Beschreibung der Hnswlib- Bibliothek :
Nur-Header-C ++ - HNSW-Implementierung mit Python-Bindungen. Papiercode für das HNSW 200M SIFT-Experiment.
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- Warnung : V596 Das Objekt wurde erstellt, wird jedoch nicht verwendet. Das Schlüsselwort 'throw' könnte fehlen: throw runtime_error (FOO); bruteforce.h 26 Ich habe



vergessen , eine throw- Anweisung vor std :: runtime_error zu schreiben . Ein weiterer solcher Fehler: V596 Das Objekt wurde erstellt, wird aber nicht verwendet. Das Schlüsselwort 'throw' könnte fehlen: throw runtime_error (FOO); bruteforce.h 161







Die winzige Bibliothek



Eine kurze Beschreibung der tiny-dnn-Bibliothek :
tiny-dnn ist eine C ++ 14-Implementierung von Deep Learning. Es eignet sich für tiefes Lernen auf begrenzten Rechenressourcen, eingebetteten Systemen und IoT-Geräten.
Der erste Fehler



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- Warnung : V596 Das Objekt wurde erstellt, wird jedoch nicht verwendet. Das Schlüsselwort 'throw' könnte fehlen: throw nn_error (FOO); device.h 68



nn_error ist keine Funktion, die eine Ausnahme auslöst , sondern nur eine Klasse. Daher ist es richtig, es so zu verwenden:



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


Ein weiterer Missbrauch dieser Klasse: V596 Das Objekt wurde erstellt, wird aber nicht verwendet. Das Schlüsselwort 'throw' könnte fehlen: throw nn_error (FOO); conv2d_op_opencl.h 136



Zweiter Fehler



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- Warnung : V665 Möglicherweise ist die Verwendung der '# Pragma-Warnung (Standard: X)' in diesem Zusammenhang falsch. Stattdessen sollte die '# Pragma-Warnung (Push / Pop)' verwendet werden. Überprüfen Sie die Zeilen: 139, 146. util.h 146



Dlib-Bibliothek



Eine kurze Beschreibung der Dlib- Bibliothek :
Dlib ist ein modernes C ++ - Toolkit, das Algorithmen und Tools für maschinelles Lernen zum Erstellen komplexer Software in C ++ zur Lösung realer Probleme enthält.
Erster Fehler



Versuchen Sie zum Spaß, diesen Fehler selbst zu finden.



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


Fand es?



Verwirrtes Travolta-Einhorn


Sie ist da:



if ( res & NO_KEYWORD )


PVS-Studio- Warnung : V616 Die benannte Konstante 'NO_KEYWORD' mit dem Wert 0 wird in der bitweisen Operation verwendet. fonts.cpp 288 Die



benannte Konstante NO_KEYWORD hat den Wert 0. Daher ist die Bedingung bedeutungslos. Es wäre richtig zu schreiben:



if ( res == NO_KEYWORD )


Eine weitere falsche Prüfung wird hier gefunden: V616 Die Konstante 'NO_KEYWORD' mit dem Wert 0 wird in der bitweisen Operation verwendet. fonts.cpp 334



Zweiter Fehler



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


PVS-Studio- Warnung : V1023 Ein Zeiger ohne Eigentümer wird dem 'epa'-Container durch die' emplace_back'-Methode hinzugefügt. Im Ausnahmefall tritt ein Speicherverlust auf. tensor_tools.h 1665



Um zu verstehen, wo sich der Haken befindet, sollten Sie sich mit der Dokumentation für die V1023- Diagnose vertraut machen .



Dritter Fehler



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- Warnung : V547 Ausdruck 'samples.size ()> 0' ist immer wahr. svm.h 360



Das ist sehr, sehr seltsamer Code! Wenn eine Schleife startet, ist die Bedingung (samples.size ()> 0) immer wahr. Daher kann die Schleife vereinfacht werden:



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


Danach wird klar, dass die Schleife überhaupt nicht benötigt wird. Es kann viel einfacher und effizienter geschrieben werden:



unsigned long num_nonzero_elements = samples.size();


Aber war es geplant, getan zu werden? Der Code verdient eindeutig eine sorgfältige Prüfung durch einen Programmierer.



Der vierte Fehler



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- Warnung : V601 Der Bool- Typ wird implizit in den Double-Typ umgewandelt. console_progress_indicator.h 136



Der Wert true wird in ein Mitglied der Klasse vom Typ double geschrieben . Hmm ... Fünfter Fehler







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- Warnung : V773 Die Ausnahme wurde ausgelöst, ohne die Datei zu schließen, auf die das Handle 'ffind' verweist. Ein Ressourcenleck ist möglich. dir_nav_kernel_1.cpp 60



Wenn ein Verzeichnis gefunden wird, wird eine Ausnahme ausgelöst. Aber wer wird den Griff schließen?



Der sechste Fehler



Ein weiterer sehr seltsamer Ort.



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- Warnung : V521 Solche Ausdrücke mit dem Operator ',' sind gefährlich. Stellen Sie sicher, dass der Ausdruck korrekt ist. Optimization_line_search.h 211



Geplant zum Initialisieren von Matrizen. Aber alle diese AA2 , f_x1 , d0 und so weiter sind nur Variablen des Doppel Typ . Dies bedeutet, dass die Kommas nicht die Argumente trennen, die zum Erstellen von Matrizen vorgesehen sind, sondern gewöhnliche Kommaoperatoren sind , die den Wert der rechten Seite zurückgeben.



Fazit



Am Anfang des Artikels habe ich ein Beispiel gegeben, wie Sie mehrere nützliche Dinge gleichzeitig kombinieren können. Die Verwendung eines statischen Analysators ist aus mehreren Gründen gleichzeitig nützlich:



  • Die Weiterbildung. Wenn Sie die Warnungen des Analysators studieren, können Sie viele neue und nützliche Dinge lernen. Beispiele: Memset , # Pragma-Warnung , emplace_back , streng ausgerichtet .
  • Früherkennung von Tippfehlern, Fehlern und potenziellen Schwachstellen.
  • Der Code wird allmählich besser, einfacher und verständlicher.
  • Sie können stolz sein und allen sagen, dass Sie bei der Entwicklung von Projekten moderne Technologien verwenden :). Und das ist nur teilweise Humor. Dies ist ein echter Wettbewerbsvorteil.


Die Frage ist nur, wie man anfängt, wie man es schmerzlos implementiert und wie man es richtig benutzt. Die folgenden Artikel helfen Ihnen dabei:







Wenn Sie diesen Artikel einem englischsprachigen Publikum zugänglich machen möchten, verwenden Sie bitte den Übersetzungslink: Andrey Karpov. Überprüfen einer C ++ - Bibliothekssammlung nur für Header (awesome-hpp) .



All Articles