diff --git a/include/SFML/Window/WindowBase.inl b/include/SFML/Window/WindowBase.inl index 14c82796e..01a906e52 100644 --- a/include/SFML/Window/WindowBase.inl +++ b/include/SFML/Window/WindowBase.inl @@ -28,6 +28,7 @@ #include #include // NOLINT(misc-header-include-cycle) +#include #include @@ -53,6 +54,40 @@ struct DelayOverloadResolution { } }; + +// Wrapper providing a callable type suitable as OverloadSet base type for different kinds of handlers. +// By default, we derive from the handler type and inherit its call operators. +template +struct Caller : Handler +{ + using Handler::operator(); +}; + +// Inheritance is not possible with reference types. +// In this case, we capture the reference and forward arguments to it. +template +struct Caller +{ + // Use SFINAE so that the call operator exists only for arguments the captured handler accepts + template , int> = 0> + decltype(auto) operator()(Argument&& argument) + { + return handler(std::forward(argument)); + } + Handler& handler; +}; + +// Inheritance is not possible with function pointers either. +// In this case, we capture the function pointer to call it. +template +struct Caller +{ + Return operator()(Argument&& argument) + { + return function(std::forward(argument)); + } + Return (*function)(Argument); +}; } // namespace priv @@ -67,7 +102,7 @@ void WindowBase::handleEvents(Handlers&&... handlers) // complains about it even though the code would become incorrect // NOLINTNEXTLINE(misc-const-correctness) - priv::OverloadSet overloadSet{std::forward(handlers)..., + priv::OverloadSet overloadSet{priv::Caller{std::forward(handlers)}..., [](const priv::DelayOverloadResolution&) { /* ignore */ }}; while (std::optional event = pollEvent()) diff --git a/test/Window/WindowBase.test.cpp b/test/Window/WindowBase.test.cpp index 58d404e80..2cb6f6afc 100644 --- a/test/Window/WindowBase.test.cpp +++ b/test/Window/WindowBase.test.cpp @@ -12,6 +12,7 @@ #include #include #include +#include TEST_CASE("[Window] sf::WindowBase", runDisplayTests()) { @@ -240,5 +241,13 @@ TEST_CASE("[Window] sf::WindowBase", runDisplayTests()) void operator()(const sf::Event::Closed&) && = delete; }; windowBase.handleEvents(LvalueOnlyHandler{}); + + // Should compile if user provides a reference to a handler + auto handler = [](const sf::Event::Closed&) {}; + windowBase.handleEvents(handler); + windowBase.handleEvents(std::as_const(handler)); + + // Should compile if user provides a function pointer + windowBase.handleEvents(+[](const sf::Event::Closed&) {}); }; }