commons 0.1.5
Header-only C++23 library of common/shared types for the C++ libraries
Loading...
Searching...
No Matches
origin.hpp
Go to the documentation of this file.
1#pragma once
2
36
39
40#include <concepts>
41#include <format>
42#include <functional>
43#include <map>
44#include <memory>
45#include <ostream>
46#include <string>
47#include <string_view>
48#include <utility>
49
50namespace comms {
51
54class IOrigin {
55public:
56 virtual ~IOrigin() = default;
57
59 [[nodiscard]] virtual std::string_view kind() const noexcept = 0;
60
62 [[nodiscard]] virtual std::unique_ptr<IOrigin> clone() const = 0;
63
66 [[nodiscard]] virtual const DisplayInfo& info() const = 0;
67
68protected:
69 // Protected to prevent slicing through the interface; derived types remain
70 // copyable/movable via their own (implicitly defined) operations.
71 IOrigin() = default;
72 IOrigin(const IOrigin&) = default;
73 IOrigin(IOrigin&&) = default;
74 IOrigin& operator=(const IOrigin&) = default;
75 IOrigin& operator=(IOrigin&&) = default;
76};
77
79using OriginPtr = std::unique_ptr<IOrigin>;
80
86template <FixedString Kind, typename Derived>
87class OriginKind : public IOrigin {
88public:
89 // KIND keeps the SCREAMING_CASE spelling of the reference's discriminator
90 // constant rather than the project's lower_case style — it reads as the
91 // type's identity tag and is the recognized convention for it.
92 // NOLINTNEXTLINE(readability-identifier-naming)
93 static constexpr std::string_view KIND = Kind.view();
94
95 [[nodiscard]] std::string_view kind() const noexcept override {
96 return KIND;
97 }
98
99 [[nodiscard]] std::unique_ptr<IOrigin> clone() const override {
100 return std::make_unique<Derived>(static_cast<const Derived&>(*this));
101 }
102
103 [[nodiscard]] const DisplayInfo& info() const override {
104 return Derived::display_info();
105 }
106};
107
108// -- built-in kinds ----------------------------------------------------------
109
111class CoreOrigin final : public OriginKind<"core", CoreOrigin> {
112public:
113 [[nodiscard]] static const DisplayInfo& display_info() {
114 static const DisplayInfo info{
115 .name = "Core",
116 .description = "Registered by the host core itself.",
117 .icon = Icon::from("mdi:home"),
118 };
119 return info;
120 }
121};
122
124class InternalOrigin final : public OriginKind<"internal", InternalOrigin> {
125public:
126 [[nodiscard]] static const DisplayInfo& display_info() {
127 static const DisplayInfo info{
128 .name = "Internal",
129 .description = "Registered by an internal subsystem.",
130 .icon = Icon::from("mdi:cog"),
131 };
132 return info;
133 }
134};
135
137class ExternalOrigin final : public OriginKind<"external", ExternalOrigin> {
138public:
139 std::string source;
140
141 ExternalOrigin() = default;
142 explicit ExternalOrigin(std::string src) : source(std::move(src)) {}
143
144 [[nodiscard]] static const DisplayInfo& display_info() {
145 static const DisplayInfo info{
146 .name = "External",
147 .description = "Registered by an external source.",
148 .icon = Icon::from("mdi:web"),
149 };
150 return info;
151 }
152};
153
155class UnknownOrigin final : public OriginKind<"unknown", UnknownOrigin> {
156public:
157 [[nodiscard]] static const DisplayInfo& display_info() {
158 static const DisplayInfo info{
159 .name = "Unknown",
160 .description = "Provenance is unknown.",
161 .icon = Icon::from("mdi:help-circle"),
162 };
163 return info;
164 }
165};
166
167// -- registry ----------------------------------------------------------------
168
174public:
176 using Factory = OriginPtr (*)();
177
178private:
179 std::map<std::string, Factory, std::less<>> factories_;
180 GlobalOriginRegistry() = default;
181
182public:
184 GlobalOriginRegistry& operator=(const GlobalOriginRegistry&) = delete;
186 GlobalOriginRegistry& operator=(GlobalOriginRegistry&&) = delete;
187 ~GlobalOriginRegistry() = default;
188
189 [[nodiscard]] static GlobalOriginRegistry& instance() noexcept {
190 static GlobalOriginRegistry r;
191 return r;
192 }
193
196 bool register_kind(const std::string_view kind, Factory factory) {
197 return factories_.emplace(std::string{kind}, factory).second;
198 }
199
201 [[nodiscard]] OriginPtr create(const std::string_view kind) const {
202 if (const auto it = factories_.find(kind); it != factories_.end()) {
203 return it->second();
204 }
205 return nullptr;
206 }
207
209 [[nodiscard]] bool contains(const std::string_view kind) const {
210 return factories_.contains(kind);
211 }
212};
213
218template <typename T>
220 OriginRegistrar() noexcept {
221 GlobalOriginRegistry::instance().register_kind(
222 T::KIND, []() -> OriginPtr { return std::make_unique<T>(); });
223 }
224};
225
226} // namespace comms
227
230// NOLINTNEXTLINE(bugprone-macro-parentheses)
231#define COMMONS_REGISTER_ORIGIN(Ident) \
232 inline const ::comms::OriginRegistrar<Ident> commons_origin_registrar_##Ident {}
233
234namespace comms {
235
236// The built-in kinds self-register so the JSON `from_json` dispatch resolves them.
237COMMONS_REGISTER_ORIGIN(CoreOrigin);
238COMMONS_REGISTER_ORIGIN(InternalOrigin);
239COMMONS_REGISTER_ORIGIN(ExternalOrigin);
240COMMONS_REGISTER_ORIGIN(UnknownOrigin);
241
242// ---------------------------------------------------------------------------
243// Text output: to_string + std::ostream insertion. (std::format support is the
244// std::formatter specialization below, outside namespace comms.) Both accept any
245// origin by upcast to the IOrigin base.
246// ---------------------------------------------------------------------------
247
249[[nodiscard]] inline std::string to_string(const IOrigin& o) {
250 return std::string{o.kind()};
251}
252
253inline std::ostream& operator<<(std::ostream& os, const IOrigin& o) {
254 return os << o.kind();
255}
256
257} // namespace comms
258
259// ---------------------------------------------------------------------------
260// std::format support. A partial specialization constrained to IOrigin-derived
261// types so any concrete origin (or an IOrigin reference) formats, mirroring the
262// adl_serializer route used in json.hpp.
263// ---------------------------------------------------------------------------
264
265// This spec-less formatter reads no member state, but `std::formatter` requires
266// `parse`/`format` to be non-static members — so silence the convert-to-static
267// suggestion here.
268// NOLINTBEGIN(readability-convert-member-functions-to-static)
269
271template <typename T>
272 requires std::derived_from<T, comms::IOrigin>
273struct std::formatter<T> {
274 constexpr auto parse(const std::format_parse_context& ctx) {
275 const auto* it = ctx.begin();
276 if (it != ctx.end() && *it != '}') {
277 throw std::format_error("commons: IOrigin takes no format spec");
278 }
279 return it;
280 }
281
282 auto format(const comms::IOrigin& o, std::format_context& ctx) const {
283 return std::format_to(ctx.out(), "{}", comms::to_string(o));
284 }
285}; // namespace std
286
287// NOLINTEND(readability-convert-member-functions-to-static)
The definition was registered by the host core itself.
Definition origin.hpp:111
The definition came from an external source, named by source.
Definition origin.hpp:137
std::string source
Free-form identifier of the external source.
Definition origin.hpp:139
A program-wide registry mapping an origin kind string to a factory.
Definition origin.hpp:173
OriginPtr create(const std::string_view kind) const
Create a fresh origin for kind, or nullptr if the kind is unknown.
Definition origin.hpp:201
bool register_kind(const std::string_view kind, Factory factory)
Register factory under kind.
Definition origin.hpp:196
bool contains(const std::string_view kind) const
Whether kind has a registered factory.
Definition origin.hpp:209
OriginPtr(*)() Factory
Produces a fresh, default-constructed origin of one kind.
Definition origin.hpp:176
Abstract provenance envelope.
Definition origin.hpp:54
virtual std::unique_ptr< IOrigin > clone() const =0
A deep, independent copy.
virtual std::string_view kind() const noexcept=0
The stable discriminator for this origin (e.g. "core").
virtual const DisplayInfo & info() const =0
Presentation metadata for this origin's kind (the description), sourced from the concrete type's stat...
The definition was registered by an internal subsystem.
Definition origin.hpp:124
CRTP base wiring kind(), clone(), and info() from a compile-time kind string and the concrete Derived...
Definition origin.hpp:87
std::unique_ptr< IOrigin > clone() const override
A deep, independent copy.
Definition origin.hpp:99
std::string_view kind() const noexcept override
The stable discriminator for this origin (e.g. "core").
Definition origin.hpp:95
const DisplayInfo & info() const override
Presentation metadata for this origin's kind (the description), sourced from the concrete type's stat...
Definition origin.hpp:103
Provenance is unknown.
Definition origin.hpp:155
std::string to_string(const Color &c)
Color as its canonical hex string (#RRGGBB, or #RRGGBBAA when not opaque).
Definition color.hpp:1388
Presentation metadata for a type/value — name, description, icon, color, all optional — plus the trai...
const DisplayInfo & display_info()
Retrieve the DisplayInfo for T via whichever mechanism is present.
Definition display_info.hpp:73
NTTP-friendly fixed-size string usable as a non-type template parameter.
#define COMMONS_REGISTER_ORIGIN(Ident)
Register an already-defined origin type Ident into the GlobalOriginRegistry.
Definition origin.hpp:231
std::unique_ptr< IOrigin > OriginPtr
A heap-owned origin. The canonical way to carry an IOrigin by value.
Definition origin.hpp:79
Presentation metadata for a type/value.
Definition display_info.hpp:43
std::optional< std::string > name
Human-readable label.
Definition display_info.hpp:44
Compile-time fixed-size string usable as a non-type template parameter.
Definition fixed_string.hpp:21
A self-registering object: constructing one registers T's factory into the GlobalOriginRegistry.
Definition origin.hpp:219