如何在STL容器中存储抽象模板类型的抽象模板类?

时间:2018-05-01 01:01:03

标签: c++ c++11 templates c++14 cqrs

我希望标题不会产生误导,但我找不到合适的语言来定义我的问题。

最近我一直在尝试使用C ++实现“CQRS Command Handler”设计模式。我有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,并且编写调度方法,只需尝试dynamic_cast所有已注册的处理程序,如果某些转换成功,它将调用找到的处理程序,如下所示:

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 store应该使用什么类型的Dispatcher :: Dispatch方法正常工作,如果可能的话?

到目前为止我尝试过:

  • 的std ::矢量&lt; ICommandHandler *&gt; - 没有编译,因为将具体的处理程序转换为ICommandHandler&lt;的ICommand&GT; * GT;是不可能的。

    Error   C2440   'initializing': cannot convert from 'SampleCommandHandler *' to 'ICommandHandler<ICommand,void> *'
    
  • 的std ::矢量&lt; void *&gt; - 没有编译,因为dynamic_cast不能应用于void *

2 个答案:

答案 0 :(得分:2)

这里有一堆毫无意义的(至少对我而言)多态性;我根据处理的内容拆分处理程序容器,而不是使用一个向量。就像从typeindex到handler的map / unordered map一样;或者,有一组固定的类型来处理。

但是要解决问题:

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>的矢量。

答案 1 :(得分:0)

当然可以使用地图或无序地图进行查找。如果必须,可以使用带有自定义比较器的(无序)集。虽然这不能回答你的问题: - )

假设你的处理程序确实是多态的,那么你的map或set必须是指向接口ICommandHandler的指针。它们不应该是原始指针,但可能是一些智能指针。

使用std :: shared_ptr意味着处理事件时保证处理程序对象的生命周期 - 如果它也需要一个shared_ptr,即使事件已从映射中移除(在处理程序内部或由另一个线程或处理程序),但增加了内部复杂性。

std :: unique_ptr的实现更加简单,当您知道处理程序无法中断时,它可以很好地工作。请注意,客户端代码只能看到原始指针 - 它不能窃取unique_ptr!