Extracting type info

Suppose that I want to implement the following scenario. There is a board and each board has a set of peripherals like leds, temp sensors, gpio and so on. Each of them implements the according C++ interface: Led, Sensor, Gpio. At some place in the code, most likely in some high-level layer I would like to get a "reference" to the board instance and enumerate its peripherals and e.g. forward the information about all peripherals to the other service (e.g. HomeAssistant). At this point I need to know what's the interface that the device implements. The traditional solution would be: Make an enum DeviceType { Led, Sensor, Gpio } and add base interface Device that has method DeviceType getType() that would be overridden by derived classes. This way I can simply get the information I need. The resulting code would be: #include enum class DeviceType { Led, Sensor, Gpio }; class Device { public: Device() = default; virtual ~Device() = default; virtual DeviceType get_type() = 0; }; class Led : public Device { public: DeviceType get_type() { return DeviceType::Led; }; }; class Sensor : public Device { public: DeviceType get_type() { return DeviceType::Sensor; }; }; class LedImpl : public Led {}; class SensorImpl : public Sensor {}; using DeviceId = int; class MyBoard { public: using DeviceInfo = std::tuple; void init() { devices_.push_back({1, std::make_unique()}); devices_.push_back({2, std::make_unique()}); } const auto& devices() { return devices_; } private: std::vector devices_; }; Pros: easy to implement I can call switch on DeviceType Cons: "Device" is an artificial interface whose only purpose is to group all devices in single container and allow to get information about the interface the device implements "Information" duplication. Basically e.g. the interface "Led" holds the same information as DeviceType::Led, both of them clearly defines what's the interface the device implements, so in my opinion this is a perhaps not code duplication, but information duplication. I have to manage DeviceType num, extend it when the new interface comes up Other solutions I have though about: A) using std::variant, but it seems it solves the problem just partially. Indeed I no longer need to manage DeviceType but I need to manage std::variant B) I could move the functionality of finding the type out of Device class and remove it completely. But then I would have to manage some kind of a container that ties device instance with its type, also MyBoard::devices() would return container with some meanigless deivce ids that also seems to be kinda fishy. I believe that there are some better solutions for such an old problem, especially with C++23, perhaps you have implemented something similar and would like to share it. C) RTTI - is not on the table

Mar 17, 2025 - 12:34
 0
Extracting type info

Suppose that I want to implement the following scenario. There is a board and each board has a set of peripherals like leds, temp sensors, gpio and so on. Each of them implements the according C++ interface: Led, Sensor, Gpio. At some place in the code, most likely in some high-level layer I would like to get a "reference" to the board instance and enumerate its peripherals and e.g. forward the information about all peripherals to the other service (e.g. HomeAssistant). At this point I need to know what's the interface that the device implements. The traditional solution would be: Make an enum DeviceType { Led, Sensor, Gpio } and add base interface Device that has method DeviceType getType() that would be overridden by derived classes. This way I can simply get the information I need. The resulting code would be:

#include 

enum class DeviceType { Led, Sensor, Gpio };
class Device {
public:
    Device() = default;
    virtual ~Device() = default;
    virtual DeviceType get_type() = 0;
};

class Led : public Device {
public:
    DeviceType get_type() { return DeviceType::Led; };
};

class Sensor : public Device {
public:
    DeviceType get_type() { return DeviceType::Sensor; };
};

class LedImpl : public Led {};
class SensorImpl : public Sensor {};

using DeviceId = int;

class MyBoard
{
public:
    using DeviceInfo = std::tuple>;

    void init()
    {
        devices_.push_back({1, std::make_unique()});
        devices_.push_back({2, std::make_unique()});
    }

    const auto& devices()
    {
        return devices_;
    }

private:
    std::vector devices_;
};

Pros:

  • easy to implement
  • I can call switch on DeviceType Cons:
  • "Device" is an artificial interface whose only purpose is to group all devices in single container and allow to get information about the interface the device implements
  • "Information" duplication. Basically e.g. the interface "Led" holds the same information as DeviceType::Led, both of them clearly defines what's the interface the device implements, so in my opinion this is a perhaps not code duplication, but information duplication.
  • I have to manage DeviceType num, extend it when the new interface comes up Other solutions I have though about:

A) using std::variant, but it seems it solves the problem just partially. Indeed I no longer need to manage DeviceType but I need to manage std::variant

B) I could move the functionality of finding the type out of Device class and remove it completely. But then I would have to manage some kind of a container that ties device instance with its type, also MyBoard::devices() would return container with some meanigless deivce ids that also seems to be kinda fishy.

I believe that there are some better solutions for such an old problem, especially with C++23, perhaps you have implemented something similar and would like to share it.

C) RTTI - is not on the table