Skip to content

Commit

Permalink
GUI2: finished refactoring out usage of boost::mpl
Browse files Browse the repository at this point in the history
dispatcher::has_event called the private find() function, which after a whole bunch of
jumping around essentially checked that the signal queue for the given event was not
empty. This was a run-time op, yet the code was set up using SFINAE in order to for
has_handler::oper() to call the event_signal function corresponding to the event's queue
(ie, the mouse queue for a mouse-type event).

Since this code was written before the era of constexpr, event type validation was done
using boost::mpl, which, in this case, resulted in a monstrous amalgamation of build-time
template specialization for a run-time check. I'm not certain, but I believe it might
have resulted in an specialization of find() (or at least, implementation::find()) for
every single event type (each member of the ui_event enum).

This converts the code to a purely run-time check and throws out all the template stuff.
It also removes the relevant event_signal overload dealing with events in a set. The
version dealing with function types is preserved as it's used in the fire_event()
implementation and is a fairly standard usecase of SFINAE.

The has_handler class has also been converted to a static function since it's no longer
needed for template specializations in find().

And finally, is_raw_event had to be renamed to is_raw_event_event to allow simple name
completion in IMPLEMENT_RUNTIME_EVENT_SIGNAL_CHECK. I could have also renamed the raw event
queue to signal_raw_queue but I figured keeping the name as signal_raw_event_queue made
its purpose clearer.
  • Loading branch information
Vultraz committed Oct 13, 2020
1 parent 0069942 commit 0571fef
Show file tree
Hide file tree
Showing 4 changed files with 62 additions and 317 deletions.
50 changes: 6 additions & 44 deletions src/gui/core/event/dispatcher.cpp
Expand Up @@ -57,50 +57,12 @@ void dispatcher::connect()
bool dispatcher::has_event(const ui_event event, const event_queue_type event_type)
{
#if 0
// Debug code to test whether the event is in the right queue.
std::cerr << "Event '" << event
<< "' event "
<< find<set_event>(event, dispatcher_implementation
::has_handler(event_type, *this))
<< " mouse "
<< find<set_event_mouse>(event, dispatcher_implementation
::has_handler(event_type, *this))
<< " keyboard "
<< find<set_event_touch_motion>(event, dispatcher_implementation
::has_handler(event_type, *this))
<< " touch_motion "
<< find<set_event_touch_gesture>(event, dispatcher_implementation
::has_handler(event_type, *this))
<< " touch_gesture "
<< find<set_event_keyboard>(event, dispatcher_implementation
::has_handler(event_type, *this))
<< " notification "
<< find<set_event_notification>(event, dispatcher_implementation
::has_handler(event_type, *this))
<< " message "
<< find<set_event_message>(event, dispatcher_implementation
::has_handler(event_type, *this))
<< ".\n";
const bool res = dispatcher_implementation::has_handler(*this, event_type, event);
std::cerr << "Event '" << event << " '" << (res ? "found" : "not found") << "in queue\n";
return res;
#else
return dispatcher_implementation::has_handler(*this, event_type, event);
#endif

return find<set_event>(
event, dispatcher_implementation::has_handler(event_type, *this))
|| find<set_event_mouse>(
event, dispatcher_implementation::has_handler(event_type, *this))
|| find<set_event_keyboard>(
event, dispatcher_implementation::has_handler(event_type, *this))
|| find<set_event_text_input>(
event, dispatcher_implementation::has_handler(event_type, *this))
|| find<set_event_touch_motion>(
event, dispatcher_implementation::has_handler(event_type, *this))
|| find<set_event_touch_gesture>(
event, dispatcher_implementation::has_handler(event_type, *this))
|| find<set_event_notification>(
event, dispatcher_implementation::has_handler(event_type, *this))
|| find<set_event_message>(
event, dispatcher_implementation::has_handler(event_type, *this))
|| find<set_event_raw_event>(
event, dispatcher_implementation::has_handler(event_type, *this));
}

bool dispatcher::fire(const ui_event event, widget& target)
Expand Down Expand Up @@ -154,7 +116,7 @@ bool dispatcher::fire(const ui_event event, widget& target, const point& center,

bool dispatcher::fire(const ui_event event, widget& target, const SDL_Event& sdlevent)
{
assert(is_raw_event(event));
assert(is_raw_event_event(event));
return fire_event<signal_raw_event_function>(event, this, &target, sdlevent);
}

Expand Down
32 changes: 29 additions & 3 deletions src/gui/core/event/dispatcher.hpp
Expand Up @@ -157,7 +157,7 @@ constexpr bool is_message_event(const ui_event event)
*
* This version is for callbacks of raw events.
*/
constexpr bool is_raw_event(const ui_event event)
constexpr bool is_raw_event_event(const ui_event event)
{
return event == SDL_RAW_EVENT;
}
Expand Down Expand Up @@ -755,7 +755,7 @@ class dispatcher
* @param position The position to place the callback.
*/
template<ui_event E>
std::enable_if_t<is_raw_event(E)>
std::enable_if_t<is_raw_event_event(E)>
connect_signal(const signal_raw_event_function& signal, const queue_position position = back_child)
{
signal_raw_event_queue_.connect_signal(E, position, signal);
Expand All @@ -772,7 +772,7 @@ class dispatcher
* was added in front or back.)
*/
template<ui_event E>
std::enable_if_t<is_raw_event(E)>
std::enable_if_t<is_raw_event_event(E)>
disconnect_signal(const signal_raw_event_function& signal, const queue_position position = back_child)
{
signal_raw_event_queue_.disconnect_signal(E, position, signal);
Expand Down Expand Up @@ -877,6 +877,32 @@ class dispatcher
std::list<T> pre_child;
std::list<T> child;
std::list<T> post_child;

/**
* Checks whether the queue of a given type is empty.
*
* @param queue_type The queue to check. This may be one or more types
* OR'd together (event_queue_type is bit-unique).
*
* @returns True if ALL the matching queues are empty, or false
* if any of the matching queues is NOT empty.
*/
bool empty(const dispatcher::event_queue_type queue_type) const
{
if((queue_type & dispatcher::pre) && !pre_child.empty()) {
return false;
}

if((queue_type & dispatcher::child) && !child.empty()) {
return false;
}

if((queue_type & dispatcher::post) && !post_child.empty()) {
return false;
}

return true;
}
};

/** Helper struct to generate the various event queues. */
Expand Down
168 changes: 27 additions & 141 deletions src/gui/core/event/dispatcher_private.hpp
Expand Up @@ -20,7 +20,6 @@

#include <SDL2/SDL_events.h>

#include <boost/mpl/for_each.hpp>
#include <boost/range/adaptor/reversed.hpp>

namespace gui2
Expand Down Expand Up @@ -65,27 +64,6 @@ struct dispatcher_implementation
{ \
return dispatcher.QUEUE.queue[event]; \
} \
\
/** \
* Returns the signal structure for a key in SET. \
* \
* There are several functions that only overload the return value, in \
* order to do so they use SFINAE. \
* \
* @tparam K A key in set_event. \
* @param dispatcher The dispatcher whose signal queue is used. \
* @param event The event to get the signal for. \
* \
* @returns The signal of the type \
* dispatcher::signal_type<FUNCTION> \
*/ \
template<typename K> \
static std::enable_if_t<boost::mpl::has_key<SET, K>::value, dispatcher::signal_type<FUNCTION>>& \
event_signal(dispatcher& dispatcher, const ui_event event) \
{ \
return dispatcher.QUEUE.queue[event]; \
}


IMPLEMENT_EVENT_SIGNAL(set_event, signal_function, signal_queue_)

Expand Down Expand Up @@ -115,137 +93,45 @@ struct dispatcher_implementation
#undef IMPLEMENT_EVENT_SIGNAL_WRAPPER
#undef IMPLEMENT_EVENT_SIGNAL

#define IMPLEMENT_RUNTIME_EVENT_SIGNAL_CHECK(TYPE) \
else if(is_##TYPE##_event(event)) { \
return queue_check(dispatcher.signal_##TYPE##_queue_); \
}

/**
* A helper class to find out whether dispatcher has an handler for a
* certain event.
* A helper to test whether dispatcher has an handler for a certain event.
*
* @param dispatcher The dispatcher whose signal queue is used.
* @param queue_type The type of event to look for.
* @param event The event to get the signal for.
*
* @returns Whether or not the handler is found.
*/
class has_handler
static bool has_handler(dispatcher& dispatcher, const dispatcher::event_queue_type queue_type, ui_event event)
{
public:
/**
* Constructor.
*
* @param event_type The type of event to look for.
* @param dispatcher The dispatcher whose signal queue is used.
*/
has_handler(const dispatcher::event_queue_type event_type, dispatcher& dispatcher)
: event_type_(event_type), dispatcher_(dispatcher)
{
}

/**
* Tests whether a handler for an event is available.
*
* It tests for both the event and the event_type send in the
* constructor.
*
* @tparam T A key from an event set used to instantiate
* the proper @p event_signal function.
* @param event The event to get the signal for.
*
* @returns Whether or not the handler is found.
*/
// not called operator() to work around a problem in MSVC
// (known to affect all versions up to 2015)
template<typename T>
bool oper(ui_event event)
{
if((event_type_ & dispatcher::pre)
&& !event_signal<T>(dispatcher_, event).pre_child.empty()) {
return true;
}
const auto queue_check = [&](auto& queue_set) {
return !queue_set.queue[event].empty(queue_type);
};

if((event_type_ & dispatcher::child)
&& !event_signal<T>(dispatcher_, event).child.empty()) {
return true;
}

if((event_type_ & dispatcher::post)
&& !event_signal<T>(dispatcher_, event).post_child.empty()) {
return true;
}

return false;
if(is_general_event(event)) {
return queue_check(dispatcher.signal_queue_);
}

private:
dispatcher::event_queue_type event_type_;
dispatcher& dispatcher_;
};
};
IMPLEMENT_RUNTIME_EVENT_SIGNAL_CHECK(mouse)
IMPLEMENT_RUNTIME_EVENT_SIGNAL_CHECK(keyboard)
IMPLEMENT_RUNTIME_EVENT_SIGNAL_CHECK(touch_motion)
IMPLEMENT_RUNTIME_EVENT_SIGNAL_CHECK(touch_gesture)
IMPLEMENT_RUNTIME_EVENT_SIGNAL_CHECK(notification)
IMPLEMENT_RUNTIME_EVENT_SIGNAL_CHECK(message)
IMPLEMENT_RUNTIME_EVENT_SIGNAL_CHECK(raw_event)
IMPLEMENT_RUNTIME_EVENT_SIGNAL_CHECK(text_input)

/** Contains the implementation details of the find function. */
namespace implementation
{

/** Specialized class when itor == end */
template<bool done = true>
struct find
{
template<typename itor, typename end, typename E, typename F>
static bool execute(itor*, end*, E, F)
{
return false;
}
};

/** Specialized class when itor != end */
template<>
struct find<false>
{
template<typename itor, typename end, typename E, typename F>
static bool execute(itor*, end*, E event, F functor)
{
typedef typename boost::mpl::deref<itor>::type item;
typedef typename boost::mpl::apply1<boost::mpl::identity<>, item>::type arg;

boost::value_initialized<arg> x;

if(boost::get(x) == event) {
return functor.template oper<item>(event);
} else {
typedef typename boost::mpl::next<itor>::type itor_t;
return find<std::is_same<itor_t, end>::value>::execute(
static_cast<itor_t*>(nullptr),
static_cast<end*>(nullptr),
event,
functor);
}
}
#undef IMPLEMENT_RUNTIME_EVENT_SIGNAL_CHECK
};

} // namespace implementation

/**
* Tests whether an event handler is available.
*
* The code is based on boost::mpl_for_each, which doesn't allow to call a
* template function with the dereferred iterator as template parameter.
*
* The function first tries to match whether the value in the sequence matches
* event, once that matched it will execute the functor with the key found as
* template parameter and the event as parameter.
*
* @tparam sequence The sequence to test upon.
* @tparam E The value type of the item in the sequence
* @tparam F Type of the functor.
*
* @param event The event to look for.
* @param functor The predicate which should is executed if the
* event is matched.
*
* @returns Whether or not the function found a result.
*/
template<typename sequence, typename E, typename F>
inline bool find(E event, F functor)
{
typedef typename boost::mpl::begin<sequence>::type begin;
typedef typename boost::mpl::end<sequence>::type end;

return implementation::find<std::is_same<begin, end>::value>::execute(
static_cast<begin*>(nullptr), static_cast<end*>(nullptr), event, functor);
}

namespace implementation
{

Expand Down

0 comments on commit 0571fef

Please sign in to comment.