Skip to content

Commit

Permalink
break: remove co_attach & enforce event handler call semantics (retur…
Browse files Browse the repository at this point in the history
…n void/dpp::job, take const event)
  • Loading branch information
Mishura4 committed Aug 25, 2023
1 parent f51f305 commit cc3ddfb
Show file tree
Hide file tree
Showing 4 changed files with 117 additions and 61 deletions.
169 changes: 110 additions & 59 deletions include/dpp/event_router.h
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,7 @@ template<class T> class event_router_t {
* @brief Thread safety mutex
*/
mutable std::shared_mutex lock;

/**
* @brief Container of event listeners keyed by handle,
* as handles are handed out sequentially they will always
Expand All @@ -101,24 +102,6 @@ template<class T> class event_router_t {
*/
std::map<event_handle, std::function<void(const T&)>> dispatch_container;


#ifdef DPP_CORO
/**
* @brief Container for event listeners (coroutines only)
*
* Note: keep a listener's parameter as a value type, the event passed can die while a coroutine is suspended
*/
std::map<event_handle, std::function<dpp::job(const T&)>> coroutine_container;
#else
#ifndef _DOXYGEN_
/**
* @brief Dummy container to keep the struct size same
*/
std::map<event_handle, std::function<void(T)>> dummy_container;
#endif /* _DOXYGEN_ */
#endif /* DPP_CORO */


/**
* @brief A function to be called whenever the method is called, to check
* some condition that is required for this event to trigger correctly.
Expand Down Expand Up @@ -163,13 +146,6 @@ template<class T> class event_router_t {
listener(event);
}
};
#ifdef DPP_CORO
for (const auto& [_, listener] : coroutine_container) {
if (!event.is_cancelled()) {
listener(event);
}
}
#endif /* DPP_CORO */
};

/**
Expand All @@ -181,11 +157,7 @@ template<class T> class event_router_t {
*/
bool empty() const {
std::shared_lock l(lock);
#ifdef DPP_CORO
return dispatch_container.empty() && coroutine_container.empty();
#else
return dispatch_container.empty();
#endif /* DPP_CORO */
}

/**
Expand All @@ -199,69 +171,148 @@ template<class T> class event_router_t {
return !empty();
}

#ifdef _DOXYGEN_
/**
* @brief Attach a lambda to the event, adding a listener.
* The lambda should follow the signature specified when declaring
* the event object and should take exactly one parameter derived
* from event_dispatch_t.
*
* @param func Function lambda to attach to event
* @brief Attach a callable to the event, adding a listener.
* The callable should either be of the form `void(const T &)` or
* `dpp::job(T)` (the latter requires DPP_CORO to be defined),
* where T is the event type for this event router.
*
* This has the exact same behavior as using attach.
*
* @see attach
* @param fun Callable to attach to event
* @return event_handle An event handle unique to this event, used to
* detach the listener from the event later if necessary.
*/
template <typename F>
event_handle operator()(F&& fun);

/**
* @brief Attach a callable to the event, adding a listener.
* The callable should either be of the form `void(const T &)` or
* `dpp::job(T)` (the latter requires DPP_CORO to be defined),
* where T is the event type for this event router.
*
* @param fun Callable to attach to event
* @return event_handle An event handle unique to this event, used to
* detach the listener from the event later if necessary.
*/
template <typename F>
event_handle attach(F&& fun);
#else /* not _DOXYGEN_ */
# ifdef DPP_CORO
/**
* @brief Attach a callable to the event, adding a listener.
* The callable should either be of the form `void(const T &)` or
* `dpp::job(T)` (the latter requires DPP_CORO to be defined),
* where T is the event type for this event router.
*
* @param fun Callable to attach to event
* @return event_handle An event handle unique to this event, used to
* detach the listener from the event later if necessary.
*/
template <typename F>
requires utility::callable_returns<F, dpp::job, const T&>
event_handle operator()(F&& fun) {
return this->attach(std::forward<F>(fun));
}

/**
* @brief Attach a callable to the event, adding a listener.
* The callable should either be of the form `void(const T &)` or
* `dpp::job(T)` (the latter requires DPP_CORO to be defined),
* where T is the event type for this event router.
*
* @param fun Callable to attach to event
* @return event_handle An event handle unique to this event, used to
* detach the listener from the event later if necessary.
*/
template <typename F>
requires std::same_as<std::invoke_result_t<F, const T&>, void>
event_handle operator()(F&& fun) {
return this->attach(std::forward<F>(fun));
}
# else
/**
* @brief Attach a callable to the event, adding a listener.
* The callable should either be of the form `void(const T &)` or
* `dpp::job(T)` (the latter requires DPP_CORO to be defined),
* where T is the event type for this event router.
*
* @param fun Callable to attach to event
* @return event_handle An event handle unique to this event, used to
* detach the listener from the event later if necessary.
*/
event_handle operator()(std::function<void(const T&)> func) {
return this->attach(func);
template <typename F>
std::enable_if_t<std::is_same_v<std::invoke_result_t<F, const T&>, void>, event_handle> operator()(F&& fun) {
return this->attach(std::forward<F>(fun));
}
# endif /* DPP_CORO */

# ifdef __cpp_concepts // if c++20, use requires instead of enable_if for better errors
/**
* @brief Attach a lambda to the event, adding a listener.
* The lambda should follow the signature specified when declaring
* the event object and should take exactly one parameter derived
* from event_dispatch_t.
* @brief Attach a callable to the event, adding a listener.
* The callable should be able to take an event object and return void.
*
* @param func Function lambda to attach to event
* @param fun Callable to attach to event
* @return event_handle An event handle unique to this event, used to
* detach the listener from the event later if necessary.
*/
event_handle attach(std::function<void(const T&)> func) {
template <typename F>
requires std::same_as<std::invoke_result_t<F, T>, void>
event_handle attach(F&& fun) {
std::unique_lock l(lock);
event_handle h = next_handle++;
dispatch_container.emplace(h, func);
dispatch_container.emplace(h, std::forward<F>(fun));
return h;
}

#ifdef DPP_CORO
# else
/**
* @brief Attach a coroutine task to the event, adding a listener.
* The coroutine should follow the signature specified when declaring
* the event object and should take exactly one parameter derived
* from event_dispatch_t.
* @brief Attach a callable to the event, adding a listener.
* The callable should be able to take an event object and return void.
*
* @param func Coroutine task to attack to the event. <b>It MUST take the event by value.</b>
* @param fun Callable to attach to event
* @return event_handle An event handle unique to this event, used to
* detach the listener from the event later if necessary.
*/
event_handle co_attach(std::function<job(T)> func) {
template <typename F>
std::enable_if_t<std::is_same_v<std::invoke_result_t<F, T>, void>, event_handle> attach(F&& fun) {
std::unique_lock l(lock);
event_handle h = next_handle++;
coroutine_container.emplace(h, func);
dispatch_container.emplace(h, std::forward<F>(fun));
return h;
}
#endif /* DPP_CORO */
# endif /* __cpp_concepts */
# ifdef DPP_CORO
/**
* @brief Attach a callable to the event, adding a listener.
* The callable should be able to take an event object and return a dpp::job.
*
* @param fun Callable to attach to event
* @return event_handle An event handle unique to this event, used to
* detach the listener from the event later if necessary.
*/
template <typename F>
requires utility::callable_returns<F, dpp::job, const T&>
event_handle attach(F&& fun) {
std::unique_lock l(lock);
event_handle h = next_handle++;
dispatch_container.emplace(h, std::forward<F>(fun));
return h;
}
# endif /* DPP_CORO */
#endif /* _DOXYGEN_ */
/**
* @brief Detach a listener from the event using a previously obtained ID.
*
*
* @param handle An ID obtained from event_router_t::operator()
* @return true The event was successfully detached
* @return false The ID is invalid (possibly already detached, or does not exist)
*/
bool detach(const event_handle& handle) {
std::unique_lock l(lock);
#ifdef DPP_CORO
return this->dispatch_container.erase(handle) || this->coroutine_container.erase(handle);
#else
return this->dispatch_container.erase(handle);
#endif /* DPP_CORO */
}
};

Expand Down
5 changes: 5 additions & 0 deletions include/dpp/utility.h
Original file line number Diff line number Diff line change
Expand Up @@ -603,5 +603,10 @@ namespace dpp {
*/
void DPP_EXPORT set_thread_name(const std::string& name);

#ifdef __cpp_concepts // if c++20
template <typename F, typename R, typename... Args>
concept callable_returns = std::convertible_to<std::invoke_result_t<F, Args...>, R>;
#endif

} // namespace utility
} // namespace dpp
2 changes: 1 addition & 1 deletion src/unittest/coro.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -389,7 +389,7 @@ void coro_offline_tests()
}

void event_handler_test(dpp::cluster *bot) {
bot->on_message_create.co_attach([](dpp::message_create_t event) -> dpp::job {
bot->on_message_create([](dpp::message_create_t event) -> dpp::job {
if (event.msg.content == "coro test") {
dpp::cluster *bot = event.from->creator;

Expand Down
2 changes: 1 addition & 1 deletion src/unittest/test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -574,7 +574,7 @@ Markdown lol \\|\\|spoiler\\|\\| \\~\\~strikethrough\\~\\~ \\`small \\*code\\* b
* are sending audio later, this way if the audio receive code is plain unstable
* the test suite will crash and fail.
*/
bot.on_voice_receive_combined([&](auto& event) {
bot.on_voice_receive_combined([&](const auto& event) {
});

std::promise<void> ready_promise;
Expand Down

0 comments on commit cc3ddfb

Please sign in to comment.