Skip to content

Commit 169f9a6

Browse files
committed
Execution/EventSystem
1 parent e43e6bb commit 169f9a6

5 files changed

Lines changed: 165 additions & 0 deletions

File tree

modules/CppUtils.mpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ export module CppUtils;
33
export import std;
44
export import CppUtils.Chrono;
55
export import CppUtils.Container;
6+
export import CppUtils.Execution;
67
export import CppUtils.FileSystem;
78
export import CppUtils.Language;
89
export import CppUtils.ChronoLogger;

modules/Execution/EventSystem.mpp

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
export module CppUtils.Execution.EventSystem;
2+
3+
import std;
4+
import CppUtils.Type.Callable;
5+
import CppUtils.String.Hash;
6+
7+
export namespace CppUtils::Execution
8+
{
9+
class EventSystem final
10+
{
11+
using GenericFunction = std::unique_ptr<void, void (*)(void*)>;
12+
using Key = std::pair<String::Hash, std::type_index>;
13+
14+
public:
15+
template<String::Hasher eventName = String::Hash{}>
16+
inline auto emit(auto event) -> void
17+
{
18+
using Event = decltype(event);
19+
auto lockGuard = std::shared_lock{m_mutex};
20+
21+
auto key = std::make_pair(static_cast<String::Hash>(eventName), std::type_index{typeid(Event)});
22+
if (auto subscriberIt = m_subscribers.find(key); subscriberIt == std::cend(m_subscribers))
23+
return;
24+
else
25+
for (auto& function : subscriberIt->second)
26+
std::invoke(*static_cast<std::function<void(const Event&)>*>(function.get()), event);
27+
}
28+
29+
template<String::Hasher eventName = String::Hash{}>
30+
inline auto subscribe(auto&& function) -> void
31+
{
32+
using FunctionType = std::decay_t<decltype(function)>;
33+
using FunctionInformations = Type::CallableTrait<FunctionType>;
34+
using ArgumentsTypes = FunctionInformations::ArgumentsTypes;
35+
constexpr auto nbArguments = std::tuple_size_v<ArgumentsTypes>;
36+
static_assert(nbArguments == 1, "EventSystem: subscribed callable must take exactly one argument");
37+
using Event = std::tuple_element_t<0, ArgumentsTypes>;
38+
39+
auto lockGuard = std::unique_lock{m_mutex};
40+
41+
auto key = std::make_pair(static_cast<String::Hash>(eventName), std::type_index{typeid(Event)});
42+
43+
using Function = std::function<void(const Event&)>;
44+
auto functionPointer = std::make_unique<Function>(std::forward<decltype(function)>(function));
45+
auto genericPointer = GenericFunction{functionPointer.release(), [](void* pointer) { delete static_cast<Function*>(pointer); }};
46+
m_subscribers[key].emplace_back(std::move(genericPointer));
47+
}
48+
49+
private:
50+
struct PairHasher final
51+
{
52+
[[nodiscard]] inline auto operator()(const Key& key) const noexcept -> std::size_t
53+
{
54+
return std::hash<String::Hash>{}(key.first) ^ (key.second.hash_code() << 1);
55+
}
56+
};
57+
58+
std::shared_mutex m_mutex;
59+
std::unordered_map<Key, std::vector<GenericFunction>, PairHasher> m_subscribers;
60+
};
61+
}

modules/Execution/Execution.mpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
export module CppUtils.Execution;
2+
3+
export import CppUtils.Execution.EventSystem;

tests/Execution/EventSystem.mpp

Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
export module CppUtils.UnitTests.Execution.EventSystem;
2+
3+
import std;
4+
import CppUtils;
5+
6+
export namespace CppUtils::UnitTest::Execution::EventSystem
7+
{
8+
inline auto _ = TestSuite{
9+
"Execution/EventSystem", {"Logger"}, [](auto& suite) {
10+
using namespace std::literals;
11+
using Logger = CppUtils::Logger<"CppUtils">;
12+
13+
suite.addTest("Empty", [&] {
14+
[[maybe_unused]] auto eventSystem = CppUtils::Execution::EventSystem{};
15+
});
16+
17+
suite.addTest("Subscribe", [&] {
18+
auto eventSystem = CppUtils::Execution::EventSystem{};
19+
20+
eventSystem.subscribe<>([&suite](const std::string& message) -> void {
21+
Logger::print("{}\n", message);
22+
suite.expect(false);
23+
});
24+
});
25+
26+
suite.addTest("Subscribe with name", [&] {
27+
auto eventSystem = CppUtils::Execution::EventSystem{};
28+
29+
eventSystem.subscribe<"Ping">([&suite](const std::string& message) -> void {
30+
Logger::print("{}\n", message);
31+
suite.expect(false);
32+
});
33+
});
34+
35+
suite.addTest("Emit", [&] {
36+
auto eventSystem = CppUtils::Execution::EventSystem{};
37+
eventSystem.emit<>("ping"s);
38+
});
39+
40+
suite.addTest("Emit", [&] {
41+
auto eventSystem = CppUtils::Execution::EventSystem{};
42+
eventSystem.emit<"Ping">("ping"s);
43+
});
44+
45+
suite.addTest("Send/Receive", [&] {
46+
auto eventSystem = CppUtils::Execution::EventSystem{};
47+
auto expectPingReceived = ExpectedCall{};
48+
49+
eventSystem.subscribe<>([&expectPingReceived, &suite](const std::string& message) -> void {
50+
Logger::print("{}\n", message);
51+
expectPingReceived.call();
52+
suite.expectEqual(message, "ping"sv);
53+
});
54+
eventSystem.emit<>("ping"s);
55+
56+
suite.expectCall(expectPingReceived);
57+
});
58+
59+
suite.addTest("Send/Receive with name", [&] {
60+
auto eventSystem = CppUtils::Execution::EventSystem{};
61+
auto expectPingReceived = ExpectedCall{};
62+
63+
eventSystem.subscribe<"Ping">([&expectPingReceived, &suite](const std::string& message) -> void {
64+
Logger::print("{}\n", message);
65+
expectPingReceived.call();
66+
suite.expectEqual(message, "ping"sv);
67+
});
68+
eventSystem.subscribe<"Other">([&suite](const std::string& message) -> void {
69+
Logger::print("{}\n", message);
70+
suite.expect(false);
71+
});
72+
eventSystem.emit<"Ping">("ping"s);
73+
74+
suite.expectCall(expectPingReceived);
75+
});
76+
77+
suite.addTest("Cascade calls", [&] {
78+
auto eventSystem = CppUtils::Execution::EventSystem{};
79+
auto expectPingReceived = ExpectedCall{};
80+
auto expectPongReceived = ExpectedCall{};
81+
82+
eventSystem.subscribe<"Ping">([&expectPingReceived, &suite, &eventSystem](const std::string& message) -> void {
83+
Logger::print("{}\n", message);
84+
expectPingReceived.call();
85+
suite.expectEqual(message, "ping"sv);
86+
eventSystem.emit<"Pong">("pong"s);
87+
});
88+
eventSystem.subscribe<"Pong">([&expectPongReceived, &suite](const std::string& message) -> void {
89+
Logger::print("{}\n", message);
90+
expectPongReceived.call();
91+
suite.expectEqual(message, "pong"sv);
92+
});
93+
eventSystem.emit<"Ping">("ping"s);
94+
95+
suite.expectCall(expectPingReceived);
96+
suite.expectCall(expectPongReceived);
97+
});
98+
}};
99+
}

tests/UnitTests.mpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ export import CppUtils.UnitTests.Container.Stack;
1212
export import CppUtils.UnitTests.Container.Tree;
1313
export import CppUtils.UnitTests.Container.TypedStack;
1414
export import CppUtils.UnitTests.Container.Vector;
15+
export import CppUtils.UnitTests.Execution.EventSystem;
1516
export import CppUtils.UnitTests.FileSystem.Directory;
1617
export import CppUtils.UnitTests.FileSystem.File;
1718
export import CppUtils.UnitTests.FileSystem.Watcher;

0 commit comments

Comments
 (0)