Skip to content

Commit 9650d13

Browse files
committed
EventDispatcher: Support multiple parameters
1 parent 4226b54 commit 9650d13

File tree

9 files changed

+55
-42
lines changed

9 files changed

+55
-42
lines changed

README.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,8 @@
8484

8585
### 🚦 Multithreading & Synchronization
8686
- [`Scheduler`](modules/Thread/Scheduler.mpp) - Simple scheduler to run delayed functions on separate thread
87+
- [`ScheduledEventDispatcher`](modules/Thread/ScheduledEventDispatcher.mpp) - Dispatches events asynchronously with timed (delay/when) execution
88+
- [`AsyncEventDispatcher`](modules/Thread/AsyncEventDispatcher.mpp) - Event dispatcher that uses a ThreadPool for immediate asynchronous event execution
8789
- [`ThreadLoop`](modules/Thread/ThreadLoop.mpp) - Thread loop with exception handling
8890
- [`ThreadPool`](modules/Thread/ThreadPool.mpp) - Fixed-size thread pool for parallel task execution
8991
- [`TryAsync`](modules/Thread/TryAsync.mpp) - Launches a function asynchronously, forwards exception to caller

modules/Container/MeshNetwork.mpp

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -302,7 +302,7 @@ export namespace CppUtils::Container
302302
[[nodiscard]] auto operator[](const Key& key) -> Branch<MeshNodePtr<Key, Value>>
303303
{
304304
if (not m_node)
305-
throw std::runtime_error{"Invalid MeshNodePtr: cannot access branch on a null node"};
305+
throw std::runtime_error{"MeshNodePtr::operator[]: Invalid MeshNodePtr"};
306306

307307
auto uniqueLocker = m_node->uniqueAccess();
308308
auto& branches = uniqueLocker->value.branches;
@@ -314,13 +314,13 @@ export namespace CppUtils::Container
314314
using namespace std::literals;
315315

316316
if (not m_node)
317-
return std::unexpected{"Invalid MeshNodePtr: cannot access branch on a null node"sv};
317+
return std::unexpected{"MeshNodePtr::at(): Invalid MeshNodePtr"sv};
318318

319319
auto sharedLocker = m_node->sharedAccess();
320320
const auto& branches = sharedLocker->value.branches;
321321
if (auto iterator = branches.find(key); iterator != std::cend(branches))
322322
return Branch{*this, key, iterator->second};
323-
return std::unexpected{"Key not found in MeshNodePtr branches"sv};
323+
return std::unexpected{"MeshNodePtr::at(): Key not found"sv};
324324
}
325325

326326
[[nodiscard]] auto contains(const Key& key) const -> bool

modules/Execution/EventDispatcher.mpp

Lines changed: 20 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -11,36 +11,35 @@ export namespace CppUtils::Execution
1111
using Key = std::pair<String::Hash, std::type_index>;
1212

1313
public:
14-
template<String::Hasher eventName = String::Hash{}, class Event = std::nullptr_t>
15-
inline auto emit(const Event& event = nullptr) -> void
14+
template<String::Hasher eventName = String::Hash{}, class... Args>
15+
inline auto emit(const Args&... args) -> void
1616
{
1717
auto lockGuard = std::shared_lock{m_mutex};
18-
auto key = std::make_pair(static_cast<String::Hash>(eventName), std::type_index{typeid(Event)});
19-
if (auto subscriberIt = m_subscribers.find(key); subscriberIt == std::cend(m_subscribers))
20-
return;
21-
else
18+
using Tuple = std::tuple<std::remove_cvref_t<Args>...>;
19+
auto key = std::make_pair(static_cast<String::Hash>(eventName), std::type_index{typeid(Tuple)});
20+
if (auto subscriberIt = m_subscribers.find(key); subscriberIt != std::cend(m_subscribers))
21+
{
22+
auto payload = std::make_tuple(args...);
2223
for (auto& function : subscriberIt->second)
23-
function(std::addressof(event));
24+
function(&payload);
25+
}
2426
}
2527

2628
template<String::Hasher eventName = String::Hash{}>
2729
inline auto subscribe(auto&& function) -> void
2830
{
2931
using FunctionType = std::decay_t<decltype(function)>;
30-
using FunctionInformations = Type::CallableTrait<FunctionType>;
31-
using ArgumentsTypes = FunctionInformations::ArgumentsTypes;
32-
constexpr auto nbArguments = std::tuple_size_v<ArgumentsTypes>;
33-
static_assert(nbArguments == 1, "EventDispatcher: subscribed callable must take exactly one argument");
34-
using Event = std::remove_cvref_t<std::tuple_element_t<0, ArgumentsTypes>>;
35-
36-
auto lockGuard = std::unique_lock{m_mutex};
37-
auto key = std::make_pair(static_cast<String::Hash>(eventName), std::type_index{typeid(Event)});
38-
39-
m_subscribers[key].emplace_back(
40-
[function = std::forward<decltype(function)>(function)](const void* baseEvent) -> void {
41-
const Event& event = *static_cast<const Event*>(baseEvent);
42-
function(event);
43-
});
32+
using ArgumentsTypes = typename Type::CallableTrait<FunctionType>::ArgumentsTypes;
33+
[&]<class... Args>(std::tuple<Args...>*) {
34+
using Tuple = std::tuple<std::remove_cvref_t<Args>...>;
35+
auto lockGuard = std::unique_lock{m_mutex};
36+
auto key = std::make_pair(static_cast<String::Hash>(eventName), std::type_index{typeid(Tuple)});
37+
m_subscribers[key].emplace_back(
38+
[function = std::forward<decltype(function)>(function)](const void* payload) -> void {
39+
const auto& args = *static_cast<const Tuple*>(payload);
40+
std::apply(function, args);
41+
});
42+
}(static_cast<ArgumentsTypes*>(nullptr));
4443
}
4544

4645
private:

modules/Terminal/Canvas.mpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -104,7 +104,7 @@ export namespace CppUtils::Terminal
104104
Area{size, optionalViewport.value_or(Viewport{size, {0, 0}})},
105105
m_previousBuffer{size}
106106
{
107-
m_widgetManager.eventDispatcher.subscribe<"RequestUpdate">([this](std::nullptr_t) -> void {
107+
m_widgetManager.eventDispatcher.subscribe<"RequestUpdate">([this] {
108108
print();
109109
});
110110
setWidgetManager(m_widgetManager);

modules/Thread/AsyncEventDispatcher.mpp

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -23,11 +23,13 @@ export namespace CppUtils::Thread
2323
m_eventDispatcher.subscribe<eventName>(std::forward<decltype(function)>(function));
2424
}
2525

26-
template<String::Hasher eventName = String::Hash{}, class Event = std::nullptr_t>
27-
inline auto emit(Event&& event = nullptr) -> void
26+
template<String::Hasher eventName = String::Hash{}, class... Args>
27+
inline auto emit(Args&&... args) -> void
2828
{
29-
m_threadPool.call([this, event = std::forward<Event>(event)] {
30-
m_eventDispatcher.emit<eventName>(event);
29+
m_threadPool.call([this, payload = std::make_tuple(std::forward<Args>(args)...)] {
30+
std::apply([this](const auto&... args) {
31+
m_eventDispatcher.emit<eventName>(args...);
32+
}, payload);
3133
});
3234
}
3335

modules/Thread/ScheduledEventDispatcher.mpp

Lines changed: 18 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -28,22 +28,32 @@ export namespace CppUtils::Thread
2828
m_eventDispatcher.subscribe<eventName>(std::forward<decltype(function)>(function));
2929
}
3030

31-
template<String::Hasher eventName = String::Hash{}, class Event = std::nullptr_t, Chrono::Duration Delay = std::chrono::milliseconds>
32-
inline auto emit(Event&& event = nullptr, Delay delay = std::chrono::milliseconds{0}) -> void
31+
template<String::Hasher eventName = String::Hash{}, class... Args, Chrono::Duration Delay = std::chrono::milliseconds>
32+
inline auto emit(Delay delay, Args&&... args) -> void
3333
{
34-
m_scheduler.schedule([this, event = std::forward<Event>(event)] {
35-
m_eventDispatcher.emit<eventName>(event);
34+
m_scheduler.schedule([this, payload = std::make_tuple(std::forward<Args>(args)...)] {
35+
std::apply([this](const auto&... args) {
36+
m_eventDispatcher.emit<eventName>(args...);
37+
}, payload);
3638
}, delay);
3739
}
3840

39-
template<String::Hasher eventName = String::Hash{}, class Event>
40-
inline auto emit(Event&& event, TimePoint when) -> void
41+
template<String::Hasher eventName = String::Hash{}, class... Args>
42+
inline auto emit(TimePoint when, Args&&... args) -> void
4143
{
42-
m_scheduler.schedule([this, event = std::forward<Event>(event)] {
43-
m_eventDispatcher.emit<eventName>(event);
44+
m_scheduler.schedule([this, payload = std::make_tuple(std::forward<Args>(args)...)] {
45+
std::apply([this](const auto&... args) {
46+
m_eventDispatcher.emit<eventName>(args...);
47+
}, payload);
4448
}, when);
4549
}
4650

51+
template<String::Hasher eventName = String::Hash{}, class... Args>
52+
inline auto emit(Args&&... args) -> void
53+
{
54+
emit<eventName>(std::chrono::milliseconds{0}, std::forward<Args>(args)...);
55+
}
56+
4757
inline auto waitUntilFinished() -> void
4858
{
4959
m_scheduler.waitUntilFinished();

tests/Container/MeshNetwork.mpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -566,14 +566,14 @@ export namespace CppUtils::UnitTest::Container::MeshNetwork
566566

567567
auto result = root.at("NonExistentBranch");
568568
suite.expect(not result.has_value());
569-
suite.expectEqual(result.error(), "Key not found in MeshNodePtr branches"sv);
569+
suite.expectEqual(result.error(), "MeshNodePtr::at(): Key not found"sv);
570570
});
571571

572572
suite.addTest("at(): Invalid node", [&] {
573573
const auto invalidNode = StringMeshNodePtr{};
574574
auto result = invalidNode.at("Branch");
575575
suite.expect(not result.has_value());
576-
suite.expectEqual(result.error(), "Invalid MeshNodePtr: cannot access branch on a null node"sv);
576+
suite.expectEqual(result.error(), "MeshNodePtr::at(): Invalid MeshNodePtr"sv);
577577
});
578578

579579
suite.addTest("Get first node", [&] {

tests/Execution/EventDispatcher.mpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -101,10 +101,10 @@ export namespace CppUtils::UnitTest::Execution::EventDispatcher
101101
auto expect1 = std::atomic_bool{false};
102102
auto expect2 = std::atomic_bool{false};
103103

104-
eventDispatcher.subscribe<"Event">([&expect1](std::nullptr_t) -> void {
104+
eventDispatcher.subscribe<"Event">([&expect1]() -> void {
105105
expect1 = true;
106106
});
107-
eventDispatcher.subscribe<"Event">([&expect2](std::nullptr_t) -> void {
107+
eventDispatcher.subscribe<"Event">([&expect2]() -> void {
108108
expect2 = true;
109109
});
110110
eventDispatcher.emit<"Event">();

tests/Thread/ScheduledEventDispatcher.mpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ export namespace CppUtils::UnitTest::Thread::ScheduledEventDispatcher
2828
dispatcher.subscribe<"Event">([&](bool value) {
2929
receivedValue = value;
3030
});
31-
dispatcher.emit<"Event">(true, 200ms);
31+
dispatcher.emit<"Event">(200ms, true);
3232

3333
std::this_thread::sleep_for(100ms);
3434
suite.expectEqual(receivedValue.load(), false);

0 commit comments

Comments
 (0)