C ++ function does not accept concrete implementation

I am trying to implement a typeafe event bus. I am stuck in the EventBus::subscribe function because it does not accept my specific event handler. In an earlier version, I had AbstractEventHandler implemented only as an abstract class, without a template. I had no problems with this implementation. Therefore, I assume that the actual problem is related to the abstract pattern.

The code below is a stripped down version of my implementation. The first block consists of the "skeleton" of the event bus and its required classes, while the second block shows the actual implementation of the event, the event handler, and the main one.

enum contains all available events. An abstract event is the basis from which all concrete events originate. An event handler is an abstract template with an event as a class of templates for providing type security. The event bus is responsible for propagating all published events to the appropriate handlers.

 enum EVENT_TYPE { ON_EVENT_1, ON_EVENT_2 }; class AbstractEvent { public: AbstractEvent() {}; virtual ~AbstractEvent() {}; virtual EVENT_TYPE type() = 0; }; template<class T> class AbstractEventHandler { public: AbstractEventHandler() {}; virtual ~AbstractEventHandler() {}; virtual void on_event(T *event) = 0; }; class EventBus { public: EventBus() {}; virtual ~EventBus() {}; void subscribe(EVENT_TYPE type, AbstractEventHandler<AbstractEvent> *eventHandler) { // Add handler to vector for further use } void publish(AbstractEvent *event) { // send event to each handler in respective vector } }; 

Below is my specific event and event handler, and most importantly ()

 class ConcreteEvent : public AbstractEvent { public: ConcreteEvent() {}; virtual ~ConcreteEvent() {}; EVENT_TYPE type() { return ON_EVENT_1; }; }; class ConcreteEventHandler : public AbstractEventHandler<ConcreteEvent> { public: ConcreteEventHandler() {} virtual ~ConcreteEventHandler() {}; void on_event(ConcreteEvent *event) { // Do something }; }; int main() { EventBus *eventBus = new EventBus(); ConcreteEventHandler handler = ConcreteEventHandler(); // This failes! eventBus->subscribe(ON_EVENT_1, &handler); } 

The compiler returns an error with the message that to call

no corresponding function
 EventBus::subscribe(EVENT_TYPE, ConcreteEventHandler*) 

and that the only candidates are

 void EventBus::subscribe(EVENT_TYPE, AbstractEventHandler<AbstractEvent>*) 

How can I implement the EventBus :: subscribe method to accept specific implementations of my abstract class?

Update: Solution

I changed the description of the EventBus::subscribe method to the following, and now it works great:

 template<typename T> void subscribe(EVENT_TYPE type, AbstractEventHandler<T> *eventHandler) { } 

Thanks, Rohan, for your hints! They helped me find this solution.

+6
source share
3 answers

The reason is because ConcreteEventHandler is a subclass of AbstractEventHandler<ConcreteEvent> and not AbstractEventHandler<AbstractEvent> .

This may seem unexpected, but AbstractEventHandler<ConcreteEvent> cannot be a subclass of AbstractEventHandler<AbstractEvent> , although ConcreteEvent is a subclass of AbstractEvent .

The reason is that with templates, templates, as you wish, do not guarantee type safety. Let's look at an example. Let's move on to the standard paradigm of the base class Animal and subclasses Cat and Dog . Say we have a list of animals:

 std::list<Animals>* animals; 

and the list of cats:

 std::list<Cat> cats; 

The following is NOT a valid lith:

 animals = &cats; 

The reason is that if I do this,

 animals->add(new Dog("Ben")); 

I would actually add Dog to Cat s list. cats.last() here really will be returned Dog . So, in this case, you essentially add Dog to your Cat s list. I've seen enough Looney Tunes episodes to know this is not a good idea:

 cats.last().meow(); 

The above is certainly not true, since we all know that a Dog can only bowbow() .

EDIT

To answer your question, here is what I suggest you do; Let ConcreteEventHandler inherit from AbstractEventHandler<AbstractEvent> , and inside the code, wherever you use ConcreteEvent , use dynamic_case to drop AbstractEvent to ConcreteEvent . This will use runtime introspection, which can slightly affect performance (I also saw quite a few people who disagree with using dynamic translation), but you can successfully perform a valid data type upgrade.

+5
source
Rohan has already answered why the code does not compile, however I would suggest a different approach.

You can implement it in such a way that the Eventhandler subscribes directly to the EventGenerator . Thus, there is a direct relationship between the generation and processing of the event.
Then the event must contain a link to its generator to allow it to access the signed handlers, and eventbus calls the event method to handle it.

Thus, eventbus does not know about event handlers, and you do not even need to enumerate the type of event.
However, you need different event generators that should be available for different event handlers, and not for a single eventbus. Each event handler can process only one event, therefore, if more events are required, event handlers should be aggregated (by delegation or inheritance).

+1
source

Your class AbstractEventHandler<T> must inherit AbstractEvent . It was probably your intention, you just forgot to write it.

 template<class T> class AbstractEventHandler :public AbstractEvent { public: AbstractEventHandler() {}; virtual ~AbstractEventHandler() {}; virtual void on_event(T *event) = 0; } 
-2
source

Source: https://habr.com/ru/post/912209/


All Articles