Vérification d'une collection de bibliothèques C ++ d'en-tête uniquement (awesome-hpp)

PVS-Studio et Awesome hpp


Par un coup du sort, nous avons vérifié la plupart des bibliothèques incluses dans la collection "Awesome hpp". Ce sont de petits projets C ++ qui se composent uniquement de fichiers d'en-tête. Espérons que les bogues trouvés aideront à rendre ces bibliothèques un peu meilleures. Nous serons également heureux si leurs auteurs commencent à utiliser gratuitement l'analyseur PVS-Studio de manière régulière.



Je porte à votre attention un aperçu des résultats de la vérification de diverses bibliothèques répertoriées dans la liste awesome-hpp (Une liste organisée de bibliothèques C ++ géniales en-tête uniquement).



J'ai découvert cette liste pour la première fois dans le podcast " Cross Platform Mobile Telephony ". Profitant de cette opportunité, je recommande à tous les programmeurs C ++ de se familiariser avec CppCast . CppCast est le premier podcast pour les développeurs C ++ par des développeurs C ++!



Malgré le grand nombre de projets sur la liste, il y a eu très peu d'erreurs. Il y a trois raisons à cela:



  • Ce sont de très petits projets. Beaucoup consistent littéralement en un seul fichier d'en-tête.
  • Nous n'avons pas vérifié tous les projets. Il y a eu des problèmes de compilation de certains d'entre eux et nous avons décidé de les ignorer.
  • Souvent, pour comprendre s'il y a des erreurs dans les classes / fonctions de modèle ou non, elles doivent être instanciées. Par conséquent, de nombreuses erreurs ne peuvent être détectées par l'analyseur que dans un projet réel, lorsque la bibliothèque est activement utilisée. Nous venons d'inclure les fichiers d'en-tête dans un fichier .cpp vide et de les vérifier, ce qui rend la vérification inefficace.


Néanmoins, lors de l'étude des avertissements, il y en avait suffisamment pour écrire cet article et quelques autres.



Note à mes collègues
, - . . awesome-hpp, :



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




Remarque pour les développeurs de bibliothèques. Les personnes intéressées peuvent utiliser gratuitement l'analyseur PVS-Studio pour vérifier les projets open source. Pour obtenir une licence pour votre projet open source, veuillez remplir ce formulaire .



Jetons enfin un œil à ce qui a été trouvé dans certaines bibliothèques.



Erreurs trouvées



Bibliothèque Iutest



Brève description de la bibliothèque iutest :
iutest est un framework pour écrire des tests 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);
  ....
}


Avertissement PVS-Studio: V1023 Un pointeur sans propriétaire est ajouté au conteneur 'pools' par la méthode 'emplace_back'. Une fuite de mémoire se produira en cas d'exception. entt.hpp 17114



Ce code a le potentiel de fuite de mémoire. Si le conteneur a besoin d'une réallocation et qu'il ne peut pas allouer de mémoire pour un nouveau tableau, il lèvera une exception et le pointeur sera perdu.



Peut-être, pour les tests, cette situation est peu probable et pas critique. Cependant, j'ai décidé de mentionner cette lacune à des fins éducatives :).



Option correcte:



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


Autre endroit similaire: V1023 Un pointeur sans propriétaire est ajouté au conteneur 'pools' par la méthode 'emplace_back'. Une fuite de mémoire se produira en cas d'exception. entt.hpp 17407



Bibliothèque Jsoncons



Une brève description de la bibliothèque jsoncons :
Une bibliothèque C ++, en-tête uniquement pour la construction de formats de données JSON et JSON, avec JSON Pointer, JSON Patch, JSONPath, JMESPath, CSV, MessagePack, CBOR, BSON, UBJSON.
La première erreur



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


Avertissement PVS-Studio: V629 Pensez à inspecter l'expression '1 << k'. Décalage de bits de la valeur 32 bits avec une extension ultérieure vers le type 64 bits. bigint.hpp 744



Cette erreur a déjà été abordée en détail dans l'article " Pourquoi il est important de réaliser une analyse statique des bibliothèques open source que vous ajoutez à votre projet ". En bref, pour obtenir les valeurs de masque correctes, vous devez écrire comme ceci:



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


Ou comme ça:



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


On peut voir exactement la même erreur que la première ici: V629 Envisagez d'inspecter l'expression '1 << k'. Décalage de bits de la valeur 32 bits avec une extension ultérieure vers le type 64 bits. bigint.hpp 779



Deuxième erreur



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


Avertissement PVS-Studio: V571 Contrôle récurrent. La condition 'if (begin_! = Last_)' a déjà été vérifiée à la ligne 1138. unicode_traits.hpp 1140 Retest



étrange. Il y a un soupçon qu'il y a une faute de frappe ici et la deuxième condition devrait être différente.



Bibliothèque d'extraits



Une brève description de la bibliothèque d' extraits :
clipp - interfaces de ligne de commande pour C ++ moderne. Gestion des arguments de ligne de commande facile à utiliser, puissante et expressive pour C ++ 14/11/17 contenus dans un seul fichier d'en-tête.
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;
}


Avertissement PVS-Studio: L' expression V547 [0] == '-' 'est toujours fausse. clipp.h 303



Eh bien, en fait, ce n'est pas une erreur, mais simplement du code redondant. Le contrôle négatif est effectué deux fois.



Bibliothèque SimpleIni



Une brève description de la bibliothèque SimpleIni :
Une bibliothèque multiplateforme qui fournit une API simple pour lire et écrire des fichiers de configuration de style INI. Il prend en charge les fichiers de données ASCII, MBCS et Unicode.
#if defined(SI_NO_MBSTOWCS_NULL) || (!defined(_MSC_VER) && !defined(_linux))


Avertissement PVS-Studio: V1040 Erreur de frappe possible dans l'orthographe d'un nom de macro prédéfini. La macro '_linux' est similaire à '__linux'. SimpleIni.h 2923



Très probablement, le nom de la macro _linux manque un trait de soulignement et le nom __linux doit être utilisé . Cependant, dans POSIX, cette macro est obsolète et il est préférable d'utiliser __linux__ .



Bibliothèque d'analyseur CSV



Brève description de la bibliothèque CSV Parser :
Une bibliothèque C ++ moderne pour lire, écrire et analyser des fichiers CSV (et similaires).
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())); // <=
  ....
}


Avertissement PVS-Studio: V769 Le pointeur 'buffer.get ()' dans l'expression 'line_buffer - buffer.get ()' est égal à nullptr. La valeur résultante est insensée et ne doit pas être utilisée. csv.hpp 4957



Une situation intéressante qui nécessite un examen attentif. Par conséquent, j'ai décidé d'écrire une petite note distincte à ce sujet. De plus, en expérimentant avec un code similaire, j'ai trouvé une faille dans PVS-Studio lui-même :). Dans certains cas, il est silencieux, bien qu'il doive émettre des avertissements.



En bref, que ce code fonctionne ou non dépend de l'ordre dans lequel les arguments sont évalués. L'ordre dans lequel les arguments sont évalués dépend du compilateur.



Bibliothèque PPrint



Brève description de la bibliothèque PPRINT :.
Jolie imprimante pour C ++ moderne.
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");
  }
  ....
}


Avertissement PVS-Studio: V523 L' instruction «then» équivaut à l'instruction «else». pprint.hpp 715



Il est très étrange que la même action soit effectuée quelle que soit la condition. Il n'y a pas non plus de commentaire explicatif spécial. Tout cela est très similaire à l'erreur Copy-Paste.



Avertissements similaires:



  • V523 L'instruction 'then' est équivalente à l'instruction 'else'. pprint.hpp 780
  • V523 L'instruction 'then' est équivalente à l'instruction 'else'. pprint.hpp 851
  • V523 L'instruction 'then' est équivalente à l'instruction 'else'. pprint.hpp 927
  • V523 L'instruction 'then' est équivalente à l'instruction 'else'. pprint.hpp 1012


Bibliothèque Strf



Une brève description de la bibliothèque Strf :
Une bibliothèque de mise en forme C ++ rapide qui prend en charge la conversion de codage.
La première erreur



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


Avertissement PVS-Studio: La fonction V591 Non-void doit renvoyer une valeur. numpunct.hpp 402



Nous avons oublié d'écrire "return * this;" à la fin de la fonction.



La deuxième erreur similaire



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


Avertissement PVS-Studio: La fonction V591 Non-void doit renvoyer une valeur. numpunct.hpp 528.



Bibliothèque d'indicateurs



Brève description de la bibliothèque d' indicateurs :
Indicateurs d'activité pour C ++ moderne.
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); }


Avertissement PVS-Studio: V524 Il est étrange que le corps de la fonction 'move_down' soit totalement équivalent au corps de la fonction 'move_up'. indicateurs.hpp 983



Je ne sais pas s'il s'agit d'un bogue. Mais le code est très suspect. Il est fort probable que la fonction move_up ait été copiée et que son nom ait été changé en move_down . Mais ils ont oublié de supprimer le moins. Cela vaut la peine de vérifier ce code.



Remarque. Si le code est correct, vous devez comprendre qu'il induit en erreur non seulement les analyseurs de code, mais également les programmeurs tiers qui souhaitent utiliser ou développer ce code. Il est utile d'ajouter des commentaires à ce code.



Bibliothèque Manif



Une brève description de la bibliothèque manif :
manif est une bibliothèque de théorie de Lie C ++ 11 en en-tête uniquement pour l'estimation d'états ciblée sur les applications robotiques.
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(); // <=
}


Avertissement PVS-Studio: La fonction V591 Non-void doit renvoyer une valeur. lie_group_base.h 347 La fonction



non constante est correctement implémentée, mais la fonction constante ne l'est pas. C'est même intéressant comment c'est arrivé ...



Bibliothèque FakeIt



Une brève description de la bibliothèque FakeIt :
FakeIt est un simple framework de simulation pour C ++. Il prend en charge GCC, Clang et MS Visual C ++. FakeIt est écrit en C ++ 11 et peut être utilisé pour tester des projets C ++ 11 et 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;
};


Avertissement PVS-Studio: V522 Il peut y avoir déréférencement d'un pointeur nul potentiel 'matcher'. fakeit.hpp 6720



Le pointeur de correspondance est initialisé avec la valeur renvoyée par l'opérateur dynamic_cast . Et cet opérateur peut renvoyer nullptr, et c'est un scénario très probable. Sinon, il est plus efficace d'utiliser static_cast au lieu de dynamic_cast . Il y a un soupçon qu'il y a une faute de frappe dans la condition et en fait, il devrait être écrit:







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


Bibliothèque GuiLite



Une brève description de la bibliothèque GuiLite :
La plus petite bibliothèque GUI d'en-tête uniquement (4 KLOC) pour toutes les plates-formes
#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);
  ....
}


Avertissement PVS-Studio: V529 Point-virgule impair ';' après l'opérateur 'while'. GuiLite.h 3413



Une erreur dans une macro ne pose pas de problème. Mais c'est toujours une erreur, j'ai donc décidé de le décrire dans l'article.



Il était prévu d'utiliser le modèle classique do {...} while (....) dans la macro . Cela vous permet d'effectuer plusieurs actions dans un bloc et en même temps d'écrire un point-virgule «;» après la macro pour la beauté, comme s'il s'agissait d'un appel de fonction.



Mais dans la macro considérée, ils ont accidentellement oublié d'écrire le mot - clé do . En conséquence, la macro est, pour ainsi dire, divisée en deux parties. Le premier est le bloc. La seconde est une boucle vide non en cours d'exécution: while (0); ...



Et quel est le problème?



Par exemple, une telle macro ne peut pas être utilisée dans une construction comme:



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


Ce code ne sera pas compilé car il sera développé en:



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


D'accord, il vaut mieux trouver et résoudre un tel problème au stade du développement de la bibliothèque, et non au stade de son utilisation. Appliquez une analyse de code statique :).





Bibliothèque PpluX



Brève description de la bibliothèque PpluX :
Bibliothèques C ++ à en-tête unique pour la planification des threads, le rendu, etc.
struct DisplayList {
  DisplayList& operator=(DisplayList &&d) {
    data_ = d.data_;
    d.data_ = nullptr;
  }
  ....
}


Avertissement PVS-Studio: La fonction V591 Non-void doit renvoyer une valeur. px_render.h 398



Bibliothèque universelle



Une brève description de la bibliothèque universelle:
L'objectif des nombres universels, ou unums, est de remplacer la virgule flottante IEEE par un système de numérotation plus efficace et mathématiquement cohérent dans les environnements d'exécution simultanée.
La première erreur



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


Avertissement PVS-Studio: V1001 La variable 'scaledVector' est affectée mais n'est pas utilisée à la fin de la fonction. vector.hpp 124 Erreur de frappe



. Au lieu du vecteur d'origine v, la fonction doit renvoyer un nouveau vecteur, scaledVector .



Une faute de frappe similaire peut être vue ici: V1001 La variable 'normalizedVector' est affectée mais n'est pas utilisée à la fin de la fonction. vector.hpp 131



Deuxième erreur



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


Avertissement PVS-Studio: La fonction V591 Non-void doit renvoyer une valeur. matrix.hpp 109



Troisième erreur



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

  ....
}


Avertissement PVS-Studio: V581 Les expressions conditionnelles des instructions 'if' situées côte à côte sont identiques. Vérifiez les lignes: 789, 790. value.hpp 790



Une erreur classique causée par Copy-Paste. Ils ont pris et multiplié la ligne:



if (r1_sign) r1 = twos_complement(r1);


Nous avons changé r1 en r2 :



if (r1_sign) r2 = twos_complement(r2);


Et ils ont oublié de changer r1_sign . Option correcte:



if (r2_sign) r2 = twos_complement(r2);


Bibliothèques Chobo à en-tête unique



Une brève description des bibliothèques à en-tête simple Chobo :
Une collection de petites bibliothèques C ++ 11 à en-tête unique par Chobolabs.
La première erreur



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


Avertissement PVS-Studio: La fonction V591 Non-void doit renvoyer une valeur. vector_view.hpp 163



Deuxième erreur



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


Avertissement PVS-Studio: La fonction V591 Non-void doit renvoyer une valeur. vector_view.hpp 184



Index PGM de la bibliothèque



Brève description de la bibliothèque d' index PGM :
L'index Piecewise Geometric Model (PGM-index) est une structure de données qui permet une recherche rapide, des prédécesseurs, des recherches de plage et des mises à jour dans des tableaux de milliards d'éléments en utilisant des ordres de grandeur moins d'espace que les index traditionnels tout en offrant les mêmes garanties de temps de requête dans le pire des cas. ...
La première erreur



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


Avertissement PVS-Studio: V665 Peut-être, l'utilisation de '#pragma warning (par défaut: X)' est incorrecte dans ce contexte. Le '#pragma warning (push / pop)' doit être utilisé à la place. Vérifiez les lignes: 9170, 9172. sdsl.hpp 9172 Avertissement de



désactivation temporaire incorrect du compilateur. De telles inexactitudes sont en quelque sorte pardonnables au code utilisateur. Mais cela n'est certainement pas autorisé dans les bibliothèques d'en-tête uniquement.



Deuxième erreur



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


Avertissement PVS-Studio: V629 Pensez à inspecter l'expression '1 << log_s'. Décalage de bits de la valeur 32 bits avec une extension ultérieure vers le type 64 bits. sdsl.hpp 1350



Une des bonnes options:



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


Bibliothèque Hnswlib



Une brève description de la bibliothèque Hnswlib :
Implémentation HNSW C ++ en en-tête uniquement avec des liaisons python. Code du papier pour l'expérience 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;
  }
  ....
}


Avertissement PVS-Studio: V596 L'objet a été créé mais il n'est pas utilisé. Le mot-clé 'throw' peut être manquant: throw runtime_error (FOO); bruteforce.h 26



J'ai oublié d' écrire une instruction throw avant std :: runtime_error . Une autre erreur de ce type: V596 L'objet a été créé mais il n'est pas utilisé. Le mot clé 'throw' peut être manquant: throw runtime_error (FOO); bruteforce.h 161







La bibliothèque tiny-dnn



Une brève description de la bibliothèque tiny-dnn :
tiny-dnn est une implémentation C ++ 14 du deep learning. Il convient à l'apprentissage en profondeur sur des ressources de calcul limitées, des systèmes embarqués et des appareils IoT.
La première erreur



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
}


Avertissement PVS-Studio: V596 L'objet a été créé mais il n'est pas utilisé. Le mot clé 'throw' est peut-être manquant: throw nn_error (FOO); device.h 68



nn_error n'est pas une fonction qui lève une exception, mais juste une classe. Par conséquent, il est correct de l'utiliser comme ceci:



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


Autre utilisation abusive de cette classe: V596 L'objet a été créé mais il n'est pas utilisé. Le mot clé 'throw' est peut-être manquant: throw nn_error (FOO); conv2d_op_opencl.h 136



Deuxième erreur



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


Avertissement PVS-Studio: V665 Peut-être, l'utilisation de '#pragma warning (par défaut: X)' est incorrecte dans ce contexte. Le '#pragma warning (push / pop)' doit être utilisé à la place. Lignes de contrôle: 139, 146. util.h 146



Bibliothèque Dlib



Une brève description de la bibliothèque Dlib :
Dlib est une boîte à outils C ++ moderne contenant des algorithmes d'apprentissage automatique et des outils pour créer des logiciels complexes en C ++ pour résoudre des problèmes du monde réel.
Première erreur



Pour le plaisir, essayez de trouver cette erreur vous-même.



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


Vous l'avez trouvé?



Travolta-Licorne confuse


Elle est ici:



if ( res & NO_KEYWORD )


Avertissement PVS-Studio: V616 La constante nommée 'NO_KEYWORD' avec la valeur 0 est utilisée dans l'opération au niveau du bit. fonts.cpp 288 La



constante nommée NO_KEYWORD a la valeur 0. Par conséquent, la condition n'a pas de sens. Il serait correct d'écrire:



if ( res == NO_KEYWORD )


Une autre vérification incorrecte est trouvée ici: V616 La constante nommée 'NO_KEYWORD' avec la valeur 0 est utilisée dans l'opération au niveau du bit. fonts.cpp 334



Deuxième erreur



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


Avertissement PVS-Studio: V1023 Un pointeur sans propriétaire est ajouté au conteneur 'epa' par la méthode 'emplace_back'. Une fuite de mémoire se produira en cas d'exception. tensor_tools.h 1665



Pour comprendre où se trouve le piège , je propose de se familiariser avec la documentation du diagnostic V1023 .



Troisième erreur



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


Avertissement PVS-Studio: L' expression V547 'samples.size ()> 0' est toujours vraie. svm.h 360



C'est un code très, très étrange! Si une boucle démarre, alors la condition (samples.size ()> 0) est toujours vraie. Par conséquent, la boucle peut être simplifiée:



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


Après cela, il devient clair que la boucle n'est pas du tout nécessaire. Il peut être écrit beaucoup plus facilement et plus efficacement:



unsigned long num_nonzero_elements = samples.size();


Mais était-ce prévu de le faire? Le code mérite clairement une étude approfondie par un programmeur.



La quatrième erreur



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


Avertissement PVS-Studio: V601 Le type booléen est implicitement converti en type double. console_progress_indicator.h 136



La valeur true est écrite dans un membre de la classe de type double . Hmm ... Cinquième erreur







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


Avertissement PVS-Studio: V773 L'exception a été levée sans fermer le fichier référencé par le handle 'ffind'. Une fuite de ressources est possible. dir_nav_kernel_1.cpp 60



Si un répertoire est trouvé, une exception est levée. Mais qui fermera la poignée?



La sixième erreur



Un autre endroit très étrange.



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


Avertissement PVS-Studio: V521 De telles expressions utilisant l'opérateur ',' sont dangereuses. Assurez-vous que l'expression est correcte. Optimisation_line_search.h 211



Prévu pour initialiser les matrices. Mais tous ces aa2 , f_x1 , d0 etc. ne sont que des variables de type double . Cela signifie que les virgules ne séparent pas les arguments destinés à créer des matrices, mais sont des opérateurs virgules ordinaires qui renvoient la valeur du côté droit.



Conclusion



Au début de l'article, j'ai donné un exemple de la façon dont vous pouvez combiner plusieurs choses utiles à la fois. L'utilisation d'un analyseur statique est également utile en même temps pour plusieurs raisons:



  • Entraînement. En étudiant les avertissements de l'analyseur, vous pouvez apprendre beaucoup de choses nouvelles et utiles. Exemples: memset , #pragma warning , emplace_back , strictement alignés .
  • Détection précoce des fautes de frappe, des bogues et des vulnérabilités potentielles.
  • Le code devient progressivement meilleur, plus simple, plus compréhensible.
  • Vous pouvez être fier et dire à tout le monde que vous utilisez des technologies modernes lors du développement de projets :). Et ce n'est que partiellement de l'humour. C'est un réel avantage concurrentiel.


La seule question est de savoir comment commencer, comment l'implémenter sans douleur et comment l'utiliser correctement? Les articles suivants vous aideront à cela:







Si vous souhaitez partager cet article avec un public anglophone, veuillez utiliser le lien de traduction: Andrey Karpov. Vérification d'une collection de bibliothèques C ++ en-tête uniquement (awesome-hpp) .



All Articles