CULTIVA IA NovaTech Analytics S.L. C++17/20 Core Guidelines

Revisión de Código: SensorManager

Auditoría completa con C++ Core Guidelines (isocpp) — Módulo de ingesta IoT
Archivo: sensor_manager.h / .cpp
Estándar objetivo: C++17 → C++20
Revisado: 18 jun 2026
Herramienta: CULTIVA / estandares-de-codigo-cpp
12
Violaciones detectadas
6
Categorías afectadas
3
Riesgos de seguridad
12
Correcciones aplicadas
⚠️
Violaciones detectadas ordenadas por severidad
Regla Severidad Categoría Violación encontrada Corrección aplicada
R.11 · R.20 Alta Gestión de recursos vector<Sensor*> — punteros raw propietarios, fugas garantizadas vector<unique_ptr<Sensor>> — RAII automático
CP.20 Alta Concurrencia lock(mtx) / mtx.unlock() manual — riesgo de deadlock si hay excepción std::lock_guard<std::mutex> lock(mutex_); — RAII lock
I.11 Alta Interfaces add_sensor(Sensor* s) transfiere ownership por puntero raw add_sensor(std::unique_ptr<Sensor> s)
C.35 Alta Clases Destructor no virtual en clase base — UB al borrar a través de puntero base virtual ~SensorManager() = default;
SF.7 Media Archivos fuente using namespace std; en cabecera — contamina todos los TUs que la incluyen Eliminado; uso explícito de std::
Enum.1 Media Enumeraciones #define MAX_SENSORS 256 y #define PI 3.14159 constexpr int k_max_sensors = 256; y constexpr double k_pi = …;
Enum.3 · Enum.5 Media Enumeraciones enum SensorType { TEMPERATURE, HUMIDITY, PRESSURE }; enum class SensorType { temperature, humidity, pressure };
F.20 · F.21 Media Funciones get_reading(int id, double& out, bool& ok) — parámetros de salida std::optional<double> get_reading(int id) const;
C.46 Media Clases Constructor SensorManager(int capacity) sin explicit explicit SensorManager(int capacity);
SL.con.1 Baja Stdlib double readings[256]; — C array en C++ std::array<double, k_max_sensors> readings_{};
Con.1 · ES.25 Baja Inmutabilidad Funciones observadoras sin const get_reading(int id) const y sensor_count() const
ES.20 · ES.23 Baja Expresiones int count = 0; — usar {} y tipo correcto std::size_t count_{};
Cabecera: sensor_manager.h — antes vs. después
✗ Original (con violaciones)
// sensor_manager.h — VIOLACIONES MARCADAS
#pragma once
#include <string>
#include <vector>
#include <mutex>
#include <queue>
using namespace std;  // ❌ SF.7

#define MAX_SENSORS 256  // ❌ Enum.1
#define PI 3.14159       // ❌ Enum.1

class SensorManager {
public:
    SensorManager() {}

    // ❌ I.11: raw pointer ownership
    void add_sensor(Sensor* s);

    // ❌ F.20: output params
    void get_reading(int id,
        double& out_val,
        bool& out_ok);

    // ❌ C.35: no virtual destructor
    ~SensorManager();

    // ❌ C.46: no explicit
    SensorManager(int capacity);

private:
    // ❌ R.3/R.20: owning raw ptrs
    vector<Sensor*> sensors_;
    // ❌ SL.con.1: C array
    double readings[256];
    mutex mtx;
    queue<double> data_q;
    // ❌ ES.23
    int count = 0;
};

// ❌ Enum.3 + Enum.5
enum SensorType {
    TEMPERATURE, HUMIDITY, PRESSURE
};
✓ Refactorizado (C++17/20 idiomático)
// sensor_manager.h — VERSIÓN CORREGIDA
#pragma once
#include <array>
#include <memory>
#include <mutex>
#include <optional>
#include <queue>
#include <string>
#include <vector>
// ✅ Sin "using namespace" (SF.7)

namespace novatech {

// ✅ Enum.1: constexpr en lugar de #define
constexpr std::size_t k_max_sensors{256};
constexpr double k_pi{3.14159265358979};

// ✅ Enum.3 + Enum.5: enum class, no ALL_CAPS
enum class SensorType {
    temperature, humidity, pressure
};

class SensorManager {
public:
    SensorManager() = default;

    // ✅ C.46: explicit
    explicit SensorManager(std::size_t capacity);

    // ✅ C.35: virtual destructor
    virtual ~SensorManager() = default;

    // ✅ I.11: transfiere por unique_ptr
    void add_sensor(std::unique_ptr<Sensor> s);

    // ✅ F.20: return value con optional
    std::optional<double> get_reading(
        std::size_t id) const;

    // ✅ Con.2: observadoras const
    std::size_t sensor_count() const noexcept;

private:
    // ✅ R.20: unique_ptr gestiona ownership
    std::vector<std::unique_ptr<Sensor>> sensors_;
    // ✅ SL.con.1: std::array en lugar de C array
    std::array<double, k_max_sensors> readings_{};
    mutable std::mutex mutex_;
    std::queue<double> data_queue_;
    // ✅ ES.23: {} initializer, size_t
    std::size_t count_{};
};

} // namespace novatech
🔧
Implementación: sensor_manager.cpp — antes vs. después
✗ Original
// sensor_manager.cpp

void SensorManager::get_reading(
    int id, double& out_val, bool& out_ok)
{
    // ❌ CP.20: lock manual sin RAII
    lock(mtx);
    if (id >= 0 && id < count) {
        out_val = readings[id];
        out_ok = true;
    }
    // ❌ Si se lanza excepción arriba,
    //    unlock() no se llama → deadlock
    mtx.unlock();
}

void SensorManager::add_sensor(Sensor* s)
{
    // ❌ R.20: guarda raw pointer
    sensors_.push_back(s);
    count++;
}

// Función libre con violaciones
void process_batch(
    vector<double>& results,
    SensorManager* mgr)  // ❌ I.11
{
    for (int i=0; i<mgr->count; i++) {
        double val; bool ok;
        mgr->get_reading(i, val, ok);
        if (ok) results.push_back(val);
    }
}
✓ Corregido
// sensor_manager.cpp

namespace novatech {

std::optional<double>
SensorManager::get_reading(
    std::size_t id) const
{
    // ✅ CP.20: RAII lock_guard (named)
    std::lock_guard<std::mutex> lock(mutex_);
    if (id < count_) {
        // ✅ F.20: return value (optional)
        return readings_[id];
    }
    return std::nullopt;
    // Si se lanza excepción, lock_guard
    // libera el mutex automáticamente
}

void SensorManager::add_sensor(
    std::unique_ptr<Sensor> s)
{
    std::lock_guard<std::mutex> lock(mutex_);
    // ✅ R.20: unique_ptr gestiona lifetime
    sensors_.push_back(std::move(s));
    ++count_;
}

// ✅ F.16: const ref; I.11: no raw ptr
std::vector<double> process_batch(
    const SensorManager& mgr)
{
    std::vector<double> results;
    results.reserve(mgr.sensor_count());
    for (std::size_t i{}; i < mgr.sensor_count(); ++i) {
        // ✅ F.20: optional, no output params
        if (const auto r = mgr.get_reading(i); r) {
            results.push_back(*r);
        }
    }
    return results;  // NRVO/move
}

} // namespace novatech
🏛️
Principios transversales aplicados en esta revisión
P.8 · R.1
RAII everywhere
Recursos ligados al lifetime del objeto. unique_ptr y lock_guard garantizan liberación en cualquier flujo.
P.10 · Con.1-5
Inmutabilidad por defecto
Funciones observadoras marcadas const. Variables con const y constexpr en lugar de #define.
P.4 · I.4
Seguridad de tipos
enum class, std::optional y std::size_t evitan mezcla de tipos y conversiones implícitas peligrosas.
P.3 · F.1
Expresar intención
std::optional<double> comunica claramente que la lectura puede no existir. Mejor que un bool& de salida.
C.10 · R.3-5
Semántica de valor
process_batch devuelve vector<double> por valor (NRVO). No se usan parámetros de salida con referencias.
F.2-3 · ES.5
Minimizar complejidad
Cada función hace una sola cosa. Scopes pequeños. if-init de C++17 para el optional mantiene el ámbito de r mínimo.
Checklist final de calidad C++ 17/17 ítems verificados tras refactorización
Sin new/delete explícitos — smart pointers
R.11
Objetos inicializados en declaración con {}
ES.20 · ES.23
Variables const/constexpr por defecto
Con.1 · ES.25
Funciones miembro const donde aplica
Con.2
enum class en lugar de enum plano
Enum.3
nullptr en lugar de 0/NULL
ES.47
Sin conversiones narrowing
ES.46
Sin casts estilo C
ES.48
Constructores de 1 arg con explicit
C.46
Rule of Zero (o Five si procede)
C.20 · C.21
Destructor base: public virtual
C.35
Templates con concepts (C++20)
T.10
Sin using namespace en cabeceras
SF.7
Headers con guardas e independientes
SF.8 · SF.11
Locks con RAII (scoped_lock/lock_guard)
CP.20
Sin magic numbers — constantes con nombre
ES.45
'\n' en lugar de std::endl
SL.io.50