-
Notifications
You must be signed in to change notification settings - Fork 2
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #6 from LemLib/feature/event-handler
feat: ✨ Finish event handler implementation
- Loading branch information
Showing
5 changed files
with
424 additions
and
114 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,57 +1,81 @@ | ||
#pragma once | ||
|
||
#include <mutex> | ||
#include <utility> | ||
#include <functional> | ||
#include <map> | ||
#include <atomic> | ||
#include <vector> | ||
#include <algorithm> | ||
|
||
#include "gamepad/todo.hpp" | ||
#include "pros/rtos.hpp" | ||
#include "gamepad/recursive_mutex.hpp" | ||
|
||
namespace Gamepad { | ||
namespace Gamepad::_impl { | ||
|
||
class MonotonicCounter { | ||
template <typename... Args> friend class EventHandler; | ||
|
||
static uint32_t next_value() { | ||
static std::atomic<uint32_t> counter = 0; | ||
return ++counter; | ||
} | ||
}; | ||
|
||
template <typename... Args> class EventHandler { | ||
/** | ||
* @brief Event handling class with thread safety that supports adding, removing, and running listeners | ||
* | ||
* @tparam Key the key type for (un)registering listener (this type MUST support operator== and operator!=) | ||
* @tparam Args the types of the parameters that each listener is passed | ||
*/ | ||
template <typename Key, typename... Args> class EventHandler { | ||
public: | ||
using Listener = std::function<void(Args...)>; | ||
|
||
uint32_t add_listener(Listener func) { | ||
/** | ||
* @brief Add a listener to the list of listeners | ||
* | ||
* @param key The listener key (this must be a unique key value) | ||
* @param func The function to run when this event is fired | ||
* @return true The listener was successfully added | ||
* @return false The listener was NOT successfully added (there is already a listener with the same key) | ||
*/ | ||
bool add_listener(Key key, Listener func) { | ||
std::lock_guard lock(mutex); | ||
uint32_t id = MonotonicCounter::next_value(); | ||
listeners.emplace(id, std::move(func)); | ||
return id; | ||
if (std::find(keys.begin(), keys.end(), key) != keys.end()) return false; | ||
keys.push_back(key); | ||
listeners.push_back(func); | ||
return true; | ||
} | ||
|
||
bool remove_listener(uint32_t id) { | ||
/** | ||
* @brief Remove a listener from the list of listeners | ||
* | ||
* @param key The listener key (this must be a unique key value) | ||
* @return true The listener was successfully removed | ||
* @return false The listener was NOT successfully removed (there is no listener with the same key) | ||
*/ | ||
bool remove_listener(Key key) { | ||
std::lock_guard lock(mutex); | ||
if (listeners.find(id) == listeners.end()) { | ||
TODO("change handling maybe?") | ||
return false; | ||
auto i = std::find(keys.begin(), keys.end(), key); | ||
if (i != keys.end()) { | ||
keys.erase(i); | ||
listeners.erase(listeners.begin() + (i - keys.begin())); | ||
return true; | ||
} | ||
listeners.erase(id); | ||
return true; | ||
return false; | ||
} | ||
|
||
/** | ||
* @brief Whther or not there are any listeners registered | ||
* | ||
* @return true There are listeners registered | ||
* @return false There are no listeners registered | ||
*/ | ||
bool is_empty() { | ||
std::lock_guard lock(mutex); | ||
return listeners.empty(); | ||
} | ||
|
||
/** | ||
* @brief Runs each listener registered | ||
* | ||
* @param args The parameters to pass to each listener | ||
*/ | ||
void fire(Args... args) { | ||
std::lock_guard lock(mutex); | ||
for (auto listener : listeners) { listener.second(args...); } | ||
for (auto listener : listeners) { listener(args...); } | ||
} | ||
private: | ||
std::map<uint32_t, Listener> listeners; | ||
pros::Mutex mutex; | ||
std::vector<Key> keys {}; | ||
std::vector<Listener> listeners {}; | ||
Gamepad::_impl::RecursiveMutex mutex {}; | ||
}; | ||
} // namespace Gamepad | ||
} // namespace Gamepad::_impl |
Oops, something went wrong.