Как сохранить абстрактный шаблонный класс с абстрактным типом шаблона в контейнере STL?

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

Недавно я пытался реализовать шаблон проектирования «CQRS Command Handler» с использованием C ++. У меня есть 2 иерархии, которые я должен жениться вместе:

  • ICommand

    struct ICommand
    {
    virtual ~ICommand() = default;
    };
    
    struct SampleCommand : ICommand
    {
    int sampleParameter;
    
    SampleCommand() : sampleParameter(0)
    {
    }
    
    explicit SampleCommand(const int sampleParameter)
    {
    this->sampleParameter = sampleParameter;
    }
    };
    
  • ICommandHandler

    template<typename T, typename = std::enable_if_t<std::is_base_of<ICommand, std::decay_t<T>>::value>>
    struct ICommandHandler
    {
    virtual void Handle(std::shared_ptr<T> command) = 0;
    virtual ~ICommandHandler() = default;
    };
    
    class SampleCommandHandler : public ICommandHandler<SampleCommand>
    {
    public:
    void Handle(std::shared_ptr<SampleCommand> command) override
    {
    std::cout << "sampleParameter " << command->sampleParameter << std::endl;
    }
    };
    

Последний фрагмент, который мне нужно реализовать, — это диспетчер, который принимает команду, находит обработчик и делегирует команду найденному обработчику. Первая идея, которая пришла мне в голову, состояла в том, чтобы раскрыть некоторый API регистрации обработчиков в диспетчере и написать метод диспетчеризации, который просто попытался бы выполнить динамическую трансляцию всех зарегистрированных обработчиков, и если какое-то приведение было успешным, он вызвал бы найденный обработчик, как показано ниже:

class Dispatcher
{
public:

template<typename T>
void Dispatch(std::shared_ptr<T> command)
{
auto handler = std::find_if(std::begin(_handlers), std::end(_handlers), [](auto handler)
{
return dynamic_cast<ICommandHandler<T>*>(handler);
});

if(handler != std::end(_handlers))
{
(*handler)->Handle(command);
}
}

private:
std::vector<?> _handlers;
};

Вопрос в том, какого типа должно быть хранилище «_handlers» std :: vector, чтобы метод Dispatcher :: Dispatch работал нормально и возможно ли это?

Что я уже пробовал:

  • станд :: вектор< ICommandHandler *> — Не скомпилирован, потому что приведение конкретного обработчика к ICommandHandler< ICommand> *> не возможно.

    Error   C2440   'initializing': cannot convert from 'SampleCommandHandler *' to 'ICommandHandler<ICommand,void> *'
    
  • станд :: вектор< void *> — не скомпилирован, так как dynamic_cast нельзя применить к void *

0

Решение

У вас тут куча бессмысленного (по крайней мере для меня) полиморфизма здесь; Я бы разделил контейнер обработчиков на основе того, что они обрабатывают, вместо того, чтобы использовать один вектор. Как карта / неупорядоченная карта от typeindex до обработчика; или иметь фиксированный набор типов для обработки.

Но для решения проблемы попросили:

struct ICommandHandlerBase{
virtual ~ICommandHandlerBase(){};
};
template<typename T, typename = std::enable_if_t<std::is_base_of<ICommand, std::decay_t<T>>::value>>
struct ICommandHandler:ICommandHandlerBase
{

Теперь сохраните вектор ICommandHandlerBase* или же unique_ptr<ICommandHandlerBase>,

2

Другие решения

Конечно, используйте карту или неупорядоченную карту для поиска. Если необходимо, вы можете использовать (неупорядоченный) набор с пользовательским компаратором. Хотя это не отвечает на ваш вопрос 🙂

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

Использование std :: shared_ptr означает, что время обработки вашего объекта обработчика гарантируется при обработке события — если оно также принимает shared_ptr, даже если событие удаляется из отображения (либо внутри обработчика, либо в другом потоке или обработчике), но поставляется с добавлена ​​внутренняя сложность.

Реализация std :: unique_ptr намного проще и может хорошо работать, когда вы знаете, что обработчик не может быть прерван. Обратите внимание, что клиентский код видит только необработанный указатель — он не должен красть unique_ptr!

0

По вопросам рекламы ammmcru@yandex.ru
Adblock
detector