Я пытаюсь управлять устройствами на встроенном устройстве. Устройства управляются диспетчером устройств. Его ответственность — обеспечить срок службы устройств и снабдить устройствами другие части кода.
У устройства есть определенные методы (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())
{
}
}
}
вы спрашиваете, как static_cast а shared_ptr?
По моему это логическая ошибка. Вы запрашиваете IStream объекты и пытаетесь взаимодействовать с ними, используя несвязанный IDevice интерфейс.
Я бы просто использовал ключи/перечисления в самом вложенном базовом классе (которого здесь нет, поэтому он немного меняет дизайн) и просто static_cast в соответствии с этим. Но фактически это RTTI, сделанный вручную.
Все ли ваши устройства знают об этом во время компиляции? Потому что в этом случае подходы статического полиморфизма могут быть лучшим вариантом.
Для умного указателя C++ укажите pointer_cast
Большинство да, но не все





Вот несколько вариантов:
deviceManager.CastDevice<IDevice>(ptr, "IDevice");, который будет очень похож на ваш существующий метод.IUnknown как в COM, от которого наследуется каждый интерфейс.IDevice для всего и содержать такие методы, как GetStream(), которые возвращают nullptr, если интерфейс недоступен.IDevice, содержащий методы, которые есть не на всех устройствах.Все варианты 1–3 добавляют немного дополнительного кода для доступа к устройствам. Для сравнения 4. — это вариант с наименьшим количеством кода и очень простой для понимания, хотя он не является архитектурно чистым и может оказаться проблематичным, если количество методов станет большим.
Я думаю, что вариант 1 является наиболее простым, учитывая существующий код. Я думал о втором варианте, но первый мне понравился больше. Вариант 3 невозможен, поскольку код используется несколькими библиотеками. Я хочу предотвратить зависимость между ними. Вариант 4 кажется непрактичным, учитывая, сколько методов он будет содержать.
Вы описали, что делаете. В чем вопрос?