﻿# Проверка коллекции header\-only C\+\+ библиотек \(awesome\-hpp\)

Волею судьбы мы проверили большинство библиотек, входящих в коллекцию под названием "Awesome hpp"\. Это небольшие проекты на языке C\+\+, состоящие только из заголовочных файлов\. Надеемся, найденные ошибки помогут сделать эти библиотеки немного лучше\. Также мы будем рады, если их авторы начнут бесплатно использовать анализатор PVS\-Studio на регулярной основе\.

![0770_check_awesome_hpp_ru/image1.png](https://import.viva64.com/docx/blog/0770_check_awesome_hpp_ru/image1.png)

Предлагаю вашему вниманию обзор результатов проверки различных библиотек, перечисленных в списке [awesome\-hpp](https://github.com/p-ranav/awesome-hpp) \(A curated list of awesome header\-only C\+\+ libraries\)\.

Впервые про этот список я узнал из подкаста "[Cross Platform Mobile Telephony](https://cppcast.com/telephony-dave-hagedorn/)"\. Пользуясь случаем, рекомендую всем C\+\+ программистам познакомиться с [CppCast](https://cppcast.com/)\. CppCast is the first podcast for C\+\+ developers by C\+\+ developers\!

Несмотря на большое количество проектов в списке, ошибок нашлось совсем немного\. Тому есть три причины:

1. Это очень маленькие проекты\. Многие буквально состоят из одного заголовочного файла\.
1. Мы проверили не все проекты\. С компиляцией некоторых из них возникли проблемы, и мы решили их пропустить\.
1. Часто, чтобы понять, есть ли ошибки в шаблонных классах/функциях или нет, они должны инстанцироваться\. Соответственно многие ошибки смогут быть выявлены анализатором только в настоящем проекте, когда библиотека активно используется\. Мы же просто включали заголовочные файлы в пустой \.cpp файл и проверяли, что делает проверку малоэффективной\.

Тем не менее в процессе изучения предупреждений их набралось достаточно, чтобы написать эту статью и парочку дополнительных\.

**Примечание для моих коллег :\)\.** Я люблю совмещать и достигать несколько полезных результатов, занимаясь каким\-то делом\. Призываю брать пример\. Узнав про существование коллекции awesome\-hpp, я смог реализовать следующие полезные дела:

* поставил задачу пополнить нашу тестовую базу хорошими маленькими современными проектами, написанными с использованием стандартов C\+\+11, C\+\+14 и C\+\+17;
* написал статью "[Почему важно проводить статический анализ открытых библиотек, которые вы добавляете в свой проект](https://pvs-studio.ru/ru/blog/posts/cpp/0762/)";
* написал статью "[Почему обзоры кода — это хорошо, но недостаточно](https://pvs-studio.ru/ru/blog/posts/cpp/0761/)";
* написал эту статью;
* напишу ещё одну заметку на тему порядка вычисления аргументов \(см\. CSV Parser\);
* узнаю, кто внимательно из коллег читает мои публикации\. Кто прочитал это \- приходите, у меня будет подарочная шоколадка или что\-то аналогичное :\);
* показал этим примером, как совмещать несколько полезных занятий\.

**Примечание для разработчиков библиотек\.** Желающие могут бесплатно использовать анализатор PVS\-Studio для проверки открытых проектов\. Для получения лицензии для вашего открытого проекта заполните, пожалуйста, эту [форму](https://pvs-studio.ru/ru/order/open-source-license/)\.

Теперь давайте наконец посмотрим, что нашлось в некоторых библиотеках\.

## Найденные ошибки

### Библиотека iutest

Краткое описание библиотеки [iutest](https://github.com/srz-zumix/iutest):

> iutest is framework for writing C\+\+ tests\.

```cpp
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](https://pvs-studio.ru/ru/docs/warnings/v1023/) A pointer without owner is added to the 'pools' container by the 'emplace\_back' method\. A memory leak will occur in case of an exception\. entt\.hpp 17114

Этот код потенциально может привести к утечке памяти\. В случае, если контейнеру понадобится реаллокация и он не сможет выделить память под новый массив, то он бросит исключение и указатель будет потерян\.

Пожалуй, для тестов эта ситуация маловероятна и некритична\. Однако я решил упомянуть этот недостаток в образовательных целях :\)\.

Правильный вариант:

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

Ещё одно такое же место: V1023 A pointer without owner is added to the 'pools' container by the 'emplace\_back' method\. A memory leak will occur in case of an exception\. entt\.hpp 17407

### Библиотека jsoncons

Краткое описание библиотеки [jsoncons](https://github.com/danielaparker/jsoncons):

> A C\+\+, header\-only library for constructing JSON and JSON\-like data formats, with JSON Pointer, JSON Patch, JSONPath, JMESPath, CSV, MessagePack, CBOR, BSON, UBJSON\.

**Первая ошибка**

```cpp
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](https://pvs-studio.ru/ru/docs/warnings/v629/) Consider inspecting the '1 << k' expression\. Bit shifting of the 32\-bit value with a subsequent expansion to the 64\-bit type\. bigint\.hpp 744

Эта ошибка подробно уже рассматривалась в статье "[Почему важно проводить статический анализ открытых библиотек, которые вы добавляете в свой проект](https://pvs-studio.ru/ru/blog/posts/cpp/0762/)"\. Если совсем кратко, то, чтобы получать корректные значения маски, нужно написать так:

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

Или так:

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

Точно такую же ошибку, как первая, можно увидеть здесь: [V629](https://pvs-studio.ru/ru/docs/warnings/v629/) Consider inspecting the '1 << k' expression\. Bit shifting of the 32\-bit value with a subsequent expansion to the 64\-bit type\. bigint\.hpp 779

**Вторая ошибка**

```cpp
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](https://pvs-studio.ru/ru/docs/warnings/v571/) Recurring check\. The 'if \(begin\_ \!\= last\_\)' condition was already verified in line 1138\. unicode\_traits\.hpp 1140

Странная повторная проверка\. Есть подозрение, что здесь какая\-то опечатка и второе условие должно выглядеть как\-то иначе\.

### Библиотека clipp

Краткое описание библиотеки [clipp](https://github.com/muellan/clipp):

> clipp \- command line interfaces for modern C\+\+\. Easy to use, powerful and expressive command line argument handling for C\+\+11/14/17 contained in a single header file\.

```cpp
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](https://pvs-studio.ru/ru/docs/warnings/v547/) Expression 's\[0\] \=\= '\-'' is always false\. clipp\.h 303

Ну на самом деле это не ошибка, а просто избыточный код\. Проверка на наличие минуса выполняется дважды\.

### Библиотека SimpleIni

Краткое описание библиотеки [SimpleIni](https://github.com/brofield/simpleini):

> A cross\-platform library that provides a simple API to read and write INI\-style configuration files\. It supports data files in ASCII, MBCS and Unicode\.

```cpp
#if defined(SI_NO_MBSTOWCS_NULL) || (!defined(_MSC_VER) && !defined(_linux))
```

Предупреждение PVS\-Studio: [V1040](https://pvs-studio.ru/ru/docs/warnings/v1040/) Possible typo in the spelling of a pre\-defined macro name\. The '\_linux' macro is similar to '\_\_linux'\. SimpleIni\.h 2923

Скорее всего, в имени макроса _\_linux_ не хватает одного подчёркивания и должно использоваться имя _\_\_linux_\. Впрочем, в POSIX этот макрос объявлен устаревшим и лучше использовать _\_\_linux\_\__\.

### Библиотека CSV Parser

Краткое описание библиотеки [CSV Parser](https://github.com/vincentlaucsb/csv-parser):

> A modern C\+\+ library for reading, writing, and analyzing CSV \(and similar\) files\.

```cpp
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](https://pvs-studio.ru/ru/docs/warnings/v769/) The 'buffer\.get\(\)' pointer in the 'line\_buffer \- buffer\.get\(\)' expression equals nullptr\. The resulting value is senseless and it should not be used\. csv\.hpp 4957

Интересная ситуация, которая требует внимательного рассмотрения\. Поэтому я решил, что напишу про это отдельную [маленькую заметку](https://pvs-studio.ru/ru/blog/posts/cpp/0779/)\. Плюс, ставя эксперименты с аналогичным кодом, я выявил недоработку в самом PVS\-Studio :\)\. В некоторых случаях он молчит, хотя должен выдавать предупреждения\.

Если совсем кратко, работает этот код или нет, зависит от порядка вычисления аргументов\. А в каком порядке вычисляются аргументы, зависит от компилятора\.

### Библиотека PPrint

Краткое описание библиотеки [PPrint](https://github.com/p-ranav/pprint):

> Pretty Printer for Modern C\+\+\.

```cpp
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](https://pvs-studio.ru/ru/docs/warnings/v523/) The 'then' statement is equivalent to the 'else' statement\. pprint\.hpp 715

Очень странно, что независимо от условия выполняется одно и то же действие\. Нет и какого\-то специального поясняющего комментария\. Всё это очень похоже на Copy\-Paste ошибку\.

Аналогичные предупреждения:

* V523 The 'then' statement is equivalent to the 'else' statement\. pprint\.hpp 780
* V523 The 'then' statement is equivalent to the 'else' statement\. pprint\.hpp 851
* V523 The 'then' statement is equivalent to the 'else' statement\. pprint\.hpp 927
* V523 The 'then' statement is equivalent to the 'else' statement\. pprint\.hpp 1012

### Библиотека Strf

Краткое описание библиотеки [Strf](https://github.com/robhz786/strf):

> A fast C\+\+ formatting library that supports encoding conversion\.

**Первая ошибка**

```cpp
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](https://pvs-studio.ru/ru/docs/warnings/v591/) Non\-void function should return a value\. numpunct\.hpp 402

В конце функции забыли написать "return \*this;"\.

**Вторая аналогичная ошибка**

```cpp
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](https://pvs-studio.ru/ru/docs/warnings/v591/) Non\-void function should return a value\. numpunct\.hpp 528\.

### Библиотека Indicators

Краткое описание библиотеки [Indicators](https://github.com/p-ranav/indicators):

> Activity Indicators for Modern C\+\+\.

```cpp
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](https://pvs-studio.ru/ru/docs/warnings/v524/) It is odd that the body of 'move\_down' function is fully equivalent to the body of 'move\_up' function\. indicators\.hpp 983

Я не уверен, что это ошибка\. Но код очень подозрительный\. Высока вероятность, что была скопирована функция _move\_up_ и заменено её имя на _move\_down_\. А вот минус удалить забыли\. Стоит проверить этот код\.

**Примечание\.** Если код верен, надо понимать, что он вводит в заблуждение не только анализаторы кода, но и сторонних программистов, которые захотят использовать или развивать этот код\. Полезно сопровождать такой код комментариями\.

### Библиотека manif

Краткое описание библиотеки [manif](https://github.com/artivis/manif):

> **manif** is a header\-only C\+\+11 Lie theory library for state\-estimation targeted at robotics applications\.

```cpp
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](https://pvs-studio.ru/ru/docs/warnings/v591/) Non\-void function should return a value\. lie\_group\_base\.h 347

Неконстантная функция реализована правильно, а константная \- нет\. Интересно даже, как так получилось\.\.\.

### Библиотека FakeIt

Краткое описание библиотеки [FakeIt](https://github.com/eranpeer/FakeIt):

> FakeIt is a simple mocking framework for C\+\+\. It supports GCC, Clang and MS Visual C\+\+\. FakeIt is written in C\+\+11 and can be used for testing both C\+\+11 and C\+\+ projects\.

```cpp
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](https://pvs-studio.ru/ru/docs/warnings/v522/) There might be dereferencing of a potential null pointer 'matcher'\. fakeit\.hpp 6720

Указатель _matcher_ инициализируется значением, которое возвращает оператор _dynamic\_cast_\. А этот оператор может возвращать _nullptr,_ и это весьма вероятный сценарий\. Иначе вместо _dynamic\_cast_ эффективнее использовать _static\_cast_\. 

Есть подозрение, что в условии допущена опечатка и на самом деле должно быть написано:

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

### Библиотека GuiLite

Краткое описание библиотеки [GuiLite](https://github.com/idea4good/GuiLite):

> The smallest header\-only GUI library\(4 KLOC\) for all platforms\.

```cpp
#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](https://pvs-studio.ru/ru/docs/warnings/v529/) Odd semicolon ';' after 'while' operator\. GuiLite\.h 3413

К какой\-то проблеме ошибка в макросе не приводит\. Но всё равно это ошибка, поэтому я решил описать её в статье\.

В макросе планировалось использовать классический паттерн _do \{ \.\.\.\. \} while\(\.\.\.\.\)_\. Это позволяет выполнить несколько действий в одном блоке и при этом иметь возможность для красоты после макроса писать точку с запятой ';', как будто это вызов функции\.

Но в рассмотренном макросе случайно забыли написать ключевое слово _do_\. В результате макрос как\-бы разделился на две части\. Первая \- это блок\. Вторая \- пустой не выполняющийся цикл: _while \(0\);_\.

А в чём, собственно, проблема?

Например, такой макрос нельзя использовать в конструкции вида:

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

Этот код не скомпилируется, так как он будет раскрыт в:

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

Согласитесь, такую проблему лучше найти и исправить на этапе разработки библиотеки, а не на этапе её использования\.  Применяйте статический анализ кода :\)\.

[![getTrialImageLink](https://cdn.pvs-studio.ru/media/get_trial_insert_ru.png)](https://pvs-studio.ru/ru/pvs-studio-download/)

### Библиотека PpluX

Краткое описание библиотеки [PpluX](https://github.com/pplux/px):

> Single header C\+\+ Libraries for Thread Scheduling, Rendering, and so on\.\.\.

```cpp
struct DisplayList {
  DisplayList& operator=(DisplayList &&d) {
    data_ = d.data_;
    d.data_ = nullptr;
  }
  ....
}
```

Предупреждение PVS\-Studio: [V591](https://pvs-studio.ru/ru/docs/warnings/v591/) Non\-void function should return a value\. px\_render\.h 398

### Библиотека Universal

Краткое описание библиотеки Universal:

> The goal of Universal Numbers, or unums, is to replace IEEE floating\-point with a number system that is more efficient and mathematically consistent in concurrent execution environments\.

**Первая ошибка**

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

Предупреждение PVS\-Studio: [V1001](https://pvs-studio.ru/ru/docs/warnings/v1001/) The 'scaledVector' variable is assigned but is not used by the end of the function\. vector\.hpp 124

Опечатка\. Вместо исходного вектора _v_ из функции нужно вернуть новый вектор _scaledVector_\.

Аналогичную опечатку можно увидеть здесь: V1001 The 'normalizedVector' variable is assigned but is not used by the end of the function\. vector\.hpp 131

**Вторая ошибка**

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

Предупреждение PVS\-Studio: [V591](https://pvs-studio.ru/ru/docs/warnings/v591/) Non\-void function should return a value\. matrix\.hpp 109

**Третья ошибка**

```cpp
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](https://pvs-studio.ru/ru/docs/warnings/v581/) The conditional expressions of the 'if' statements situated alongside each other are identical\. Check lines: 789, 790\. value\.hpp 790

Классическая ошибка, возникшая из\-за Copy\-Paste\. Взяли и размножили строчку:

```cpp
if (r1_sign) r1 = twos_complement(r1);
```

Поменяли в ней _r1_ на _r2_:

```cpp
if (r1_sign) r2 = twos_complement(r2);
```

А поменять _r1\_sign_ забыли\. Правильный вариант:

```cpp
if (r2_sign) r2 = twos_complement(r2);
```

### Библиотека Chobo Single\-Header Libraries

Краткое описание библиотеки [Chobo Single\-Header Libraries](https://github.com/Chobolabs/chobo-shl):

> A collection of small single\-header C\+\+11 libraries by Chobolabs\.

**Первая ошибка**

```cpp
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](https://pvs-studio.ru/ru/docs/warnings/v591/) Non\-void function should return a value\. vector\_view\.hpp 163

**Вторая ошибка**

```cpp
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](https://pvs-studio.ru/ru/docs/warnings/v591/) Non\-void function should return a value\. vector\_view\.hpp 184

### Библиотека PGM\-index

Краткое описание библиотеки [PGM\-index](https://github.com/gvinciguerra/PGM-index):

> The Piecewise Geometric Model index \(PGM\-index\) is a data structure that enables fast lookup, predecessor, range searches and updates in arrays of billions of items using orders of magnitude less space than traditional indexes while providing the same worst\-case query time guarantees\.

**Первая ошибка**

```cpp
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](https://pvs-studio.ru/ru/docs/warnings/v665/) Possibly, the usage of '\#pragma warning\(default: X\)' is incorrect in this context\. The '\#pragma warning\(push/pop\)' should be used instead\. Check lines: 9170, 9172\. sdsl\.hpp 9172

Неправильное временное отключение предупреждения компилятора\. Подобные неаккуратности ещё как\-то простительны пользовательскому коду\. Но это точно недопустимо в header\-only библиотеках\.

**Вторая ошибка**

```cpp
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](https://pvs-studio.ru/ru/docs/warnings/v629/) Consider inspecting the '1 << log\_s' expression\. Bit shifting of the 32\-bit value with a subsequent expansion to the 64\-bit type\. sdsl\.hpp 1350

Один из правильных вариантов:

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

### Библиотека Hnswlib

Краткое описание библиотеки [Hnswlib](https://github.com/nmslib/hnswlib):

> Header\-only C\+\+ HNSW implementation with python bindings\. Paper's code for the HNSW 200M SIFT experiment\.

```cpp
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](https://pvs-studio.ru/ru/docs/warnings/v596/) The object was created but it is not being used\. The 'throw' keyword could be missing: throw runtime\_error\(FOO\); bruteforce\.h 26

Забыли перед _std::runtime\_error_ написать оператор _throw_\.

Ещё одна такая ошибка: [V596](https://pvs-studio.ru/ru/docs/warnings/v596/) The object was created but it is not being used\. The 'throw' keyword could be missing: throw runtime\_error\(FOO\); bruteforce\.h 161

### Библиотека tiny\-dnn

Краткое описание библиотеки [tiny\-dnn](https://github.com/tiny-dnn/tiny-dnn):

> tiny\-dnn is a C\+\+14 implementation of deep learning\. It is suitable for deep learning on limited computational resource, embedded systems and IoT devices\.

**Первая ошибка**

```cpp
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](https://pvs-studio.ru/ru/docs/warnings/v596/) The object was created but it is not being used\. The 'throw' keyword could be missing: throw nn\_error\(FOO\); device\.h 68

_nn\_error_ \- это не функция, генерирующая исключение, а просто класс\. Поэтому правильно его использовать так:

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

Ещё одно неправильное использование этого класса: V596 The object was created but it is not being used\. The 'throw' keyword could be missing: throw nn\_error\(FOO\); conv2d\_op\_opencl\.h 136

**Вторая ошибка**

```cpp
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](https://pvs-studio.ru/ru/docs/warnings/v665/) Possibly, the usage of '\#pragma warning\(default: X\)' is incorrect in this context\. The '\#pragma warning\(push/pop\)' should be used instead\. Check lines: 139, 146\. util\.h 146

### Библиотека Dlib

Краткое описание библиотеки [Dlib](http://dlib.net/):

> Dlib is a modern C\+\+ toolkit containing machine learning algorithms and tools for creating complex software in C\+\+ to solve real world problems\.

**Первая ошибка**

Ради интереса попробуйте найти эту ошибку самостоятельно\.

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

Нашли?

![0770_check_awesome_hpp_ru/image2.png](https://import.viva64.com/docx/blog/0770_check_awesome_hpp_ru/image2.png)

Она здесь:

```cpp
if ( res & NO_KEYWORD )
```

Предупреждение PVS\-Studio: [V616](https://pvs-studio.ru/ru/docs/warnings/v616/) The 'NO\_KEYWORD' named constant with the value of 0 is used in the bitwise operation\. fonts\.cpp 288

Именованная константа _NO\_KEYWORD_ имеет значение 0\. А следовательно условие не имеет смысла\. Правильно было бы написать:

```cpp
if ( res == NO_KEYWORD )
```

Ещё одна неправильная проверка находится здесь: V616 The 'NO\_KEYWORD' named constant with the value of 0 is used in the bitwise operation\. fonts\.cpp 334

**Вторая ошибка**

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

Предупреждение PVS\-Studio: [V1023](https://pvs-studio.ru/ru/docs/warnings/v1023/) A pointer without owner is added to the 'epa' container by the 'emplace\_back' method\. A memory leak will occur in case of an exception\. tensor\_tools\.h 1665

Чтобы понять, в чём тут заковыка, предлагаю познакомиться с документацией на диагностику [V1023](https://pvs-studio.ru/ru/docs/warnings/v1023/)\.

**Третья ошибка**

```cpp
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](https://pvs-studio.ru/ru/docs/warnings/v547/) Expression 'samples\.size\(\) \> 0' is always true\. svm\.h 360

Это очень, очень странный код\! Если запускается цикл, то значит условие _\(samples\.size\(\) \> 0\)_ всегда истинно\. Следовательно, цикл можно упростить:

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

После этого становится понятно, что цикл вообще не нужен\. Можно написать гораздо проще и эффективнее:

```cpp
unsigned long num_nonzero_elements = samples.size();
```

Но это ли планировалось сделать? Код явно заслуживает внимательного изучения программистом\.

**Четвёртая ошибка**

```cpp
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: [V601](https://pvs-studio.ru/ru/docs/warnings/v601/) The bool type is implicitly cast to the double type\. console\_progress\_indicator\.h 136

В член класса, имеющий тип _double_, записывают значение _true_\. Хм\.\.\.\.

**Пятая ошибка**

```cpp
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](https://pvs-studio.ru/ru/docs/warnings/v773/) The exception was thrown without closing the file referenced by the 'ffind' handle\. A resource leak is possible\. dir\_nav\_kernel\_1\.cpp 60

Если найдена директория, то генерируется исключение\. Но кто будет закрывать дескриптор?

**Шестая ошибка**

Ещё одно очень странное место\.

```cpp
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](https://pvs-studio.ru/ru/docs/warnings/v521/) Such expressions using the ',' operator are dangerous\. Make sure the expression is correct\. optimization\_line\_search\.h 211

Планируется инициализировать матрицы\. Но ведь все эти _aa2_, _f\_x1_, _d0_ и так далее \- это просто переменные типа _double_\. Значит, запятые не разделяют аргументы, предназначенные для создания матриц, а являются обыкновенными [comma operator](https://en.wikipedia.org/wiki/Comma_operator), которые возвращают значение правой части\.

## Заключение

В начале статьи я привёл пример, как можно совместить несколько полезных дел сразу\. Использование статического анализатора тоже одновременно полезно по нескольким причинам:

1. Повышение квалификации\. Изучая предупреждения анализатора можно узнать много нового и полезного\. Примеры: [memset](https://pvs-studio.ru/ru/docs/warnings/v597/), [\#pragma warning](https://pvs-studio.ru/ru/docs/warnings/v665/), [emplace\_back](https://pvs-studio.ru/ru/docs/warnings/v1023/), [strictly aligned](https://pvs-studio.ru/ru/docs/warnings/v1032/)\.
1. Выявление опечаток, ошибок и потенциальных уязвимостей на ранних этапах\. 
1. Код постепенно становится качественнее, проще, понятней\.
1. Вы можете гордиться и всем рассказывать, что используете современные технологии при разработке проектов :\)\. И это юмор только отчасти\. Это настоящее конкурентное преимущество\.

Вопрос только в том, как начать, как безболезненно внедрить и как правильно использовать? С этим вам помогут следующие статьи:

1. [Как быстро посмотреть интересные предупреждения, которые выдает анализатор PVS\-Studio для C и C\+\+ кода?](https://pvs-studio.ru/ru/blog/posts/cpp/0633/)
1. [Как внедрить статический анализатор кода в legacy проект и не демотивировать команду](https://pvs-studio.ru/ru/blog/posts/0743/)\.
1. [Внедряйте статический анализ в процесс, а не ищите с его помощью баги](https://habr.com/ru/post/436868/)\.