Приведение смарт-указателей без RTTI C++

Я пытаюсь управлять устройствами на встроенном устройстве. Устройства управляются диспетчером устройств. Его ответственность — обеспечить срок службы устройств и снабдить устройствами другие части кода.

У устройства есть определенные методы (init, проверка статуса и т. д.) Для простоты я добавил только IsReady(). Чтобы сделать код более гибким, мне нравится использовать интерфейсы. Например, у меня есть расширитель ввода-вывода с поддержкой UART. Итак, это устройство реализует IDevice, а также интерфейс IStream.

Я могу запросить все устройства, совместимые с IStream. Поскольку я не хочу полагаться на RTTI, я использую для этого ключи. (В примере они жестко запрограммированы). Я бы предпочел, чтобы это не требовалось, но без RTTI я не вижу, чтобы это произошло.

Проблема возникает, когда я пытаюсь получить устройства с определенным интерфейсом. Поскольку сам этот интерфейс не наследуется от IDevice, у меня нет доступа к методам IDevice.

Вот мой вопрос: у меня есть объект, то есть std::shared_ptr<IStream>, теперь я хотел бы получить доступ к методам, которые определены IDevice. Поскольку IStream не реализует IDevice, я не могу получить доступ к этим методам. По той же причине я не могу использовать static _cast, а поскольку у меня нет RTTI, я не могу использовать dynamic_cast.

Одно из решений, которое только что пришло мне в голову, — использовать диспетчер устройств, чтобы получить IDevice из IStream.

Вот урезанная версия того, чего я пытаюсь достичь:


class IDevice {
public:    
    virtual bool IsReady() = 0;
};

class IStream {
public:
    virtual size_t ReadStream(uint8_t* buffer, size_t bufferSize) = 0;
};

class DeviceManager {
public:
    template<typename T>
    std::vector<std::shared_ptr<T>> GetDevicesByCompatibility(const std::string& compatibility);
};


class MAX14830_Uart : public IDevice, public IStream {
public:
    bool IsReady() override;
    size_t ReadStream(uint8_t* buffer, size_t bufferSize) override;
};



void Test()
{
    DeviceManager deviceManager; // Not included in example, but the devices are instantiated and registered to the device manager

    auto devices = deviceManager.GetDevicesByCompatibility<IStream>("IStream");
    for (auto& device: devices)
    {   
        // Here is the problem, I want to check if the device is ready, but my device is of type IStream, not IDevice
        if (device->IsReady())
        {
            
        }
    }
}

Вы описали, что делаете. В чем вопрос?

Vladlen 23.05.2024 10:39

вы спрашиваете, как static_cast а shared_ptr?

Alan Birtles 23.05.2024 10:39

По моему это логическая ошибка. Вы запрашиваете IStream объекты и пытаетесь взаимодействовать с ними, используя несвязанный IDevice интерфейс.

Fareanor 23.05.2024 10:44

Я бы просто использовал ключи/перечисления в самом вложенном базовом классе (которого здесь нет, поэтому он немного меняет дизайн) и просто static_cast в соответствии с этим. Но фактически это RTTI, сделанный вручную.

alagner 23.05.2024 10:47

Все ли ваши устройства знают об этом во время компиляции? Потому что в этом случае подходы статического полиморфизма могут быть лучшим вариантом.

Pepijn Kramer 23.05.2024 12:07

Для умного указателя C++ укажите pointer_cast

Jakkapong Rattananen 23.05.2024 12:19

Большинство да, но не все

Bas Visscher 23.05.2024 12:23
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
1
8
77
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Вот несколько вариантов:

  1. Добавьте deviceManager.CastDevice<IDevice>(ptr, "IDevice");, который будет очень похож на ваш существующий метод.
  2. Сделайте IUnknown как в COM, от которого наследуется каждый интерфейс.
  3. Если вам не нужен внешний код для добавления новых интерфейсов, вы можете использовать IDevice для всего и содержать такие методы, как GetStream(), которые возвращают nullptr, если интерфейс недоступен.
  4. Будьте проще и сделайте большой «уродливый» IDevice, содержащий методы, которые есть не на всех устройствах.

Все варианты 1–3 добавляют немного дополнительного кода для доступа к устройствам. Для сравнения 4. — это вариант с наименьшим количеством кода и очень простой для понимания, хотя он не является архитектурно чистым и может оказаться проблематичным, если количество методов станет большим.

Я думаю, что вариант 1 является наиболее простым, учитывая существующий код. Я думал о втором варианте, но первый мне понравился больше. Вариант 3 невозможен, поскольку код используется несколькими библиотеками. Я хочу предотвратить зависимость между ними. Вариант 4 кажется непрактичным, учитывая, сколько методов он будет содержать.

Bas Visscher 23.05.2024 10:55

Другие вопросы по теме