
Por un giro del destino, comprobamos la mayoría de las bibliotecas de la colección "Awesome hpp". Estos son pequeños proyectos de C ++ que solo constan de archivos de encabezado. Con suerte, los errores encontrados ayudarán a mejorar un poco estas bibliotecas. También estaremos encantados de que sus autores empiecen a utilizar el analizador PVS-Studio de forma gratuita de forma regular.
Traigo a su atención una descripción general de los resultados de la verificación de varias bibliotecas enumeradas en la lista awesome-hpp (Una lista curada de increíbles bibliotecas C ++ solo de encabezado).
Me enteré de esta lista por primera vez en el podcast " Telefonía móvil multiplataforma ". Aprovechando esta oportunidad, recomiendo a todos los programadores de C ++ que se familiaricen con CppCast . ¡CppCast es el primer podcast para desarrolladores de C ++ realizado por desarrolladores de C ++!
A pesar de la gran cantidad de proyectos en la lista, hubo muy pocos errores. Hay tres razones para esto:
- Estos son proyectos muy pequeños. Muchos consisten literalmente en un solo archivo de encabezado.
- No hemos verificado todos los proyectos. Hubo problemas con la compilación de algunos de ellos y decidimos omitirlos.
- A menudo, para comprender si hay errores en las clases / funciones de la plantilla o no, se deben instanciar. En consecuencia, el analizador puede detectar muchos errores solo en un proyecto real, cuando la biblioteca se utiliza activamente. Simplemente incluimos los archivos de encabezado en un archivo .cpp vacío y los verificamos, lo que hace que la verificación sea ineficaz.
Sin embargo, en el proceso de estudio de las advertencias, hubo suficientes para escribir este artículo y un par de más.
Nota para mis colegas
Nota para desarrolladores de bibliotecas. Los interesados pueden utilizar el analizador PVS-Studio de forma gratuita para comprobar proyectos de código abierto. Para obtener una licencia para su proyecto de código abierto, complete este formulario .
Ahora, finalmente echemos un vistazo a lo que se encontró en algunas de las bibliotecas.
Errores encontrados
Biblioteca Iutest
Breve descripción de la biblioteca iutest :
iutest es un marco para escribir pruebas 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);
....
}
Advertencia de PVS-Studio: V1023 Se agrega un puntero sin propietario al contenedor 'pools' mediante el método 'emplace_back'. Se producirá una pérdida de memoria en caso de una excepción. entt.hpp 17114
Este código tiene el potencial de perder memoria. Si el contenedor necesita reasignación y no puede asignar memoria para una nueva matriz, lanzará una excepción y el puntero se perderá.
Quizás, para las pruebas, esta situación sea poco probable y no crítica. Sin embargo, decidí mencionar esta deficiencia con fines educativos :).
Opción correcta:
pools.emplace_back(std::make_unique<pool_handler<Event>>{})
Otro lugar similar: V1023 Se agrega un puntero sin propietario al contenedor 'pools' mediante el método 'emplace_back'. Se producirá una pérdida de memoria en caso de una excepción. entt.hpp 17407
Biblioteca jsoncons
Una breve descripción de la biblioteca jsoncons :
Una biblioteca de solo encabezado de C ++ para construir formatos de datos JSON y similares a JSON, con JSON Pointer, JSON Patch, JSONPath, JMESPath, CSV, MessagePack, CBOR, BSON, UBJSONEl primer error
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;
}
Advertencia de PVS-Studio: V629 Considere inspeccionar la expresión '1 << k'. Desplazamiento de bits del valor de 32 bits con una posterior expansión al tipo de 64 bits. bigint.hpp 744
Este error ya se ha comentado en detalle en el artículo " Por qué es importante realizar un análisis estático de las bibliotecas de código abierto que agrega a su proyecto ". En resumen, para obtener los valores de máscara correctos, debe escribir así:
uint64_t mask = (static_cast<uint64_t>(1) << k) - 1;
O así:
uint64_t mask = (1ull << k) - 1;
Aquí se puede ver exactamente el mismo error que el primero: V629 Considere inspeccionar la expresión '1 << k'. Desplazamiento de bits del valor de 32 bits con una posterior expansión al tipo de 64 bits. bigint.hpp 779
Segundo error
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_)
{
....
}
Advertencia de PVS-Studio: V571 Comprobación recurrente. La condición 'if (begin_! = Last_)' ya se verificó en la línea 1138. unicode_traits.hpp 1140
Extraña nueva prueba . Existe la sospecha de que hay algún error tipográfico aquí y la segunda condición debería tener un aspecto diferente.
Biblioteca de Clipp
Una breve descripción de la biblioteca clipp :
clipp: interfaces de línea de comandos para C ++ moderno. Manejo de argumentos de línea de comando fácil de usar, poderoso y expresivo para C ++ 14/11/17 contenido en un solo archivo de encabezado.
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;
}
Advertencia de PVS-Studio: V547 Expression 's [0] ==' - '' siempre es falsa. clipp.h 303
Bueno, de hecho, esto no es un error, sino simplemente un código redundante. La comprobación negativa se realiza dos veces.
Biblioteca SimpleIni
Una breve descripción de la biblioteca SimpleIni :
Una biblioteca multiplataforma que proporciona una API simple para leer y escribir archivos de configuración de estilo INI. Admite archivos de datos en ASCII, MBCS y Unicode.
#if defined(SI_NO_MBSTOWCS_NULL) || (!defined(_MSC_VER) && !defined(_linux))
Advertencia de PVS-Studio: V1040 Posible error tipográfico en la ortografía de un nombre de macro predefinido. La macro '_linux' es similar a '__linux'. SimpleIni.h 2923
Lo más probable es que al nombre de la macro _linux le falte un guión bajo y se deba usar el nombre __linux . Sin embargo, en POSIX esta macro está en desuso y es mejor usar __linux__ .
Biblioteca del analizador CSV
Breve descripción de la biblioteca CSV Parser :
Una biblioteca C ++ moderna para leer, escribir y analizar archivos CSV (y similares).
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())); // <=
....
}
Advertencia de PVS-Studio: V769 El puntero 'buffer.get ()' en la expresión 'line_buffer - buffer.get ()' es igual a nullptr. El valor resultante no tiene sentido y no debe utilizarse. csv.hpp 4957
Una situación interesante que requiere una consideración cuidadosa. Por lo tanto, decidí que escribiría una pequeña nota por separado sobre esto. Además, mientras experimentaba con un código similar, encontré una falla en PVS-Studio :). En algunos casos, es silencioso, aunque debería emitir advertencias.
En resumen, si este código funciona o no depende del orden en que se evalúen los argumentos. El orden en que se evalúan los argumentos depende del compilador.
Biblioteca de impresiones
Breve descripción de la biblioteca PPRINT :.
Impresora bonita para C ++ moderno.
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");
}
....
}
Advertencia de PVS-Studio: V523 La instrucción 'then' es equivalente a la instrucción 'else'. pprint.hpp 715
Es muy extraño que, independientemente de la condición, se realice la misma acción. Tampoco hay un comentario explicativo especial. Todo esto es muy similar al error Copiar-Pegar.
Advertencias similares:
- V523 La instrucción 'entonces' es equivalente a la instrucción 'else'. pprint.hpp 780
- V523 La instrucción 'entonces' es equivalente a la instrucción 'else'. pprint.hpp 851
- V523 La instrucción 'entonces' es equivalente a la instrucción 'else'. pprint.hpp 927
- V523 La instrucción 'entonces' es equivalente a la instrucción 'else'. pprint.hpp 1012
Biblioteca strf
Una breve descripción de la biblioteca Strf :
Una biblioteca de formato C ++ rápida que admite conversión de codificación.El primer error
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_;
}
....
};
Advertencia de PVS-Studio: La función no nula V591 debe devolver un valor. numpunct.hpp 402 Olvidamos
escribir "return * this;" al final de la función.
El segundo error similar
template <int Base>
class no_grouping final
{
constexpr STRF_HD no_grouping& operator=(const no_grouping& other) noexcept
{
decimal_point_ = other.decimal_point_;
}
....
}
Advertencia de PVS-Studio: La función no nula V591 debe devolver un valor. numpunct.hpp 528.
Biblioteca de indicadores
Breve descripción de la biblioteca de indicadores :
Indicadores de actividad para C ++ moderno.
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); }
Advertencia de PVS-Studio: V524 Es extraño que el cuerpo de la función 'move_down' sea totalmente equivalente al cuerpo de la función 'move_up'. Indicadores.hpp 983
No estoy seguro de si se trata de un error. Pero el código es muy sospechoso. Es muy probable que la función move_up se haya copiado y su nombre se haya cambiado a move_down . Pero se olvidaron de quitar el menos. Vale la pena consultar este código.
Nota. Si el código es correcto, debe comprender que confunde no solo a los analizadores de código, sino también a los programadores de terceros que desean usar o desarrollar este código. Es útil agregar comentarios a este código.
Biblioteca de manifiestos
Una breve descripción de la biblioteca de manifiestos :
manif es una biblioteca de teoría de mentiras C ++ 11 de solo encabezado para la estimación de estados dirigida a aplicaciones robóticas.
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(); // <=
}
Advertencia de PVS-Studio: La función no nula V591 debe devolver un valor. lie_group_base.h 347 La función
no constante está implementada correctamente, pero la función constante no. Incluso es interesante cómo sucedió ...
Biblioteca FakeIt
Una breve descripción de la biblioteca FakeIt :
FakeIt es un marco de simulación simple para C ++. Es compatible con GCC, Clang y MS Visual C ++. FakeIt está escrito en C ++ 11 y se puede utilizar para probar proyectos de C ++ 11 y 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;
};
Advertencia de PVS-Studio: V522 Puede haber desreferenciación de un posible puntero nulo 'comparador'. fakeit.hpp 6720
El puntero del comparador se inicializa con el valor devuelto por el operador dynamic_cast . Y este operador puede devolver nullptr, y este es un escenario muy probable. De lo contrario, es más eficaz utilizar static_cast en lugar de dynamic_cast . Existe la sospecha de que hay un error tipográfico en la condición y, de hecho, debería escribirse:
if (matcher)
_matching = matcher->matches(actualArg);
Biblioteca GuiLite
Una breve descripción de la biblioteca GuiLite :
La biblioteca GUI de encabezado más pequeña (4 KLOC) para todas las plataformas.
#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);
....
}
Advertencia de PVS-Studio: V529 Punto y coma impar ';' después del operador 'while'. GuiLite.h 3413
Un error en una macro no da lugar a ningún problema. Pero sigue siendo un error, así que decidí describirlo en el artículo.
Se planeó utilizar el patrón clásico do {...} while (....) en la macro . Esto le permite realizar varias acciones en un bloque y al mismo tiempo poder escribir un punto y coma ';' después de la macro de belleza, como si fuera una llamada a función.
Pero en la macro considerada, accidentalmente olvidaron escribir la palabra clave do . Como resultado, la macro está, por así decirlo, dividida en dos partes. El primero es el bloque. El segundo es un ciclo vacío que no se ejecuta: while (0); ...
¿Y cuál es el problema?
Por ejemplo, tal macro no se puede utilizar en una construcción como:
if (A)
CORRECT(y_min, m_wave_bottom, m_wave_top);
else
Foo();
Este código no se compilará ya que se expandirá en:
if (A)
{ ..... }
while(0);
else
Foo();
De acuerdo, es mejor encontrar y solucionar este problema en la etapa de desarrollo de la biblioteca y no en la etapa de uso. Aplicar análisis de código estático :).

Biblioteca PpluX
Breve descripción de la biblioteca PpluX :
Bibliotecas C ++ de encabezado único para programación de subprocesos, renderizado, etc.
struct DisplayList {
DisplayList& operator=(DisplayList &&d) {
data_ = d.data_;
d.data_ = nullptr;
}
....
}
Advertencia de PVS-Studio: La función no nula V591 debe devolver un valor. px_render.h 398
Biblioteca universal
Una breve descripción de la biblioteca universal:
El objetivo de Universal Numbers, o unums, es reemplazar el punto flotante IEEE con un sistema numérico que sea más eficiente y matemáticamente consistente en entornos de ejecución concurrente.El primer error
template<typename Scalar>
vector<Scalar> operator*(double scalar, const vector<Scalar>& v) {
vector<Scalar> scaledVector(v);
scaledVector *= scalar;
return v;
}
Advertencia de PVS-Studio: V1001 La variable 'scaledVector' está asignada pero no se utiliza al final de la función. vector.hpp 124 Error tipográfico
. En lugar del vector v original , la función necesita devolver un nuevo vector, scaledVector .
Se puede ver un error tipográfico similar aquí: V1001 La variable 'vector normalizado' se asigna pero no se usa al final de la función. vector.hpp 131
Segundo error
template<typename Scalar>
class matrix {
....
matrix& diagonal() {
}
....
};
Advertencia de PVS-Studio: La función no nula V591 debe devolver un valor. matrix.hpp 109
Tercer error
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); // <=
....
}
Advertencia de PVS-Studio: V581 Las expresiones condicionales de las declaraciones 'if' situadas una junto a la otra son idénticas. Verifique las líneas: 789, 790. value.hpp 790
Un error clásico causado por Copiar-Pegar. Tomaron y multiplicaron la línea:
if (r1_sign) r1 = twos_complement(r1);
Cambiamos r1 a r2 en él :
if (r1_sign) r2 = twos_complement(r2);
Y se olvidaron de cambiar r1_sign . Opción correcta:
if (r2_sign) r2 = twos_complement(r2);
Bibliotecas Chobo de un solo encabezado
Una breve descripción de las bibliotecas de encabezado único Chobo :
Una colección de pequeñas bibliotecas C ++ 11 de un solo encabezado de Chobolabs.El primer error
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);
}
....
}
Advertencia de PVS-Studio: La función no nula V591 debe devolver un valor. vector_view.hpp 163
Segundo error
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];
}
}
Advertencia de PVS-Studio: La función no nula V591 debe devolver un valor. vector_view.hpp 184
Biblioteca PGM-index
Breve descripción de la biblioteca de índices PGM :
El índice de modelo geométrico por partes (PGM-index) es una estructura de datos que permite búsquedas rápidas, predecesoras, búsquedas de rango y actualizaciones en arreglos de miles de millones de elementos utilizando órdenes de magnitud menos espacio que los índices tradicionales, al tiempo que proporciona las mismas garantías de tiempo de consulta en el peor de los casos. ...El primer error
char* str_from_errno()
{
#ifdef MSVC_COMPILER
#pragma warning(disable:4996)
return strerror(errno);
#pragma warning(default:4996)
#else
return strerror(errno);
#endif
}
Advertencia de PVS-Studio: V665 Posiblemente, el uso de '#pragma warning (predeterminado: X)' sea incorrecto en este contexto. En su lugar, debería usarse la 'advertencia de #pragma (push / pop)'. Verifique las líneas: 9170, 9172. sdsl.hpp 9172 Advertencia
incorrecta que deshabilita temporalmente el compilador. Tales inexactitudes son de alguna manera perdonables para el código de usuario. Pero esto definitivamente no está permitido en bibliotecas de solo encabezado.
Segundo error
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;
}
Advertencia de PVS-Studio: V629 Considere inspeccionar la expresión '1 << log_s'. Desplazamiento de bits del valor de 32 bits con una posterior expansión al tipo de 64 bits. sdsl.hpp 1350
Una de las opciones correctas:
mask = ((uint64_t)(1)<<log_s)-1;
Biblioteca Hnswlib
Una breve descripción de la biblioteca Hnswlib :
Implementación de HNSW de C ++ solo de encabezado con enlaces de Python. Código de papel para el experimento 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;
}
....
}
Advertencia de PVS-Studio: V596 El objeto fue creado pero no se está utilizando. Es posible que falte la palabra clave 'throw': throw runtime_error (FOO); bruteforce.h 26
Olvidé escribir una declaración de lanzamiento antes de std :: runtime_error . Otro error similar : V596 Se creó el objeto pero no se está utilizando. Es posible que falte la palabra clave 'throw': throw runtime_error (FOO); fuerza bruta.h 161
La biblioteca tiny-dnn
Una breve descripción de la biblioteca tiny-dnn :
tiny-dnn es una implementación C ++ 14 de aprendizaje profundo. Es adecuado para el aprendizaje profundo en recursos computacionales limitados, sistemas integrados y dispositivos de IoT.El primer error
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
}
Advertencia de PVS-Studio: V596 El objeto fue creado pero no se está utilizando. Podría faltar la palabra clave 'lanzar': throw nn_error (FOO); device.h 68
nn_error no es una función que lanza una excepción, sino solo una clase. Por lo tanto, es correcto usarlo así:
throw nn_error("TinyDNN has not been compiled with OpenCL or CUDA support.");
Otro mal uso de esta clase: V596 El objeto fue creado pero no se está utilizando. Es posible que falte la palabra clave 'throw': throw nn_error (FOO); conv2d_op_opencl.h 136
Segundo error
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);
}
Advertencia de PVS-Studio: V665 Posiblemente, el uso de '#pragma warning (predeterminado: X)' sea incorrecto en este contexto. En su lugar, debería usarse la 'advertencia de #pragma (push / pop)'. Compruebe las líneas: 139, 146. util.h 146
Biblioteca Dlib
Una breve descripción de la biblioteca Dlib :
Dlib es un conjunto de herramientas moderno de C ++ que contiene algoritmos y herramientas de aprendizaje automático para crear software complejo en C ++ para resolver problemas del mundo real.Primer error
Para divertirse, intente encontrar este error usted mismo.
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;
}
....
};
¿Lo encontré?

Ella está aquí:
if ( res & NO_KEYWORD )
Advertencia de PVS-Studio: V616 La constante denominada 'NO_KEYWORD' con el valor 0 se utiliza en la operación bit a bit. fonts.cpp 288 La
constante denominada NO_KEYWORD tiene el valor 0. Por lo tanto, la condición no tiene sentido. Sería correcto escribir:
if ( res == NO_KEYWORD )
Aquí se encuentra otra comprobación incorrecta: V616 La constante denominada 'NO_KEYWORD' con el valor 0 se utiliza en la operación bit a bit. fonts.cpp 334
Segundo error
void set(std::vector<tensor*> items)
{
....
epa.emplace_back(new enable_peer_access(*g[0], *g[i]));
....
}
Advertencia de PVS-Studio: V1023 Se agrega un puntero sin propietario al contenedor 'epa' mediante el método 'emplace_back'. Se producirá una pérdida de memoria en caso de una excepción. tensor_tools.h 1665
Para comprender dónde está el problema, propongo familiarizarse con la documentación para los diagnósticos de V1023 .
Tercer error
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;
....
}
Advertencia de PVS-Studio: la expresión V547 'samples.size ()> 0' siempre es verdadera. svm.h 360 ¡
Este es un código muy, muy extraño! Si se inicia un bucle, entonces la condición (samples.size ()> 0) siempre es verdadera. Por lo tanto, el ciclo se puede simplificar:
for (unsigned long i = 0; i < samples.size(); ++i)
{
++num_nonzero_elements;
}
Después de eso, queda claro que el bucle no es necesario en absoluto. Se puede escribir mucho más fácil y eficientemente:
unsigned long num_nonzero_elements = samples.size();
¿Pero estaba planeado que se hiciera? El código claramente merece un estudio cuidadoso por parte de un programador.
El cuarto error
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;
}
....
}
Advertencia de PVS-Studio: V601 El tipo bool se convierte implícitamente en el tipo doble. console_progress_indicator.h 136
El valor verdadero se escribe en un miembro de la clase de tipo double . Hmm ... Quinto error
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
{
....
}
}
Advertencia de PVS-Studio: V773 La excepción se lanzó sin cerrar el archivo al que hace referencia el identificador 'ffind'. Es posible una fuga de recursos. dir_nav_kernel_1.cpp 60
Si se encuentra un directorio, se lanza una excepción. ¿Pero quién cerrará la manija?
El sexto error
Otro lugar muy extraño.
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;
....
}
Advertencia de PVS-Studio: V521 Tales expresiones que utilizan el operador ',' son peligrosas. Asegúrate de que la expresión sea correcta. optimization_line_search.h 211
Planificado para inicializar matrices. Pero todos estos aa2 , f_x1 , d0 y así sucesivamente son solo variables de tipo doble . Esto significa que las comas no separan los argumentos destinados a crear matrices, sino que son operadores de coma ordinarios que devuelven el valor del lado derecho.
Conclusión
Al comienzo del artículo, di un ejemplo de cómo se pueden combinar varias cosas útiles a la vez. El uso de un analizador estático también es útil al mismo tiempo por varias razones:
- Formación. Al estudiar las advertencias del analizador, puede aprender muchas cosas nuevas y útiles. Ejemplos: memset , #pragma warning , emplace_back , estrictamente alineado .
- Detección temprana de errores tipográficos, errores y vulnerabilidades potenciales.
- El código se vuelve gradualmente mejor, más simple y más comprensible.
- Puede estar orgulloso y decirles a todos que está utilizando tecnologías modernas al desarrollar proyectos :). Y esto es solo en parte humor. Ésta es una ventaja competitiva real.
La única pregunta es cómo empezar, cómo implementarlo sin dolor y cómo usarlo correctamente. Los siguientes artículos te ayudarán con esto:
- ¿Cómo ver rápidamente las advertencias interesantes generadas por el analizador PVS-Studio para código C y C ++?
- Cómo implementar un analizador de código estático en un proyecto heredado y no desmotivar al equipo .
- Introduzca el análisis estático en su proceso, en lugar de buscar errores con él .

Si desea compartir este artículo con una audiencia de habla inglesa, utilice el enlace de traducción: Andrey Karpov. Comprobación de una colección de bibliotecas C ++ de solo encabezado (awesome-hpp) .