31template <CellLike... Ts>
42template <CellLike... Ts>
56 [[nodiscard]]
static constexpr std::array<std::string_view,
sizeof...(Ts)>
alternatives() {
57 return {Ts::kind_id...};
61 return descriptor::CellCategory::Union;
64 [[nodiscard]] std::vector<std::string_view>
sub_kinds()
const override {
65 return {Ts::kind_id...};
74 json_t arr = json_t::array();
75 ((arr.push_back(Ts::kind_id)), ...);
76 j[
"alternatives"] = std::move(arr);
99template <CellLike... Ts>
103 static_assert(
sizeof...(Ts) > 0,
"UnionCell requires at least one alternative");
106 using base_t::base_t;
107 using base_t::operator=;
113 template <std::
size_t I>
120 static constexpr std::array<std::string_view,
sizeof...(Ts)>
alternatives{Ts::kind_id...};
127 static constexpr auto kind_id_buf = [] {
128 constexpr std::array<std::string_view,
sizeof...(Ts)> alts{Ts::kind_id...};
129 constexpr std::size_t total = [&] {
130 std::size_t n = 2 + (
sizeof...(Ts) - 1);
131 for (
auto sv : alts) {
136 std::array<char, total> buf{};
140 for (std::size_t i = 0; i < alts.size(); ++i) {
144 for (
char c : alts[i]) {
159 static constexpr std::string_view
kind_id{kind_id_buf.data(), kind_id_buf.size()};
164 [[nodiscard]] std::size_t
index() const noexcept {
165 return this->
value.index();
173 template <std::
size_t I>
174 [[nodiscard]]
auto&
get() {
175 return std::get<I>(this->
value);
183 template <std::
size_t I>
184 [[nodiscard]]
auto const&
get()
const {
185 return std::get<I>(this->
value);
193 template <
typename S>
195 return std::get<S>(this->
value);
203 template <
typename S>
204 [[nodiscard]] S
const&
get()
const {
205 return std::get<S>(this->
value);
214 return visit_indexed([]<std::size_t I>(std::integral_constant<std::size_t, I>,
215 auto const& storage) -> std::string {
221 json_t j = visit_indexed([]<std::size_t I>(std::integral_constant<std::size_t, I>,
222 auto const& storage) ->
json_t {
224 WrapperT wrapper(storage);
235 auto out = std::make_shared<
UnionCell<Ts...>>();
237 [&out]<std::size_t I>(std::integral_constant<std::size_t, I>,
auto const& storage) {
239 const cell_t cloned = WrapperT(storage).clone();
240 auto* typed =
dynamic_cast<WrapperT*
>(cloned.get());
241 if (typed ==
nullptr) {
242 throw std::runtime_error(
"UnionCell::clone: alternative type mismatch");
244 out->value.template emplace<I>(std::move(typed->value));
258 [[nodiscard]] std::partial_ordering
compare(
ICell const& other)
const override {
259 if (
const auto k_cmp = this->
kind() <=> other.
kind(); k_cmp != 0) {
262 const auto* o =
dynamic_cast<UnionCell const*
>(&other);
264 return std::partial_ordering::unordered;
266 if (
const auto idx_cmp = this->
value.index() <=> o->value.index(); idx_cmp != 0) {
269 return compare_active(*o, std::index_sequence_for<Ts...>{});
285 if (!j.is_object()) {
289 if (it_k == j.end() || !it_k->is_string()) {
294 if (it_k->get<std::string_view>() !=
kind_id) {
300 if (it_v == j.end() || !it_v->is_object()) {
305 if (it_inner_k == it_v->end() || !it_inner_k->is_string()) {
309 const auto active = it_inner_k->get<std::string_view>();
310 cell_t out = dispatch_from_json(active, *it_v, reg, std::index_sequence_for<Ts...>{});
322 .description =
"Closed-set polymorphic cell (one of fixed alternatives)",
342 template <
typename F>
344 return std::visit(std::forward<F>(f), this->
value);
348 template <
typename F>
350 return std::visit(std::forward<F>(f), this->
value);
357 template <std::
size_t I>
359 return std::get_if<I>(&this->
value);
363 template <std::
size_t I>
364 [[nodiscard]]
auto const*
get_if() const noexcept {
365 return std::get_if<I>(&this->
value);
372 template <
typename S>
374 return std::get_if<S>(&this->
value);
378 template <
typename S>
379 [[nodiscard]] S
const*
get_if() const noexcept {
380 return std::get_if<S>(&this->
value);
384 template <std::
size_t I>
386 return this->
value.index() == I;
390 template <
typename S>
391 [[nodiscard]]
bool holds() const noexcept {
392 return std::holds_alternative<S>(this->
value);
396 template <
typename F>
397 auto visit_indexed(F&& f)
const {
398 return visit_indexed_impl(std::forward<F>(f), std::index_sequence_for<Ts...>{});
401 template <
typename F, std::size_t... Is>
402 auto visit_indexed_impl(F&& f, std::index_sequence<Is...>)
const {
403 using R = std::invoke_result_t<F&,
404 std::integral_constant<std::size_t, 0>,
405 typename nth_wrapper_t<0>::storage_t
const&>;
406 if constexpr (std::is_void_v<R>) {
407 (void)((this->
value.index() == Is
408 ? (f(std::integral_constant<std::size_t, Is>{}, std::get<Is>(this->
value)),
415 ((this->
value.index() == Is ? (result = f(std::integral_constant<std::size_t, Is>{},
416 std::get<Is>(this->
value)),
421 throw std::runtime_error(
"UnionCell: valueless variant");
427 template <std::size_t... Is>
428 [[nodiscard]] std::partial_ordering compare_active(UnionCell
const& other,
429 std::index_sequence<Is...>)
const {
430 std::partial_ordering result = std::partial_ordering::equivalent;
431 const auto idx = this->
value.index();
432 const bool matched = ((idx == Is ? (result = compare_alt<Is>(other),
true) : false) || ...);
437 template <std::
size_t I>
438 [[nodiscard]] std::partial_ordering compare_alt(UnionCell
const& other)
const {
439 using WrapperT = nth_wrapper_t<I>;
440 WrapperT a(std::get<I>(this->
value));
441 WrapperT b(std::get<I>(other.value));
445 template <std::size_t... Is>
446 static cell_t dispatch_from_json(
const std::string_view active,
448 ParcelRegistry
const& reg,
449 std::index_sequence<Is...>) {
451 if (
const bool matched = ((try_alt<Is>(active, raw, reg, out)) || ...); !matched) {
452 throw KindMismatchException(
"UnionCell: active kind '" + std::string(active) +
453 "' is not one of the alternatives",
459 template <std::
size_t I>
461 try_alt(std::string_view active, json_t
const& raw, ParcelRegistry
const& reg, cell_t& out) {
462 using NthT = nth_wrapper_t<I>;
463 if (active != NthT::kind_id) {
466 const cell_t v = NthT::from_json(raw, reg);
467 auto* typed =
dynamic_cast<NthT*
>(v.get());
468 if (typed ==
nullptr) {
469 throw KindMismatchException(
470 "UnionCell: alternative from_json did not return expected type",
473 auto u = std::make_shared<UnionCell<Ts...>>();
474 u->value.template emplace<I>(std::move(typed->value));
486template <
typename F, CellLike... Ts>
488 return u.
visit(std::forward<F>(f));
492template <
typename F, CellLike... Ts>
494 return u.
visit(std::forward<F>(f));
500template <std::size_t I, CellLike... Ts>
502 return u.template get<I>();
505template <std::size_t I, CellLike... Ts>
506auto const& get(UnionCell<Ts...>
const& u) {
507 return u.template get<I>();
513template <
typename S, CellLike... Ts>
515 return u.template get<S>();
518template <
typename S, CellLike... Ts>
519S
const& get(UnionCell<Ts...>
const& u) {
520 return u.template get<S>();
526template <std::size_t I, CellLike... Ts>
528 return u ? u->template get_if<I>() :
nullptr;
531template <std::size_t I, CellLike... Ts>
532auto const* get_if(UnionCell<Ts...>
const* u)
noexcept {
533 return u ? u->template get_if<I>() : nullptr;
536template <
typename S, CellLike... Ts>
537S*
get_if(UnionCell<Ts...>* u)
noexcept {
538 return u ? u->template get_if<S>() : nullptr;
541template <
typename S, CellLike... Ts>
542S
const*
get_if(UnionCell<Ts...>
const* u)
noexcept {
543 return u ? u->template get_if<S>() : nullptr;
560template <
class... Fs>
562 using Fs::operator()...;
565template <
class... Fs>
Core ICell interface, cell_t handle, BaseCell CRTP base, and CellLike concept.
std::shared_ptr< ICellTypeDescriptor > cell_type_descriptor_t
Shared handle to a runtime cell-type descriptor.
Definition cell.h:63
std::shared_ptr< ICell > cell_t
Shared handle to any ICell-derived value — the canonical cell pointer.
Definition cell.h:68
CRTP base that fills in the boilerplate of every cell-type descriptor.
Definition descriptor.h:165
json_t base_to_json() const
Common JSON skeleton (kind, display_info, category) reused by overrides.
Definition descriptor.h:202
JSON shape was wrong (missing/non-string k, missing v, etc.).
Definition error.h:158
k field was present but did not match the expected kind.
Definition error.h:168
Runtime catalog of cell-type descriptors, keyed by wire kind id.
Definition registry.h:115
Descriptor for UnionCell<Ts...>.
Definition union.h:44
json_t to_json() const override
Serialize the descriptor itself (kind + display info + category + extras).
Definition union.h:72
cell_t cell_from_json(json_t const &j, ParcelRegistry const ®) const override
Construct a cell from JSON, dispatching to the concrete from_json.
Definition union.h:68
std::vector< std::string_view > sub_kinds() const override
Sub-kind ids reachable from this descriptor.
Definition union.h:64
UnionCellTypeDescriptor(DisplayInfo info)
Construct with the given display info.
Definition union.h:53
descriptor::CellCategory category() const override
Coarse classification (primitive, list, struct, …).
Definition union.h:60
static constexpr std::array< std::string_view, sizeof...(Ts)> alternatives()
Compile-time array of every alternative's kind id.
Definition union.h:56
Closed-set polymorphic cell — exactly one of Ts at runtime.
Definition union.h:100
static constexpr std::array< std::string_view, sizeof...(Ts)> alternatives
Kind id of each alternative in template order.
Definition union.h:120
auto & get()
Access the value at alternative I (mutable).
Definition union.h:174
S const * get_if() const noexcept
Const overload of get_if<S>.
Definition union.h:379
auto const * get_if() const noexcept
Const overload of get_if<I>.
Definition union.h:364
std::variant< typename Ts::storage_t... > variant_storage_t
Storage variant — one slot per alternative.
Definition union.h:110
S * get_if() noexcept
Returns a pointer to the active alternative if its storage is S, else nullptr — never throws.
Definition union.h:373
std::string_view active_kind() const
Kind id of the currently active alternative.
Definition union.h:209
std::partial_ordering compare(ICell const &other) const override
Three-way comparison: kind, then active alternative index, then the active alternative's wrapper-leve...
Definition union.h:258
bool holds() const noexcept
true if the active alternative's storage type is S.
Definition union.h:391
auto * get_if() noexcept
Returns a pointer to the active alternative if it lives at slot I, else nullptr — never throws.
Definition union.h:358
static cell_t from_json(json_t const &j, ParcelRegistry const ®)
Deserialize a UnionCell<Ts...> from JSON.
Definition union.h:284
decltype(auto) visit(F &&f) const
Const overload of visit.
Definition union.h:349
static constexpr std::size_t alternative_count
Number of alternatives.
Definition union.h:117
static cell_type_descriptor_t descriptor()
Cached descriptor for this union.
Definition union.h:319
std::string to_string() const override
Render the cell's value as a compact human-readable string.
Definition union.h:213
bool holds_alternative() const noexcept
true if the active alternative lives at slot I.
Definition union.h:385
std::tuple_element_t< I, std::tuple< Ts... > > nth_wrapper_t
Wrapper type at the I -th alternative slot.
Definition union.h:114
std::size_t index() const noexcept
Index of the currently active alternative.
Definition union.h:164
json_t to_json() const override
Default JSON serialization for cells with JSON-convertible storage.
Definition union.h:220
S const & get() const
Access the value held in storage type S (const).
Definition union.h:204
S & get()
Access the value held in storage type S (mutable).
Definition union.h:194
auto const & get() const
Access the value at alternative I (const).
Definition union.h:184
cell_t clone() const override
Deep-copy this cell.
Definition union.h:234
static constexpr std::string_view kind_id
Wire kind id of this union ("u:" + alternatives joined by ",").
Definition union.h:159
decltype(auto) visit(F &&f)
Visit the active alternative with f, called as f(storage).
Definition union.h:343
comms::DisplayInfo DisplayInfo
Display info attached to a cell or descriptor — re-exported from comms::DisplayInfo.
Definition common.h:75
Runtime cell-type descriptors and the schema-graph mix-ins (IHasFields, ISubTypes).
CellCategory
Coarse runtime classification of a cell type.
Definition descriptor.h:35
nlohmann::json json_t
Project-wide alias for nlohmann::json.
Definition json.h:19
CRTP base providing default to_json / clone / kind plumbing on top of a storage type.
Definition cell.h:343
std::optional< DisplayInfo > display_info_
Optional display info; omitted from JSON when empty.
Definition cell.h:507
std::string_view kind() const override
Definition cell.h:411
static void absorb_display_info(json_t const &j, Out &out)
Read "d" (if present) from a JSON object and assign it onto a cell.
Definition cell.h:499
std::variant< Ts::storage_t... > value
Held value of the cell.
Definition cell.h:348
void inject_display_info(json_t &j) const
Copy this cell's display info (if any) into the JSON object under "d".
Definition cell.h:486
Polymorphic root of every parcel cell.
Definition cell.h:84
static constexpr std::string_view KEY_VALUE
JSON key for the value payload ("v").
Definition cell.h:88
virtual std::string_view kind() const =0
Wire-stable kind identifier for this cell.
static constexpr std::string_view KEY_KIND
JSON key for the kind id ("k").
Definition cell.h:86
Mix-in declaring a descriptor refers to other registered cell kinds.
Definition descriptor.h:148
Lambda-overload set helper for parcel::visit.
Definition union.h:561
auto & get(UnionCell< Ts... > &u)
Free get<I> over UnionCell<Ts...> mirroring std::get<I>(variant).
Definition union.h:501
auto * get_if(UnionCell< Ts... > *u) noexcept
Free get_if<I> over UnionCell<Ts...>; returns nullptr on miss.
Definition union.h:527
decltype(auto) visit(F &&f, UnionCell< Ts... > &u)
std::visit-style free function over UnionCell<Ts...>.
Definition union.h:487