.. include:: 2026-05-11 (3VO): C++: Inheritance (Mocking A Sensor) ===================================================== .. topic:: See also * From :doc:`/trainings/material/soup/cxx/cxx03/inheritance-oo-design/index` * :doc:`/trainings/material/soup/cxx/cxx03/inheritance-oo-design/basics` * :doc:`/trainings/material/soup/cxx/cxx03/inheritance-oo-design/virtual-method` * :doc:`/trainings/material/soup/cxx/cxx03/inheritance-oo-design/pure-virtual` * :doc:`/trainings/material/soup/cxx/cxx03/inheritance-oo-design/destructor` * :doc:`/trainings/material/soup/cxx/cxx03/inheritance-oo-design/interface` Initial State: Reading And Writing ``sysfs`` Files -------------------------------------------------- .. plantuml:: @startuml class Sensor { + double get_temperature() } note bottom of Sensor: Linux/hwmon\n(though not reflected by its name) class PWMPin { + void set_duty_cycle(uint64_t) + void set_period(uint64_t) } note bottom of PWMPin: Linux/sysfs\n(though not reflected by its name) class Logic { + void loop() } note bottom of Logic: each loop iteration:\n* read temperature\n*update PWM duty cycle accordingly Logic -l-> Sensor Logic -r-> PWMPin @enduml * Hardware based implementation: we are reading and writing ``sysfs`` files * Sensor: ``hwmon`` style; reading, say, ``/sys/class/hwmon/hwmon2/temp1_input`` * PWM: writing, say, ``/sys/class/pwm/pwmchip0/pwm0/period`` and ``/sys/class/pwm/pwmchip0/pwm0/duty_cycle`` Existing Pieces: PWM Pin ------------------------ .. literalinclude:: code/2026-05-11/initial/pwm.h :language: c++ :caption: :download:`code/2026-05-11/initial/pwm.h` .. literalinclude:: code/2026-05-11/initial/pwm.cpp :language: c++ :caption: :download:`code/2026-05-11/initial/pwm.cpp` Existing Pieces: Sensor ----------------------- .. literalinclude:: code/2026-05-11/initial/sensor.h :language: c++ :caption: :download:`code/2026-05-11/initial/sensor.h` .. literalinclude:: code/2026-05-11/initial/sensor.cpp :language: c++ :caption: :download:`code/2026-05-11/initial/sensor.cpp` Existing Pieces: Main Program ----------------------------- .. literalinclude:: code/2026-05-11/initial/main.cpp :language: c++ :caption: :download:`code/2026-05-11/initial/temperature-display.cpp` Existing Pieces: Loop --------------------- .. literalinclude:: code/2026-05-11/initial/logic.h :language: c++ :caption: :download:`code/2026-05-11/initial/logic.h` .. literalinclude:: code/2026-05-11/initial/logic.cpp :language: c++ :caption: :download:`code/2026-05-11/initial/logic.cpp` And Sensor/PWM Alternatives? ---------------------------- * Unplanned for alternatives: ``Logic`` hardwired to exactly these ``sysfs`` hardware interface * Possible alternative sensors * Using ``/dev/i2c-1`` (implementing an I2C sensor's or PWM controller's protocol in userspace) * Likewise, ``/dev/spi*`` * User Space IO (UIO) driver (https://www.kernel.org/doc/html/latest/driver-api/uio-howto.html) * ... Possible Sensor Alternative (Brute Force Approach) -------------------------------------------------- * Sensor type is determined at startup: choose alternative implementation instead * Let logic operate on *that* instead * Intended modification: instantiate alternatives .. literalinclude:: code/2026-05-11/alternatives-nopoly/main.cpp :language: c++ :caption: :download:`code/2026-05-11/alternatives-nopoly/main.cpp` Btw, The Alternative Sensor Implementation ------------------------------------------ .. literalinclude:: code/2026-05-11/alternatives-nopoly/alternative-sensor.h :language: c++ :caption: :download:`code/2026-05-11/alternatives-nopoly/alternative-sensor.h` Alternative: Unintended Modifications ------------------------------------- .. literalinclude:: code/2026-05-11/alternatives-nopoly/logic.h :language: c++ :caption: :download:`code/2026-05-11/alternatives-nopoly/logic.h` A Better Approach For Alternatives: *Interfaces*/*Polymorphic Types* -------------------------------------------------------------------- * ``SensorInterface`` does not implement anything * Just dictates how an implementation must look like * |longrightarrow| *Abstract base class* * ``Logic`` uses the *interface* - **and not a concrete implementation** * Implementations *implement* that interface * |longrightarrow| Implementations can be exchanged, while leaving ``Logic`` unmodified .. plantuml:: @startuml interface SensorInterface { + double get_temperature() } note left of SensorInterface: interface class Sensor { + double get_temperature() } SensorInterface <|.. Sensor note bottom of Sensor: one concrete implementation class AlternativeSensor { + double get_temperature() } SensorInterface <|.. AlternativeSensor note bottom of AlternativeSensor: another concrete implementation class PWMPin { + void set_duty_cycle(uint64_t) + void set_period(uint64_t) } note bottom of PWMPin: (not touched, to be done analogously) class Logic { + void loop() } note top of Logic: uses interface Logic -l-> SensorInterface Logic -r-> PWMPin @enduml Defining An Interface --------------------- .. topic:: See also From :doc:`/trainings/material/soup/cxx/cxx03/inheritance-oo-design/index` * :doc:`/trainings/material/soup/cxx/cxx03/inheritance-oo-design/interface` * :doc:`/trainings/material/soup/cxx/cxx03/inheritance-oo-design/destructor` .. plantuml:: @startuml interface SensorInterface { + double get_temperature() } @enduml .. literalinclude:: code/2026-05-11/alternatives-poly/sensor-interface.h :language: c++ :caption: :download:`code/2026-05-11/alternatives-poly/sensor-interface.h` * *Interfaces* are *only one* usage of C++'s inheritance toolcase * ``virtual``: *dynamic dispatch* Although called (by ``Logic``) via the base class reference/pointer, the call is dispatched to the actual type * ``= 0``: *abstract* method No implementation given |longrightarrow| containing class cannot be instantiated * Virtual destructor |longrightarrow| see :doc:`/trainings/material/soup/cxx/cxx03/inheritance-oo-design/destructor` Implementing An Interface ------------------------- .. plantuml:: @startuml interface SensorInterface { + double get_temperature() } class Sensor { + double get_temperature() } SensorInterface <|.. Sensor class AlternativeSensor { + double get_temperature() } SensorInterface <|.. AlternativeSensor @enduml .. literalinclude:: code/2026-05-11/alternatives-poly/sensor.h :language: c++ :caption: :download:`code/2026-05-11/alternatives-poly/sensor.h` .. literalinclude:: code/2026-05-11/alternatives-poly/alternative-sensor.h :language: c++ :caption: :download:`code/2026-05-11/alternatives-poly/alternative-sensor.h` Assembling Parts ---------------- * Instantiate concrete type * Abstract types cannot be instantiated * Pass concrete object *as their base class* * |longrightarrow| Automatically converted to their base types .. literalinclude:: code/2026-05-11/alternatives-poly/main.cpp :language: c++ :caption: :download:`code/2026-05-11/alternatives-poly/main.cpp` Final Polishing: Names Are Important ------------------------------------ * ``Sensor`` does not say anything about its nature * It is actually Linux specific * Of the many Linux ways to talk to a sensor, it uses the ``hwmon`` (hardware monitoring) subsystem * ``LinuxHWMONSensor`` is probably a better name * ``AlternativeSensor`` has been chosen for didactic purposes, and also says nothing about the nature of the alternative * We want to use such a trivial implementation for automatic tests (``Logic`` does not require actual hardware to be tested) * ``MockSensor`` describes that fact * ``SensorInterface``. Interfaces are an important concept, and the name reflects it well. * It just does not roll well off the tongue * Why not just say ``Sensor``? .. list-table:: * * Before * After * * .. plantuml:: @startuml interface SensorInterface { + double get_temperature() } class Sensor { + double get_temperature() } SensorInterface <|.. Sensor class AlternativeSensor { + double get_temperature() } SensorInterface <|.. AlternativeSensor @enduml * .. plantuml:: @startuml interface Sensor { + double get_temperature() } class LinuxHWMONSensor { + double get_temperature() } Sensor <|.. LinuxHWMONSensor class MockSensor { + double get_temperature() } Sensor <|.. MockSensor @enduml