Search code examples
c++templateseventsvariadic-templatescircular-dependency

C++ template type circularity and non-template pointer to template instance


For reference, here is the minimal example : http://coliru.stacked-crooked.com/a/75354688a0a6af64

In this example, I have a single problem of template type circularity that, in my view, could be solved in two ways :

  • make the Controller a template class and the Event member pointer a non-template pointer to a template class instance (but how ?)
  • use std::any or something like it to keep the Controller class non-template (like is shown in the example, but doesn't compile)

What is the best way to make this work in a cleanly ?

EDIT : link updated


Solution

  • You cannot fully type erase functor which accept template argument.

    But if you know the subset of template argument to handle, std::any/ std::variant might help:

    In your case non-supported event do no-op, so "your" controller:

    template <typename ... Modules>
    class Controller {
    public:
        std::tuple<Modules...> modules;
            
        template<typename evt_t>
        void emit(evt_t event) { 
            std::apply([this](auto&&... args) {((args.dispatch(Event<evt_t>{event, this})), ...);}, modules);
        }
            
        Controller(std::tuple<Modules...> _modules) : modules{_modules}{}
    };
    

    becomes

    class Controller {
        std::function<void(std::any)> func;
    public:
        template<typename evt_t>
        void emit(evt_t event) {
            func(event);
        }
    
        template <typename ... Modules, typename EventTags>
        Controller(std::tuple<Modules...> tmodules, EventTags event_tags)
        {
            func = [=, this](std::any any_ev){
                auto f = [&, this](auto tag){
                    using EventType = typename decltype(tag)::type;
                    if (auto* ev = std::any_cast<EventType>(&any_ev)) {
                        std::apply([=, this](auto&&... modules) {((modules.dispatch(Event<EventType>{*ev, this})), ...);}, tmodules);
                    }
                };
                std::apply([&f](auto... tags){ (f(tags), ...); }, event_tags);
            };
        }
    };
    

    Demo