c++templates

How to dynamically setup a template class method in C++?


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?


Solution

  • 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{});
    }
    

    Demo

    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.