Search code examples
c++c++98

Wrong child class function being called due to signature difference


I have an event emitter that emits events and event handlers that handle events. I can extend the Event object to make different events like AlarmEvent, and the EventHandler object to make AlarmEventHandler. EventHandler has a function HandleEvent(Event &event). This causes a problem for child classes that may have HandleEvent(AlarmEvent &event) methods. Obviously these are two different functions so there is no overriding taking place here. I need instead for HandleEvent to be overridden by the child class.

I fully understand the problem is that each EventHandler has a different HandleEvent signature so the EventEmitter will always handle events with the base EventHandler::HandleEvent. I thought by using Event &event as a paremeter to EventEmitter::Emit it would know what kind of Event it was dealing with and choose the correct method.

How can I have my EventEmitter call AlarmEventHandler::HandleEvent instead of the base method EventHandler::HandleEvent ?

// Example program
#include <iostream>
#include <string>
#include <vector>

// event types
class Event {};
class AlarmEvent : public Event {};

// event handler
class EventHandler {
    public:
        virtual void HandleEvent(Event event);
};
void EventHandler::HandleEvent(Event event){
    std::cout << "Handle event " << std::endl;   
}

// alarm event handler
class AlarmEventHandler : public EventHandler {
    public:
        void HandleEvent(AlarmEvent event);  
};
void AlarmEventHandler::HandleEvent(AlarmEvent event){
    std::cout << "Handle alarm event " << std::endl;   
}

// event emitter
class Emitter {
    public:  
        std::vector<EventHandler*> handlers;
        void Emit(Event &event);
};
void Emitter::Emit(Event &event){
    for(size_t i = 0; i < this->handlers.size(); i++){
        this->handlers[i]->HandleEvent(event);   
    }
}

int main()
{
    AlarmEventHandler handler;
    AlarmEvent event;
    Emitter emitter;
    emitter.handlers.push_back(&handler);
    // problem:
    // Handle event printed instead of Handle alarm event
    emitter.Emit(event);
}

Solution

  • Use a dynamic_cast!

    So your AlarmEventHandler could look like:

    // alarm event handler
    class AlarmEventHandler : public EventHandler {
        public:
            void HandleEvent(const Event &event); // Our polymorphic override
            void HandleEvent(AlarmEvent event);  // Our custom Alarm logic
    };
    
    void AlarmEventHandler::HandleEvent(const Event &event){
        try {
            HandleEvent(dynamic_cast<const AlarmEvent&>(event));
        } catch(const std::exception& e) {
            std::cerr << "I can't handle things that aren't AlarmEvents!" << std::endl;
        }
    }
    
    void AlarmEventHandler::HandleEvent(const AlarmEvent &event){
        std::cout << "Handle alarm event " << std::endl;   
    }
    

    To do this though, you'll need Event to be a polymorphic class. So you make the destructor virtual:

    class Event { 
        public: virtual ~Event() {} //Need this so Event is a polymorphic class
    };
    

    See it run here: https://ideone.com/KMkLfq