I'm working on a C++ project where I want to dispatch events to different modules. I have a base Event class and several derived event types (EventA, EventB, EventC). My modules inherit from a templated Module base class, which uses concepts and SFINAE to dispatch events to the handler in the derived class if it is defined.
It works properly with templates, but not when trying to implement it by using auto keyword only.
Here is a working version with whole context:
struct Event {};
struct EventA : Event {};
struct EventB : Event {};
struct EventC : Event {};
class MyModule;
template <typename T>
concept IsEventType = std::is_base_of_v<Event, T>;
template <typename Dispatcher, typename Dispatchable>
concept CanDispatch = requires(Dispatcher* d, const Dispatchable& e)
{
{ d->dispatch(e) };
};
template <typename Derived>
class Module
{
public:
void onEvent(const IsEventType auto& e)
{
dispatchEvent(e, static_cast<Derived*>(this));
}
private:
// This is the problematic function
template<IsEventType Dispatchable, CanDispatch<Dispatchable> Dispatcher>
void dispatchEvent(const Dispatchable& e, Dispatcher* dispatcher)
{
dispatcher->dispatch(e);
}
void dispatchEvent(...) {}
};
class MyModule : public Module<MyModule>
{
public:
void dispatch(const EventA& event) {
std::cout << "Handling EventA in MyModule" << std::endl;
}
void dispatch(const EventB& event) {
std::cout << "Handling EventB in MyModule" << std::endl;
}
};
int main()
{
MyModule m;
m.onEvent(EventA{});
m.onEvent(EventB{});
m.onEvent(EventC{});
}
I have tried writing this instead of templated version, but it never gets called
// Also tried CanDispatch<std::remove_cvref_t<decltype(e)>>
void dispatchEvent(const IsEventType auto& e, CanDispatch<decltype(e)> auto* dispatcher)
{
dispatcher->dispatch(e);
}
This seems to be another msvc bug which is fixed in latest trunk version.