There is a class that generates an event with variable-type data from time to time.
class A
{
void reportEvent(auto event);
};
I want to generalize this class and make it possible to assign various event handlers.
class A
{
EventHandler _eventHandler();
void reportEvent(auto event)
{
_eventHandler.processEvent(event);
}
};
But it's not possible to make a virtual method with an auto
parameter.
What can I do?
I understand that I can make class A
a template class with EventHandler
as a template parameter. But I don't think it's a good solution as it's a big class, and reporting events is just a bit of its functionality. It looks like an abuse of template instrumentality here for me. Are there other workarounds?
You could rely on std::variant
, one named EventHandler
that manages several handler types and one named Event
that holds the different types of events.
struct Event1 {};
struct Event2 {};
struct Event3 {};
using Event = std::variant<Event1,Event2,Event3>;
struct EventHandlerA {};
struct EventHandlerB {};
using EventHandler = std::variant<EventHandlerA,EventHandlerB>;
Now for instance, the full definition of EventHandlerA
could be
template<class... Ts>
struct overloaded : Ts... { using Ts::operator()...; };
struct EventHandlerA {
auto operator() (Event event) const {
std::visit(overloaded{
[](Event1 evt) { std::cout << "EventHandlerA / Event1 \n"; },
[](Event2 evt) { std::cout << "EventHandlerA / Event2 \n"; },
[](Event3 evt) { std::cout << "EventHandlerA / Event3 \n"; }
}, event);
}
};
The A
class is then
class A
{
private:
EventHandler _eventHandler;
public:
void setEventHandler (auto handler) { _eventHandler = handler; }
void reportEvent(Event event) {
std::visit([&](auto&& handler) {
handler (event);
}, _eventHandler);
}
};
with a setEventHandler
setter that makes it possible to dynamically modify the kind of handler.
int main()
{
A a;
// By default, the event handler is a EventHandler1 object
// (ie. first type of the EventHandler variant)
a.reportEvent (Event1{});
a.reportEvent (Event2{});
a.reportEvent (Event3{});
// We change the EventVariant type
a.setEventHandler (EventHandlerB{});
a.reportEvent (Event1{});
a.reportEvent (Event2{});
a.reportEvent (Event3{});
}
Possible output
EventHandlerA / Event1
EventHandlerA / Event2
EventHandlerA / Event3
EventHandlerB / Event1
EventHandlerB / Event2
EventHandlerB / Event3
The drawback of such an approach is that adding a new event handler type implies a modification (i.e. a re-compilation) of the A
class since one has to add this new handler to the EventHandler
variant types list.
On the other hand, the advantage is that there is no need to define an abstract class to inherit from for the different event handlers.