Variadic class function called with shared_ptr

Viewed 50

I'm updating some of my old (<c++11) c++ projects to use some of the new features introduced and I'm stuck at making my EventHandler class variadic.

The old version looked like this:

template < class T >
class EventHandler : public virtual IEventHandler
{
  public:

    virtual void onEvent( const T& event ) = 0;

    std::type_index getEventType()
    {
        return std::type_index( typeid( T ) );
    }

    void dispatch( const EventSP &spEv )
    {
        const T* pEv( dynamic_cast< const T* >( spEv.get() ) );

        if( pEv != 0 )
        {
            onEvent( *pEv );
        }
    }
};

And used like this:

class EventConsumer : public event::EventHandler< event::EvA >
{

public:

    EventConsumer( event::IEventService* pEventService )
    : m_pEventService( pEventService )
    {
        pEventService->addEventHandler( this );
    }

    ~EventConsumer()
    {
        m_pEventService->removeEventHandler( this );
    }

    void onEvent( const event::EvA& event )
    {
        event.printMe();
    }

private:

    event::IEventService* m_pEventService;
};

And events declared like this:

namespace event {

class EvA : public Event
{
public:
    EvA()
    {
    }

    void printMe() const
    {
        ::std::cout << "Event A" << ::std::endl;
    }
};

class EvB : public Event
{
public:
    EvB()
    {
    }

    void printMe() const
    {
        ::std::cout << "Event B" << ::std::endl;
    }
};

}

And now my plan is to make EventHandler variadic to support more than one event but I can't seem to figure out how to write the dispatch function.

I would like to use it like this:

class EventConsumer : public event::EventHandler< event::EvA, event::EvB >
{

public:

    EventConsumer( event::IEventService* pEventService )
    : m_pEventService( pEventService )
    {
        pEventService->addEventHandler( this );
    }

    ~EventConsumer()
    {
        m_pEventService->removeEventHandler( this );
    }

    void onEvent( const event::EvA& event )
    {
        event.printMe();
    }

    void onEvent( const event::EvB& event )
    {
        event.printMe();
    }

private:

    event::IEventService* m_pEventService;
};

This is what I got so far:

template <typename T>
class t_impl
{
public:
    virtual void onEvent( const T& event ) = 0;
};

template < class... Ts >
class EventHandler : public t_impl<Ts>..., public IEventHandler
{
  public:

    EventHandler()
    {
        m_types = { typeid( Ts ) ... };
    }

    using t_impl<Ts>::onEvent...;

    std::list<std::type_index> getEventTypes()
    {
        return m_types;
    }
    
private:
    std::list<std::type_index> m_types;

};

I'm guaranteed that the call to dispatch( const EventSP &spEv ) is called with a shared_ptr with a pointer to one of the types that EventHandler is templated with.

It's dispatch responsibility to call the onEvent function.

Any ideas on how to write the dispatch function?

Thanks, Linus

1 Answers

You can use a fold expression:

void dispatch( const std::shared_ptr<Event> &spEv ) {
    (((std::dynamic_pointer_cast< const Ts >( spEv ) != nullptr) && (onEvent( *std::dynamic_pointer_cast< Ts >( spEv ) ), true))
    || ...);
}

Demo

You can do the same with variadic templates and recursion (taking one T at a time from Ts), but it requires much more code. This fold expression might not be the cleanest, but it's concise. It basically creates a series of ifs with the nullptr check for each T in Ts, and for the first one that passes, it executes the rest (what is after the &&).

Related