Implement support for references to handlers and function pointers

This commit is contained in:
kimci86 2025-01-25 23:40:59 +01:00 committed by Chris Thrasher
parent efb207c849
commit 4c0b5b3df9
2 changed files with 45 additions and 1 deletions

View File

@ -28,6 +28,7 @@
#include <SFML/Window/Event.hpp> #include <SFML/Window/Event.hpp>
#include <SFML/Window/WindowBase.hpp> // NOLINT(misc-header-include-cycle) #include <SFML/Window/WindowBase.hpp> // NOLINT(misc-header-include-cycle)
#include <type_traits>
#include <utility> #include <utility>
@ -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 <typename Handler>
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 <typename Handler>
struct Caller<Handler&>
{
// Use SFINAE so that the call operator exists only for arguments the captured handler accepts
template <typename Argument, std::enable_if_t<std::is_invocable_v<Handler&, Argument>, int> = 0>
decltype(auto) operator()(Argument&& argument)
{
return handler(std::forward<Argument>(argument));
}
Handler& handler;
};
// Inheritance is not possible with function pointers either.
// In this case, we capture the function pointer to call it.
template <typename Return, typename Argument>
struct Caller<Return (*)(Argument)>
{
Return operator()(Argument&& argument)
{
return function(std::forward<Argument>(argument));
}
Return (*function)(Argument);
};
} // namespace priv } // namespace priv
@ -67,7 +102,7 @@ void WindowBase::handleEvents(Handlers&&... handlers)
// complains about it even though the code would become incorrect // complains about it even though the code would become incorrect
// NOLINTNEXTLINE(misc-const-correctness) // NOLINTNEXTLINE(misc-const-correctness)
priv::OverloadSet overloadSet{std::forward<Handlers>(handlers)..., priv::OverloadSet overloadSet{priv::Caller<Handlers>{std::forward<Handlers>(handlers)}...,
[](const priv::DelayOverloadResolution&) { /* ignore */ }}; [](const priv::DelayOverloadResolution&) { /* ignore */ }};
while (std::optional event = pollEvent()) while (std::optional event = pollEvent())

View File

@ -12,6 +12,7 @@
#include <chrono> #include <chrono>
#include <memory> #include <memory>
#include <type_traits> #include <type_traits>
#include <utility>
TEST_CASE("[Window] sf::WindowBase", runDisplayTests()) TEST_CASE("[Window] sf::WindowBase", runDisplayTests())
{ {
@ -240,5 +241,13 @@ TEST_CASE("[Window] sf::WindowBase", runDisplayTests())
void operator()(const sf::Event::Closed&) && = delete; void operator()(const sf::Event::Closed&) && = delete;
}; };
windowBase.handleEvents(LvalueOnlyHandler{}); 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&) {});
}; };
} }