ui/core/token.hpp¶
High-performance type-safe token system with runtime registration. More...
Namespaces¶
| Name |
|---|
| cf |
| cf::ui |
| cf::ui::core |
| cf::ui::core::detail |
Classes¶
| Name | |
|---|---|
| struct | cf::ui::core::TokenError Error type for Token operations. |
| class | cf::ui::core::StaticToken Compile-time type-safe token with zero runtime overhead. |
| struct | cf::ui::core::detail::TokenSlot Storage slot for a single token entry. |
| class | cf::ui::core::TokenRegistry Thread-safe token registry. |
| class | cf::ui::core::EmbeddedTokenRegistry Embeddable token registry for component-scoped token storage. |
Detailed Description¶
High-performance type-safe token system with runtime registration.
Author: Charliechen114514
Version: 0.1
Since: 0.1
Date: 2026-02-25
Provides a dual-mode token system:
- StaticToken: Compile-time type-safe tokens with zero overhead
- DynamicToken: Runtime type-erased tokens using std::any
- TokenRegistry: Read-safe registry using shared_mutex for read-heavy workloads
Source code¶
#pragma once
#include <any>
#include <memory>
#include <mutex>
#include <shared_mutex>
#include <string>
#include <string_view>
#include <unordered_map>
#include "base/expected/expected.hpp"
#include "base/hash/constexpr_fnv1a.hpp"
namespace cf::ui::core {
// =============================================================================
// Forward Declarations
// =============================================================================
class TokenRegistry;
template <typename T, uint64_t Hash> class StaticToken;
// =============================================================================
// Error Types
// =============================================================================
struct TokenError {
enum class Kind {
NotFound,
TypeMismatch,
AlreadyRegistered,
Empty
} kind = Kind::NotFound;
std::string message;
TokenError() = default;
TokenError(Kind k, std::string msg) : kind(k), message(std::move(msg)) {}
explicit operator bool() const noexcept { return kind != Kind::NotFound; }
};
// =============================================================================
// StaticToken - Compile-Time Type-Safe Token
// =============================================================================
template <typename T, uint64_t Hash> class StaticToken {
public:
using value_type = T;
static constexpr uint64_t hash_value = Hash;
StaticToken() = delete;
StaticToken(const StaticToken&) = delete;
StaticToken& operator=(const StaticToken&) = delete;
static cf::expected<T*, TokenError> get();
static cf::expected<const T*, TokenError> get_const();
};
// =============================================================================
// TokenRegistry - Shared-Mutex Protected Storage
// =============================================================================
namespace detail {
struct TokenSlot {
std::unique_ptr<std::any> data;
const std::type_info* type_info = nullptr;
std::string name;
TokenSlot() = default;
TokenSlot(const TokenSlot&) = delete;
TokenSlot& operator=(const TokenSlot&) = delete;
TokenSlot(TokenSlot&& other) noexcept
: data(std::move(other.data)), type_info(other.type_info), name(std::move(other.name)) {}
TokenSlot& operator=(TokenSlot&& other) noexcept {
if (this != &other) {
data = std::move(other.data);
type_info = other.type_info;
name = std::move(other.name);
}
return *this;
}
};
} // namespace detail
class TokenRegistry {
public:
template <typename T> using Result = cf::expected<T, TokenError>;
static TokenRegistry& get();
// Non-copyable, non-movable
TokenRegistry(const TokenRegistry&) = delete;
TokenRegistry& operator=(const TokenRegistry&) = delete;
TokenRegistry(TokenRegistry&&) = delete;
TokenRegistry& operator=(TokenRegistry&&) = delete;
template <typename TokenToken, typename... Args> Result<void> register_token(Args&&... args);
template <typename TokenToken> Result<typename TokenToken::value_type*> get();
template <typename TokenToken> Result<const typename TokenToken::value_type*> get_const() const;
template <typename T, typename... Args>
Result<void> register_dynamic(std::string_view name, Args&&... args);
template <typename T> Result<void> register_dynamic(std::string_view name, const T& value);
template <typename T> Result<void> register_dynamic(std::string_view name, T&& value);
template <typename T> Result<T*> get_dynamic(std::string_view name);
template <typename T> Result<T*> get_dynamic_by_hash(uint64_t hash);
template <typename T> Result<const T*> get_dynamic_const(std::string_view name) const;
bool contains(uint64_t hash) const noexcept;
bool contains(std::string_view name) const noexcept;
bool remove(uint64_t hash);
bool remove(std::string_view name);
size_t size() const noexcept;
private:
TokenRegistry() = default;
~TokenRegistry() = default;
// Internal helpers — caller must already hold the appropriate lock.
// Returns nullptr if not found. Lock must be held by caller.
detail::TokenSlot* find_slot_locked(uint64_t hash);
const detail::TokenSlot* find_slot_locked(uint64_t hash) const;
// Shared implementation for get_dynamic / get_dynamic_by_hash (non-const).
template <typename T> Result<T*> get_by_hash_impl(uint64_t hash, const std::string& name_hint);
// Shared implementation for get_dynamic_const.
template <typename T>
Result<const T*> get_by_hash_impl_const(uint64_t hash, const std::string& name_hint) const;
mutable std::shared_mutex registry_mutex_;
std::unordered_map<uint64_t, detail::TokenSlot> slot_map_;
};
// =============================================================================
// Inline Implementations - StaticToken
// =============================================================================
template <typename T, uint64_t Hash> auto StaticToken<T, Hash>::get()
-> cf::expected<T*, TokenError> {
return TokenRegistry::get().get<StaticToken<T, Hash>>();
}
template <typename T, uint64_t Hash> auto StaticToken<T, Hash>::get_const()
-> cf::expected<const T*, TokenError> {
return TokenRegistry::get().get_const<StaticToken<T, Hash>>();
}
// =============================================================================
// Inline Implementations - TokenRegistry
// =============================================================================
inline TokenRegistry& TokenRegistry::get() {
static TokenRegistry instance;
return instance;
}
inline size_t TokenRegistry::size() const noexcept {
std::shared_lock<std::shared_mutex> lock(registry_mutex_);
return slot_map_.size();
}
inline bool TokenRegistry::contains(uint64_t hash) const noexcept {
std::shared_lock<std::shared_mutex> lock(registry_mutex_);
return slot_map_.find(hash) != slot_map_.end();
}
inline bool TokenRegistry::contains(std::string_view name) const noexcept {
return contains(cf::hash::fnv1a64(name));
}
inline bool TokenRegistry::remove(uint64_t hash) {
std::unique_lock<std::shared_mutex> lock(registry_mutex_);
return slot_map_.erase(hash) > 0;
}
inline bool TokenRegistry::remove(std::string_view name) {
return remove(cf::hash::fnv1a64(name));
}
// These helpers are only called while the caller already holds the mutex.
inline detail::TokenSlot* TokenRegistry::find_slot_locked(uint64_t hash) {
auto it = slot_map_.find(hash);
return (it != slot_map_.end()) ? &it->second : nullptr;
}
inline const detail::TokenSlot* TokenRegistry::find_slot_locked(uint64_t hash) const {
auto it = slot_map_.find(hash);
return (it != slot_map_.end()) ? &it->second : nullptr;
}
// -----------------------------------------------------------------------------
// Static Token Registration
// -----------------------------------------------------------------------------
template <typename TokenToken, typename... Args> auto TokenRegistry::register_token(Args&&... args)
-> Result<void> {
using T = typename TokenToken::value_type;
constexpr uint64_t hash = TokenToken::hash_value;
std::unique_lock<std::shared_mutex> lock(registry_mutex_);
if (slot_map_.find(hash) != slot_map_.end()) {
return cf::unexpected(
TokenError{TokenError::Kind::AlreadyRegistered,
"Token already registered, hash: " + std::to_string(hash)});
}
detail::TokenSlot slot;
slot.data = std::make_unique<std::any>(T(std::forward<Args>(args)...));
slot.type_info = &typeid(T);
slot.name = "static_token_" + std::to_string(hash);
slot_map_.emplace(hash, std::move(slot));
return {};
}
// -----------------------------------------------------------------------------
// Static Token Get
// -----------------------------------------------------------------------------
template <typename TokenToken> auto TokenRegistry::get()
-> Result<typename TokenToken::value_type*> {
using T = typename TokenToken::value_type;
constexpr uint64_t hash = TokenToken::hash_value;
// Hold shared lock for the entire operation to prevent remove() from
// destroying the slot while reading from it.
std::shared_lock<std::shared_mutex> lock(registry_mutex_);
const detail::TokenSlot* slot = find_slot_locked(hash);
if (!slot) {
return cf::unexpected(TokenError{TokenError::Kind::NotFound,
"Token not found, hash: " + std::to_string(hash)});
}
std::any* a = slot->data.get();
if (!a || !a->has_value()) {
return cf::unexpected(TokenError{TokenError::Kind::Empty, "Token has no value"});
}
if (slot->type_info != &typeid(T)) {
return cf::unexpected(
TokenError{TokenError::Kind::TypeMismatch, "Type mismatch for token"});
}
return std::any_cast<T>(a);
}
template <typename TokenToken> auto TokenRegistry::get_const() const
-> Result<const typename TokenToken::value_type*> {
using T = typename TokenToken::value_type;
constexpr uint64_t hash = TokenToken::hash_value;
std::shared_lock<std::shared_mutex> lock(registry_mutex_);
const detail::TokenSlot* slot = find_slot_locked(hash);
if (!slot) {
return cf::unexpected(TokenError{TokenError::Kind::NotFound,
"Token not found, hash: " + std::to_string(hash)});
}
const std::any* a = slot->data.get();
if (!a || !a->has_value()) {
return cf::unexpected(TokenError{TokenError::Kind::Empty, "Token has no value"});
}
if (slot->type_info != &typeid(T)) {
return cf::unexpected(
TokenError{TokenError::Kind::TypeMismatch, "Type mismatch for token"});
}
return std::any_cast<T>(a);
}
// -----------------------------------------------------------------------------
// Dynamic Token Registration
// -----------------------------------------------------------------------------
template <typename T, typename... Args>
auto TokenRegistry::register_dynamic(std::string_view name, Args&&... args) -> Result<void> {
uint64_t hash = cf::hash::fnv1a64(name);
std::unique_lock<std::shared_mutex> lock(registry_mutex_);
if (slot_map_.find(hash) != slot_map_.end()) {
return cf::unexpected(TokenError{TokenError::Kind::AlreadyRegistered,
"Already registered: " + std::string(name)});
}
detail::TokenSlot slot;
slot.data = std::make_unique<std::any>(T(std::forward<Args>(args)...));
slot.type_info = &typeid(T);
slot.name = name;
slot_map_.emplace(hash, std::move(slot));
return {};
}
template <typename T> auto TokenRegistry::register_dynamic(std::string_view name, const T& value)
-> Result<void> {
uint64_t hash = cf::hash::fnv1a64(name);
std::unique_lock<std::shared_mutex> lock(registry_mutex_);
if (slot_map_.find(hash) != slot_map_.end()) {
return cf::unexpected(TokenError{TokenError::Kind::AlreadyRegistered,
"Already registered: " + std::string(name)});
}
detail::TokenSlot slot;
slot.data = std::make_unique<std::any>(value);
slot.type_info = &typeid(T);
slot.name = name;
slot_map_.emplace(hash, std::move(slot));
return {};
}
template <typename T> auto TokenRegistry::register_dynamic(std::string_view name, T&& value)
-> Result<void> {
uint64_t hash = cf::hash::fnv1a64(name);
std::unique_lock<std::shared_mutex> lock(registry_mutex_);
if (slot_map_.find(hash) != slot_map_.end()) {
return cf::unexpected(TokenError{TokenError::Kind::AlreadyRegistered,
"Already registered: " + std::string(name)});
}
detail::TokenSlot slot;
slot.data = std::make_unique<std::any>(std::forward<T>(value));
slot.type_info = &typeid(T);
slot.name = name;
slot_map_.emplace(hash, std::move(slot));
return {};
}
// -----------------------------------------------------------------------------
// Dynamic Token Get — shared impl
// -----------------------------------------------------------------------------
template <typename T>
auto TokenRegistry::get_by_hash_impl(uint64_t hash, const std::string& name_hint) -> Result<T*> {
// Lock held by callers (get_dynamic / get_dynamic_by_hash) for entire scope.
const detail::TokenSlot* slot = find_slot_locked(hash);
if (!slot) {
return cf::unexpected(
TokenError{TokenError::Kind::NotFound, "Token not found: " + name_hint});
}
std::any* a = slot->data.get();
if (!a || !a->has_value()) {
return cf::unexpected(
TokenError{TokenError::Kind::Empty, "Token has no value: " + name_hint});
}
if (slot->type_info != &typeid(T)) {
return cf::unexpected(
TokenError{TokenError::Kind::TypeMismatch, "Type mismatch for token: " + name_hint});
}
return std::any_cast<T>(a);
}
template <typename T>
auto TokenRegistry::get_by_hash_impl_const(uint64_t hash, const std::string& name_hint) const
-> Result<const T*> {
const detail::TokenSlot* slot = find_slot_locked(hash);
if (!slot) {
return cf::unexpected(
TokenError{TokenError::Kind::NotFound, "Token not found: " + name_hint});
}
const std::any* a = slot->data.get();
if (!a || !a->has_value()) {
return cf::unexpected(
TokenError{TokenError::Kind::Empty, "Token has no value: " + name_hint});
}
if (slot->type_info != &typeid(T)) {
return cf::unexpected(
TokenError{TokenError::Kind::TypeMismatch, "Type mismatch for token: " + name_hint});
}
return std::any_cast<T>(a);
}
template <typename T> auto TokenRegistry::get_dynamic(std::string_view name) -> Result<T*> {
std::shared_lock<std::shared_mutex> lock(registry_mutex_);
return get_by_hash_impl<T>(cf::hash::fnv1a64(name), std::string(name));
}
template <typename T> auto TokenRegistry::get_dynamic_by_hash(uint64_t hash) -> Result<T*> {
std::shared_lock<std::shared_mutex> lock(registry_mutex_);
return get_by_hash_impl<T>(hash, std::to_string(hash));
}
template <typename T> auto TokenRegistry::get_dynamic_const(std::string_view name) const
-> Result<const T*> {
std::shared_lock<std::shared_mutex> lock(registry_mutex_);
return get_by_hash_impl_const<T>(cf::hash::fnv1a64(name), std::string(name));
}
// =============================================================================
// EmbeddedTokenRegistry - Non-Singleton Embeddable Registry
// =============================================================================
class EmbeddedTokenRegistry {
public:
template <typename T> using Result = cf::expected<T, TokenError>;
EmbeddedTokenRegistry() = default;
~EmbeddedTokenRegistry() = default;
EmbeddedTokenRegistry(const EmbeddedTokenRegistry&) = delete;
EmbeddedTokenRegistry& operator=(const EmbeddedTokenRegistry&) = delete;
EmbeddedTokenRegistry(EmbeddedTokenRegistry&& other) noexcept
: slot_map_(std::move(other.slot_map_)) {}
EmbeddedTokenRegistry& operator=(EmbeddedTokenRegistry&& other) noexcept {
if (this != &other)
slot_map_ = std::move(other.slot_map_);
return *this;
}
template <typename T, typename... Args>
Result<void> register_dynamic(std::string_view name, Args&&... args);
template <typename T> Result<void> register_dynamic(std::string_view name, const T& value);
template <typename T> Result<void> register_dynamic(std::string_view name, T&& value);
template <typename T> Result<T*> get_dynamic(std::string_view name);
template <typename T> Result<T*> get_dynamic_by_hash(uint64_t hash);
template <typename T> Result<const T*> get_dynamic_const(std::string_view name) const;
bool contains(uint64_t hash) const noexcept;
bool contains(std::string_view name) const noexcept;
bool remove(uint64_t hash);
bool remove(std::string_view name);
size_t size() const noexcept;
private:
detail::TokenSlot* find_slot_locked(uint64_t hash);
const detail::TokenSlot* find_slot_locked(uint64_t hash) const;
template <typename T> Result<T*> get_by_hash_impl(uint64_t hash, const std::string& name_hint);
template <typename T>
Result<const T*> get_by_hash_impl_const(uint64_t hash, const std::string& name_hint) const;
mutable std::shared_mutex registry_mutex_;
std::unordered_map<uint64_t, detail::TokenSlot> slot_map_;
};
// =============================================================================
// Inline Implementations - EmbeddedTokenRegistry
// =============================================================================
inline size_t EmbeddedTokenRegistry::size() const noexcept {
std::shared_lock<std::shared_mutex> lock(registry_mutex_);
return slot_map_.size();
}
inline bool EmbeddedTokenRegistry::contains(uint64_t hash) const noexcept {
std::shared_lock<std::shared_mutex> lock(registry_mutex_);
return slot_map_.find(hash) != slot_map_.end();
}
inline bool EmbeddedTokenRegistry::contains(std::string_view name) const noexcept {
return contains(cf::hash::fnv1a64(name));
}
inline bool EmbeddedTokenRegistry::remove(uint64_t hash) {
std::unique_lock<std::shared_mutex> lock(registry_mutex_);
return slot_map_.erase(hash) > 0;
}
inline bool EmbeddedTokenRegistry::remove(std::string_view name) {
return remove(cf::hash::fnv1a64(name));
}
inline detail::TokenSlot* EmbeddedTokenRegistry::find_slot_locked(uint64_t hash) {
auto it = slot_map_.find(hash);
return (it != slot_map_.end()) ? &it->second : nullptr;
}
inline const detail::TokenSlot* EmbeddedTokenRegistry::find_slot_locked(uint64_t hash) const {
auto it = slot_map_.find(hash);
return (it != slot_map_.end()) ? &it->second : nullptr;
}
template <typename T, typename... Args>
auto EmbeddedTokenRegistry::register_dynamic(std::string_view name, Args&&... args)
-> Result<void> {
uint64_t hash = cf::hash::fnv1a64(name);
std::unique_lock<std::shared_mutex> lock(registry_mutex_);
if (slot_map_.find(hash) != slot_map_.end()) {
return cf::unexpected(TokenError{TokenError::Kind::AlreadyRegistered,
"Already registered: " + std::string(name)});
}
detail::TokenSlot slot;
slot.data = std::make_unique<std::any>(T(std::forward<Args>(args)...));
slot.type_info = &typeid(T);
slot.name = name;
slot_map_.emplace(hash, std::move(slot));
return {};
}
template <typename T> auto EmbeddedTokenRegistry::register_dynamic(std::string_view name,
const T& value) -> Result<void> {
uint64_t hash = cf::hash::fnv1a64(name);
std::unique_lock<std::shared_mutex> lock(registry_mutex_);
if (slot_map_.find(hash) != slot_map_.end()) {
return cf::unexpected(TokenError{TokenError::Kind::AlreadyRegistered,
"Already registered: " + std::string(name)});
}
detail::TokenSlot slot;
slot.data = std::make_unique<std::any>(value);
slot.type_info = &typeid(T);
slot.name = name;
slot_map_.emplace(hash, std::move(slot));
return {};
}
template <typename T> auto EmbeddedTokenRegistry::register_dynamic(std::string_view name, T&& value)
-> Result<void> {
uint64_t hash = cf::hash::fnv1a64(name);
std::unique_lock<std::shared_mutex> lock(registry_mutex_);
if (slot_map_.find(hash) != slot_map_.end()) {
return cf::unexpected(TokenError{TokenError::Kind::AlreadyRegistered,
"Already registered: " + std::string(name)});
}
detail::TokenSlot slot;
slot.data = std::make_unique<std::any>(std::forward<T>(value));
slot.type_info = &typeid(T);
slot.name = name;
slot_map_.emplace(hash, std::move(slot));
return {};
}
template <typename T>
auto EmbeddedTokenRegistry::get_by_hash_impl(uint64_t hash, const std::string& name_hint)
-> Result<T*> {
const detail::TokenSlot* slot = find_slot_locked(hash);
if (!slot) {
return cf::unexpected(
TokenError{TokenError::Kind::NotFound, "Token not found: " + name_hint});
}
std::any* a = slot->data.get();
if (!a || !a->has_value()) {
return cf::unexpected(
TokenError{TokenError::Kind::Empty, "Token has no value: " + name_hint});
}
if (slot->type_info != &typeid(T)) {
return cf::unexpected(
TokenError{TokenError::Kind::TypeMismatch, "Type mismatch for token: " + name_hint});
}
return std::any_cast<T>(a);
}
template <typename T> auto
EmbeddedTokenRegistry::get_by_hash_impl_const(uint64_t hash, const std::string& name_hint) const
-> Result<const T*> {
const detail::TokenSlot* slot = find_slot_locked(hash);
if (!slot) {
return cf::unexpected(
TokenError{TokenError::Kind::NotFound, "Token not found: " + name_hint});
}
const std::any* a = slot->data.get();
if (!a || !a->has_value()) {
return cf::unexpected(
TokenError{TokenError::Kind::Empty, "Token has no value: " + name_hint});
}
if (slot->type_info != &typeid(T)) {
return cf::unexpected(
TokenError{TokenError::Kind::TypeMismatch, "Type mismatch for token: " + name_hint});
}
return std::any_cast<T>(a);
}
template <typename T> auto EmbeddedTokenRegistry::get_dynamic(std::string_view name) -> Result<T*> {
std::shared_lock<std::shared_mutex> lock(registry_mutex_);
return get_by_hash_impl<T>(cf::hash::fnv1a64(name), std::string(name));
}
template <typename T> auto EmbeddedTokenRegistry::get_dynamic_by_hash(uint64_t hash) -> Result<T*> {
std::shared_lock<std::shared_mutex> lock(registry_mutex_);
return get_by_hash_impl<T>(hash, std::to_string(hash));
}
template <typename T> auto EmbeddedTokenRegistry::get_dynamic_const(std::string_view name) const
-> Result<const T*> {
std::shared_lock<std::shared_mutex> lock(registry_mutex_);
return get_by_hash_impl_const<T>(cf::hash::fnv1a64(name), std::string(name));
}
} // namespace cf::ui::core
Updated on 2026-03-09 at 10:14:01 +0000