Add foundation event dispatcher tests

This commit is contained in:
2026-06-01 08:28:57 +02:00
parent 3f5711773e
commit 6c435dafb7
9 changed files with 302 additions and 8 deletions

97
src/foundation/event.cpp Normal file
View File

@@ -0,0 +1,97 @@
#include "foundation/event.h"
#include <algorithm>
namespace pp::foundation {
EventDispatcher::EventDispatcher(std::size_t max_subscriptions) noexcept
: max_subscriptions_(max_subscriptions)
{
subscriptions_.reserve(std::min(max_subscriptions_, max_event_subscriptions));
}
std::size_t EventDispatcher::size() const noexcept
{
return subscriptions_.size();
}
bool EventDispatcher::empty() const noexcept
{
return subscriptions_.empty();
}
std::size_t EventDispatcher::max_subscriptions() const noexcept
{
return max_subscriptions_;
}
Result<std::uint64_t> EventDispatcher::subscribe(std::uint32_t type, EventCallback callback, void* user_data)
{
if (max_subscriptions_ == 0U || max_subscriptions_ > max_event_subscriptions) {
return Result<std::uint64_t>::failure(
Status::out_of_range("event dispatcher capacity is outside the configured range"));
}
if (type == 0U) {
return Result<std::uint64_t>::failure(Status::invalid_argument("event type must not be zero"));
}
if (callback == nullptr) {
return Result<std::uint64_t>::failure(Status::invalid_argument("event callback must not be null"));
}
if (subscriptions_.size() >= max_subscriptions_) {
return Result<std::uint64_t>::failure(Status::out_of_range("event dispatcher is full"));
}
const auto id = next_subscription_id_++;
subscriptions_.push_back(EventSubscription {
.id = id,
.type = type,
.callback = callback,
.user_data = user_data,
});
return Result<std::uint64_t>::success(id);
}
Status EventDispatcher::unsubscribe(std::uint64_t subscription_id) noexcept
{
const auto found = std::find_if(
subscriptions_.begin(),
subscriptions_.end(),
[subscription_id](const EventSubscription& subscription) {
return subscription.id == subscription_id;
});
if (found == subscriptions_.end()) {
return Status::out_of_range("event subscription id was not found");
}
subscriptions_.erase(found);
return Status::success();
}
std::size_t EventDispatcher::publish(const Event& event) const noexcept
{
if (event.type == 0U) {
return 0;
}
std::size_t delivered = 0;
for (const auto& subscription : subscriptions_) {
if (subscription.type == event.type) {
subscription.callback(event, subscription.user_data);
++delivered;
}
}
return delivered;
}
void EventDispatcher::clear() noexcept
{
subscriptions_.clear();
}
}

48
src/foundation/event.h Normal file
View File

@@ -0,0 +1,48 @@
#pragma once
#include "foundation/result.h"
#include <cstddef>
#include <cstdint>
#include <vector>
namespace pp::foundation {
constexpr std::size_t max_event_subscriptions = 65536;
struct Event {
std::uint32_t type = 0;
std::uint64_t source_id = 0;
std::uint64_t frame_id = 0;
std::uint64_t payload_u64 = 0;
};
using EventCallback = void (*)(const Event& event, void* user_data) noexcept;
struct EventSubscription {
std::uint64_t id = 0;
std::uint32_t type = 0;
EventCallback callback = nullptr;
void* user_data = nullptr;
};
class EventDispatcher {
public:
explicit EventDispatcher(std::size_t max_subscriptions = max_event_subscriptions) noexcept;
[[nodiscard]] std::size_t size() const noexcept;
[[nodiscard]] bool empty() const noexcept;
[[nodiscard]] std::size_t max_subscriptions() const noexcept;
[[nodiscard]] Result<std::uint64_t> subscribe(std::uint32_t type, EventCallback callback, void* user_data);
[[nodiscard]] Status unsubscribe(std::uint64_t subscription_id) noexcept;
[[nodiscard]] std::size_t publish(const Event& event) const noexcept;
void clear() noexcept;
private:
std::size_t max_subscriptions_ = max_event_subscriptions;
std::uint64_t next_subscription_id_ = 1;
std::vector<EventSubscription> subscriptions_;
};
}