Transform Event::visit signature to accept multiple handlers

This commit is contained in:
Pixel-Tony 2025-01-23 01:24:32 +01:00 committed by Chris Thrasher
parent 18393ea5cb
commit d64a222c86
3 changed files with 48 additions and 40 deletions

View File

@ -34,6 +34,7 @@
#include <SFML/System/Vector2.hpp> #include <SFML/System/Vector2.hpp>
#include <type_traits>
#include <variant> #include <variant>
@ -339,26 +340,26 @@ public:
[[nodiscard]] const TEventSubtype* getIf() const; [[nodiscard]] const TEventSubtype* getIf() const;
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
/// \brief Apply a visitor to the event /// \brief Apply handlers to the event
/// ///
/// \param visitor The visitor to apply /// \param handlers Handlers to apply
/// ///
/// \return The result of applying the visitor to the event /// \return The result of applying the handlers to the event
/// ///
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
template <typename T> template <typename... Handlers>
decltype(auto) visit(T&& visitor); decltype(auto) visit(Handlers&&... handlers);
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
/// \brief Apply a visitor to the event /// \brief Apply handlers to the event
/// ///
/// \param visitor The visitor to apply /// \param handlers Handlers to apply
/// ///
/// \return The result of applying the visitor to the event /// \return The result of applying the handlers to the event
/// ///
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
template <typename T> template <typename... Handlers>
decltype(auto) visit(T&& visitor) const; decltype(auto) visit(Handlers&&... handlers) const;
private: private:
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
@ -395,11 +396,20 @@ private:
template <typename T, typename... Ts> template <typename T, typename... Ts>
[[nodiscard]] static constexpr bool isInParameterPack(const std::variant<Ts...>*) [[nodiscard]] static constexpr bool isInParameterPack(const std::variant<Ts...>*)
{ {
return (std::is_same_v<T, Ts> || ...); return std::disjunction_v<std::is_same<T, Ts>...>;
} }
template <typename T> template <typename T>
static constexpr bool isEventSubtype = isInParameterPack<T>(decltype (&m_data)(nullptr)); static constexpr bool isEventSubtype = isInParameterPack<T>(static_cast<decltype(&m_data)>(nullptr));
template <typename Handler, typename... Ts>
static constexpr bool isInvokableWithAnyOf(std::variant<Ts...>*)
{
return std::disjunction_v<std::is_invocable<Handler, Ts&>...>;
}
template <typename Handler>
static constexpr bool isValidHandler = isInvokableWithAnyOf<Handler>(static_cast<decltype(&m_data)>(nullptr));
}; };
} // namespace sf } // namespace sf

View File

@ -38,6 +38,20 @@
namespace sf namespace sf
{ {
namespace priv
{
template <typename... Ts>
struct OverloadSet : Ts...
{
using Ts::operator()...;
#if defined(_MSC_VER) && !defined(__clang__)
unsigned char dummy; // Dummy variable to ensure that this struct is not empty thus avoiding a crash due to an MSVC bug
#endif
};
template <typename... Ts>
OverloadSet(Ts...) -> OverloadSet<Ts...>;
} // namespace priv
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
template <typename TEventSubtype> template <typename TEventSubtype>
Event::Event(const TEventSubtype& eventSubtype) Event::Event(const TEventSubtype& eventSubtype)
@ -79,18 +93,20 @@ const TEventSubtype* Event::getIf() const
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
template <typename T> template <typename... Handlers>
decltype(auto) Event::visit(T&& visitor) decltype(auto) Event::visit(Handlers&&... handlers)
{ {
return std::visit(std::forward<T>(visitor), m_data); static_assert((isValidHandler<Handlers> && ...), "All handlers must accept a single event subtype parameter");
return std::visit(priv::OverloadSet{std::forward<Handlers>(handlers)...}, m_data);
} }
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
template <typename T> template <typename... Handlers>
decltype(auto) Event::visit(T&& visitor) const decltype(auto) Event::visit(Handlers&&... handlers) const
{ {
return std::visit(std::forward<T>(visitor), m_data); static_assert((isValidHandler<Handlers> && ...), "All handlers must accept a single event subtype parameter");
return std::visit(priv::OverloadSet{std::forward<Handlers>(handlers)...}, m_data);
} }
} // namespace sf } // namespace sf

View File

@ -35,17 +35,6 @@ namespace sf
{ {
namespace priv namespace priv
{ {
template <typename... Ts>
struct OverloadSet : Ts...
{
using Ts::operator()...;
#if defined(_MSC_VER) && !defined(__clang__)
unsigned char dummy; // Dummy variable to ensure that this struct is not empty thus avoiding a crash due to an MSVC bug
#endif
};
template <typename... Ts>
OverloadSet(Ts...) -> OverloadSet<Ts...>;
struct DelayOverloadResolution struct DelayOverloadResolution
{ {
template <typename T> template <typename T>
@ -55,21 +44,14 @@ struct DelayOverloadResolution
}; };
} // namespace priv } // namespace priv
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
template <typename... Ts> template <typename... Handlers>
void WindowBase::handleEvents(Ts&&... handlers) // NOLINT(cppcoreguidelines-missing-std-forward) void WindowBase::handleEvents(Handlers&&... handlers)
{ {
static_assert(sizeof...(Ts) > 0, "Must provide at least one handler"); static_assert(sizeof...(Handlers) > 0, "Must provide at least one handler");
// Disable misc-const-correctness for this line since clang-tidy
// complains about it even though the code would become uncompilable
// NOLINTNEXTLINE(misc-const-correctness)
priv::OverloadSet overloadSet{std::forward<Ts>(handlers)..., [](const priv::DelayOverloadResolution&) { /* ignore */ }};
while (std::optional event = pollEvent()) while (std::optional event = pollEvent())
event->visit(overloadSet); event->visit(std::forward<Handlers>(handlers)..., [](priv::DelayOverloadResolution) { /* ignore */ });
} }
} // namespace sf } // namespace sf