tagval 0.2.0
Modern C++23 header-only library of tagged values (open/closed enumerations with metadata)
Loading...
Searching...
No Matches
kind_registry.hpp
Go to the documentation of this file.
1#pragma once
2
21
23#include <tagval/descriptor.hpp>
24#include <tagval/entry.hpp>
25#include <tagval/open_ended.hpp>
26
27#include <commons/color.hpp>
28#include <commons/fixed_string.hpp>
29#include <commons/icon.hpp>
30
31#include <algorithm>
32#include <concepts>
33#include <functional>
34#include <memory>
35#include <optional>
36#include <ranges>
37#include <span>
38#include <string_view>
39#include <type_traits>
40#include <variant>
41#include <vector>
42
43namespace tagval {
44
45namespace detail {
46template <typename T>
47class KindViewFactory;
48} // namespace detail
49
51enum class KindCategory { Closed, Open };
52
58 std::string_view code;
59 std::string_view label;
60 std::optional<comms::Icon> icon;
61 std::optional<comms::Color> color;
62
63 [[nodiscard]] friend constexpr bool operator==(const KindEntryView&,
64 const KindEntryView&) noexcept = default;
65};
66
67namespace detail {
68
74template <comms::FixedString Id, typename T>
77}
78
79template <comms::FixedString Id, typename T>
81 return KindCategory::Open;
82}
83
84// Variadic ellipsis is intentional: it's the lowest-priority overload of
85// the probe set, picked only when neither ClosedEnded nor OpenEnded matches.
86// NOLINTNEXTLINE(cert-dcl50-cpp,modernize-avoid-variadic-functions)
87constexpr std::monostate probe_kind(...) noexcept {
88 return {};
89}
90
92template <typename T>
93concept TagValKind = !std::same_as<decltype(probe_kind(static_cast<T*>(nullptr))), std::monostate>;
94
97template <typename T>
98 requires TagValKind<T>
100 return probe_kind(static_cast<T*>(nullptr));
101}
102
103} // namespace detail
104
109class KindView {
110public:
111 using EmitFn = void (*)(void* user, const KindEntryView&);
112 using WalkFn = void (*)(void* user, EmitFn emit);
113 using FindFn = std::optional<KindEntryView> (*)(std::string_view code);
114
115 constexpr KindView() noexcept = default;
116
117 [[nodiscard]] constexpr TagValDescriptor descriptor() const noexcept {
118 return descriptor_;
119 }
120 [[nodiscard]] constexpr std::string_view kind_id() const noexcept {
121 return descriptor_.id;
122 }
123 [[nodiscard]] constexpr KindCategory category() const noexcept {
124 return category_;
125 }
126 [[nodiscard]] constexpr bool is_closed_ended() const noexcept {
127 return category_ == KindCategory::Closed;
128 }
129 [[nodiscard]] constexpr bool is_open_ended() const noexcept {
130 return category_ == KindCategory::Open;
131 }
132
136 [[nodiscard]] std::vector<KindEntryView> values() const {
137 std::vector<KindEntryView> out;
138 for_each([&](const KindEntryView& e) { out.push_back(e); });
139 return out;
140 }
141
144 template <std::invocable<const KindEntryView&> F>
145 void for_each(F&& f) const {
146 using FRef = std::remove_reference_t<F>;
147 auto trampoline =
148 +[](void* user, const KindEntryView& e) { std::invoke(*static_cast<FRef*>(user), e); };
149 // f is captured by reference via the void* user-data slot, so the
150 // user-supplied callable doesn't need to be copy-constructible.
151 walk_(static_cast<void*>(std::addressof(f)), trampoline);
152 }
153
156 [[nodiscard]] std::optional<KindEntryView> find(const std::string_view code) const {
157 return find_(code);
158 }
159
160 [[nodiscard]] friend constexpr bool operator==(const KindView& a, const KindView& b) noexcept {
161 return a.descriptor_.id == b.descriptor_.id;
162 }
163
164private:
165 template <typename T>
167
168 TagValDescriptor descriptor_{};
169 KindCategory category_{};
170 WalkFn walk_ = nullptr;
171 FindFn find_ = nullptr;
172};
173
174namespace detail {
175
178template <typename T>
180public:
181 [[nodiscard]] static KindView build() {
182 KindView v;
183 v.descriptor_ = T::descriptor();
184 v.category_ = category_of<T>();
185 v.walk_ = &walk;
186 v.find_ = &find;
187 return v;
188 }
189
190private:
191 static void walk(void* user, const KindView::EmitFn emit) {
192 for (const auto& m : T::all_values()) {
193 const KindEntryView kev{m.code, m.label, m.icon, m.color};
194 emit(user, kev);
195 }
196 }
197
198 static std::optional<KindEntryView> find(std::string_view code) {
199 auto exp = T::try_of(code);
200 if (!exp) {
201 return std::nullopt;
202 }
203 return KindEntryView{exp->code(), exp->label(), exp->icon(), exp->color()};
204 }
205};
206
207} // namespace detail
208
213public:
217 template <typename T>
218 requires detail::TagValKind<T>
219 static void register_kind() {
220 auto& v = storage();
221 const std::string_view id = T::kind_id();
222 if (std::ranges::any_of(v, [&](const KindView& kv) { return kv.kind_id() == id; })) {
223 return;
224 }
226 }
227
229 [[nodiscard]] static std::span<const KindView> all() noexcept {
230 return std::span<const KindView>{storage()};
231 }
232
237 [[nodiscard]] static auto all_closed() noexcept {
238 return all() | std::views::filter([](const KindView& kv) { return kv.is_closed_ended(); });
239 }
240
243 [[nodiscard]] static auto all_open() noexcept {
244 return all() | std::views::filter([](const KindView& kv) { return kv.is_open_ended(); });
245 }
246
249 [[nodiscard]] static const KindView* find(const std::string_view kind_id) noexcept {
250 const auto& v = storage();
251 const auto it =
252 std::ranges::find_if(v, [&](const KindView& kv) { return kv.kind_id() == kind_id; });
253 return it == v.end() ? nullptr : &*it;
254 }
255
256private:
257 [[nodiscard]] static std::vector<KindView>& storage() {
258 static std::vector<KindView> v;
259 return v;
260 }
261};
262
263} // namespace tagval
CRTP base for tag-value kinds whose values are fixed at compile time.
Definition closed_ended.hpp:37
Program-wide index of every kind that opted in via TAGVAL_REGISTER_KIND.
Definition kind_registry.hpp:212
static auto all_open() noexcept
Lazy filter view over the registered kinds that selects open-ended kinds only.
Definition kind_registry.hpp:243
static const KindView * find(const std::string_view kind_id) noexcept
Find a kind by id.
Definition kind_registry.hpp:249
static auto all_closed() noexcept
Lazy filter view over the registered kinds that selects closed-ended kinds only.
Definition kind_registry.hpp:237
static std::span< const KindView > all() noexcept
All registered kinds, in registration order.
Definition kind_registry.hpp:229
static void register_kind()
Register a kind.
Definition kind_registry.hpp:219
Type-erased handle to one registered kind.
Definition kind_registry.hpp:109
friend constexpr bool operator==(const KindView &a, const KindView &b) noexcept
Definition kind_registry.hpp:160
constexpr TagValDescriptor descriptor() const noexcept
Definition kind_registry.hpp:117
std::optional< KindEntryView >(*)(std::string_view code) FindFn
Definition kind_registry.hpp:113
constexpr KindCategory category() const noexcept
Definition kind_registry.hpp:123
std::optional< KindEntryView > find(const std::string_view code) const
Look up a value by code.
Definition kind_registry.hpp:156
constexpr bool is_closed_ended() const noexcept
Definition kind_registry.hpp:126
constexpr bool is_open_ended() const noexcept
Definition kind_registry.hpp:129
constexpr KindView() noexcept=default
void(*)(void *user, EmitFn emit) WalkFn
Definition kind_registry.hpp:112
constexpr std::string_view kind_id() const noexcept
Definition kind_registry.hpp:120
void for_each(F &&f) const
Zero-allocation walk over the kind's current values.
Definition kind_registry.hpp:145
std::vector< KindEntryView > values() const
Allocating snapshot of the kind's current values, in declaration order (open-ended: predefined entrie...
Definition kind_registry.hpp:136
void(*)(void *user, const KindEntryView &) EmitFn
Definition kind_registry.hpp:111
CRTP base for tag-value kinds whose values are partly compile-time (Derived::values_t) and partly con...
Definition open_ended.hpp:63
Builds a KindView for a single concrete kind type.
Definition kind_registry.hpp:179
static KindView build()
Definition kind_registry.hpp:181
ClosedEnded base — values come from a compile-time Values<...> list.
Concept: T is a tag-value kind (derives from ClosedEnded or OpenEnded).
Definition kind_registry.hpp:93
Public kind-level metadata struct.
Compile-time Entry NTTP and the runtime-view TagValMetadata struct.
constexpr KindCategory category_of() noexcept
Category of a tag-value kind.
Definition kind_registry.hpp:99
constexpr KindCategory probe_kind(ClosedEnded< Id, T > *) noexcept
Overload set used to detect whether T is a tag-value kind and, if so, which CRTP base it derives from...
Definition kind_registry.hpp:75
constexpr TagValDescriptor compute_descriptor() noexcept
Definition base.hpp:38
Definition base.hpp:26
KindCategory
Which CRTP base a kind derives from.
Definition kind_registry.hpp:51
OpenEnded base — same in-class declaration shape as ClosedEnded (TAGVAL_ENTRY + using values_t = tagv...
Flattened, copy-friendly view of a single entry.
Definition kind_registry.hpp:57
friend constexpr bool operator==(const KindEntryView &, const KindEntryView &) noexcept=default
std::optional< comms::Color > color
Definition kind_registry.hpp:61
std::optional< comms::Icon > icon
Definition kind_registry.hpp:60
std::string_view code
Definition kind_registry.hpp:58
std::string_view label
Definition kind_registry.hpp:59
Runtime metadata describing a tag-value kind (e.g.
Definition descriptor.hpp:19
std::optional< comms::Icon > icon
Optional UI icon.
Definition descriptor.hpp:23
std::string_view id
Stable identifier (e.g. "device_kind"). Required.
Definition descriptor.hpp:21
std::optional< comms::Color > color
Optional UI accent.
Definition descriptor.hpp:24