Zrythm v2.0.0-DEV
a highly automated and intuitive digital audio workstation
Loading...
Searching...
No Matches
uuid_identifiable_object.h
1// SPDX-FileCopyrightText: © 2024-2025 Alexandros Theodotou <alex@zrythm.org>
2// SPDX-License-Identifier: LicenseRef-ZrythmLicense
3
4#pragma once
5
6#include <utility>
7
8#include "utils/format.h"
9#include "utils/icloneable.h"
10#include "utils/logger.h"
11#include "utils/rt_thread_id.h"
12#include "utils/serialization.h"
13#include "utils/utf8_string.h"
14
15#include <QUuid>
16
17#include "type_safe/strong_typedef.hpp"
18#include <boost/stl_interfaces/iterator_interface.hpp>
19#include <boost/unordered/unordered_flat_map.hpp>
20#include <nlohmann/json_fwd.hpp>
21
22namespace zrythm::utils
23{
27template <typename Derived> class UuidIdentifiableObject
28{
29public:
30 struct Uuid final
31 : type_safe::strong_typedef<Uuid, QUuid>,
32 type_safe::strong_typedef_op::equality_comparison<Uuid>,
33 type_safe::strong_typedef_op::relational_comparison<Uuid>
34 {
35 using UuidTag = void; // used by the fmt formatter below
36 using type_safe::strong_typedef<Uuid, QUuid>::strong_typedef;
37
38 explicit Uuid () = default;
39
40 static_assert (StrongTypedef<Uuid>);
41 // static_assert (type_safe::is_strong_typedef<Uuid>::value);
42 // static_assert (std::is_same_v<type_safe::underlying_type<Uuid>, QUuid>);
43
47 bool is_null () const { return type_safe::get (*this).isNull (); }
48
49 void set_null () { type_safe::get (*this) = QUuid (); }
50
51 std::size_t hash () const { return qHash (type_safe::get (*this)); }
52 };
53 static_assert (std::regular<Uuid>);
54 static_assert (sizeof (Uuid) == sizeof (QUuid));
55
56 UuidIdentifiableObject () : uuid_ (Uuid (QUuid::createUuid ())) { }
57 UuidIdentifiableObject (const Uuid &id) : uuid_ (id) { }
58 UuidIdentifiableObject (const UuidIdentifiableObject &other) = default;
59 UuidIdentifiableObject &
60 operator= (const UuidIdentifiableObject &other) = default;
61 UuidIdentifiableObject (UuidIdentifiableObject &&other) = default;
62 UuidIdentifiableObject &operator= (UuidIdentifiableObject &&other) = default;
63 virtual ~UuidIdentifiableObject () = default;
64
65 auto get_uuid () const { return uuid_; }
66
67 friend void init_from (
68 UuidIdentifiableObject &obj,
69 const UuidIdentifiableObject &other,
70 utils::ObjectCloneType clone_type)
71 {
73 {
74 obj.uuid_ = Uuid (QUuid::createUuid ());
75 }
76 else
77 {
78 obj.uuid_ = other.uuid_;
79 }
80 }
81
82 friend void to_json (nlohmann::json &j, const UuidIdentifiableObject &obj)
83 {
84 j[kUuidKey] = obj.uuid_;
85 }
86 friend void from_json (const nlohmann::json &j, UuidIdentifiableObject &obj)
87 {
88 j.at (kUuidKey).get_to (obj.uuid_);
89 }
90
91 friend bool operator== (
92 const UuidIdentifiableObject &lhs,
93 const UuidIdentifiableObject &rhs)
94 {
95 return lhs.uuid_ == rhs.uuid_;
96 }
97
98private:
99 static constexpr std::string_view kUuidKey = "id";
100
101 Uuid uuid_;
102
103 BOOST_DESCRIBE_CLASS (UuidIdentifiableObject, (), (), (), (uuid_))
104};
105
106template <typename T>
107concept UuidIdentifiable = std::derived_from<T, UuidIdentifiableObject<T>>;
108
109template <typename T>
111 UuidIdentifiable<T> && std::derived_from<T, QObject>;
112
113// specialization for std::hash and boost::hash
114#define DEFINE_UUID_HASH_SPECIALIZATION(UuidType) \
115 namespace std \
116 { \
117 template <> struct hash<UuidType> \
118 { \
119 size_t operator() (const UuidType &uuid) const { return uuid.hash (); } \
120 }; \
121 } \
122 namespace boost \
123 { \
124 template <> struct hash<UuidType> \
125 { \
126 size_t operator() (const UuidType &uuid) const { return uuid.hash (); } \
127 }; \
128 }
129
137template <typename RegistryT> class UuidReference
138{
139public:
140 using UuidType = typename RegistryT::UuidType;
141 using VariantType = typename RegistryT::VariantType;
142
143 static constexpr auto kIdKey = "id"sv;
144
145 // unengaged - need to call set_id to acquire a reference
146 UuidReference (RegistryT &registry) : registry_ (registry) { }
147
148 UuidReference (const UuidType &id, RegistryT &registry)
149 : id_ (id), registry_ (registry)
150 {
151 acquire_ref ();
152 }
153
154 UuidReference (const UuidReference &other)
155 : UuidReference (other.get_registry ())
156 {
157 if (other.id_.has_value ())
158 {
159 set_id (other.id_.value ());
160 }
161 }
162 UuidReference &operator= (const UuidReference &other)
163 {
164 if (this != &other)
165 {
166 id_ = other.id_;
167 registry_ = other.registry_;
168 acquire_ref ();
169 }
170 return *this;
171 }
172
173 UuidReference (UuidReference &&other)
174 : id_ (std::exchange (other.id_, std::nullopt)),
175 registry_ (std::exchange (other.registry_, std::nullopt))
176 {
177 // No need to acquire_ref() here because we're taking over the reference
178 // from 'other' which already holds it
179 }
180
181 UuidReference &operator= (UuidReference &&other)
182 {
183 if (this != &other)
184 {
185 // Release our current reference (if any)
186 release_ref ();
187
188 // Take ownership from other
189 id_ = std::exchange (other.id_, std::nullopt);
190 registry_ = std::exchange (other.registry_, std::nullopt);
191
192 // No need to acquire_ref() - we're taking over the reference
193 }
194 return *this;
195 }
196
197 ~UuidReference () { release_ref (); }
198
199 auto id () const -> UuidType { return *id_; }
200
208 void set_id (const UuidType &id)
209 {
210 if (id_.has_value ())
211 {
212 throw std::runtime_error (
213 "Cannot set id of UuidReference that already has an id");
214 }
215 id_ = id;
216 acquire_ref ();
217 }
218
219 VariantType get_object () const
220 {
221 return get_registry ().find_by_id_or_throw (id ());
222 }
223
224 // Convenience getter
225 template <typename ObjectT>
226 ObjectT * get_object_as () const
227 requires IsInVariant<ObjectT *, VariantType>
228 {
229 return std::get<ObjectT *> (get_object ());
230 }
231
232 RegistryT::BaseType * get_object_base () const
233 {
234 return get_registry ().find_by_id_as_base_or_throw (id ());
235 }
236
237 template <typename... Args>
238 UuidReference clone_new_identity (Args &&... args) const
239 {
240 return std::visit (
241 [&] (auto &&obj) {
242 return get_registry ().clone_object (*obj, std::forward<Args> (args)...);
243 },
244 get_object ());
245 }
246
247 auto get_iterator_in_registry () const
248 {
249 return get_registry ().get_iterator_for_id (id ());
250 }
251
252 auto get_iterator_in_registry ()
253 {
254 return get_registry ().get_iterator_for_id (id ());
255 }
256
257private:
258 void acquire_ref ()
259 {
260 if (id_.has_value ())
261 {
262 get_registry ().acquire_reference (*id_);
263 }
264 }
265 void release_ref ()
266 {
267 if (id_.has_value ())
268 {
269 get_registry ().release_reference (*id_);
270 }
271 }
272 RegistryT &get_registry () const
273 {
274 return const_cast<RegistryT &> (*registry_);
275 }
276 RegistryT &get_registry () { return *registry_; }
277
278 friend void to_json (nlohmann::json &j, const UuidReference &ref)
279 {
280 assert (ref.id_.has_value ());
281 j[kIdKey] = *ref.id_;
282 }
283 friend void from_json (const nlohmann::json &j, UuidReference &ref)
284 {
285 auto tmp = ref;
286 j.at (kIdKey).get_to (ref.id_);
287 ref.acquire_ref ();
288 tmp.release_ref ();
289 }
290
291 friend bool operator== (const UuidReference &lhs, const UuidReference &rhs)
292 {
293 return lhs.id () == rhs.id ();
294 }
295
296private:
297 std::optional<UuidType> id_;
298 OptionalRef<RegistryT> registry_;
299
300 BOOST_DESCRIBE_CLASS (UuidReference<RegistryT>, (), (), (), (id_))
301};
302
303template <typename ReturnType, typename UuidType>
304using UuidIdentifiablObjectResolver =
305 std::function<ReturnType (const UuidType &)>;
306
324template <typename VariantT, UuidIdentifiable BaseT>
325class OwningObjectRegistry : public QObject
326{
327public:
329 using VariantType = VariantT;
330 using BaseType = BaseT;
331
332 OwningObjectRegistry (QObject * parent = nullptr)
333 : QObject (parent), main_thread_id_ (current_thread_id)
334 {
335 }
336
337 // ========================================================================
338 // QML/QObject Interface
339 // ========================================================================
340
341 // TODO signals...
342
343 // ========================================================================
344
345 // Factory method that forwards constructor arguments
346 template <typename CreateType, typename... Args>
347 auto create_object (Args &&... args) -> UuidReference<OwningObjectRegistry>
348 requires std::derived_from<CreateType, BaseT>
349 {
350 assert (is_main_thread ());
351
352 auto * obj = new CreateType (std::forward<Args> (args)...);
353 z_trace (
354 "created object of type {} with ID {}", typeid (CreateType).name (),
355 obj->get_uuid ());
356 register_object (obj);
357 return { obj->get_uuid (), *this };
358 }
359
360 // Creates a clone of the given object and registers it to the registry.
361 template <typename CreateType, typename... Args>
362 auto clone_object (const CreateType &other, Args &&... args)
364 requires std::derived_from<CreateType, BaseT>
365 {
366 assert (is_main_thread ());
367
368 CreateType * obj = clone_raw_ptr (
369 other, ObjectCloneType::NewIdentity, std::forward<Args> (args)...);
370 register_object (obj);
371 return { obj->get_uuid (), *this };
372 }
373
374 [[gnu::hot]] auto get_iterator_for_id (const UuidType &id) const
375 {
376 // TODO: some failures left to fix
377 // assert (is_main_thread ());
378
379 return objects_by_id_.find (id);
380 }
381
382 [[gnu::hot]] auto get_iterator_for_id (const UuidType &id)
383 {
384 // assert (is_main_thread ());
385
386 return objects_by_id_.find (id);
387 }
388
394 [[gnu::hot]] std::optional<VariantT>
395 find_by_id (const UuidType &id) const noexcept [[clang::blocking]]
396 {
397 const auto it = get_iterator_for_id (id);
398 if (it == objects_by_id_.end ())
399 {
400 return std::nullopt;
401 }
402 return it->second;
403 }
404
405 [[gnu::hot]] auto find_by_id_or_throw (const UuidType &id) const
406 {
407 auto val = find_by_id (id);
408 if (!val.has_value ())
409 {
410 throw std::runtime_error (
411 fmt::format ("Object with id {} not found", id));
412 }
413 return val.value ();
414 }
415
416 BaseT * find_by_id_as_base_or_throw (const UuidType &id) const
417 {
418 auto var = find_by_id_or_throw (id);
419 return std::visit ([] (auto &&val) -> BaseT * { return val; }, var);
420 }
421
422 bool contains (const UuidType &id) const
423 {
424 // assert (is_main_thread ());
425
426 return objects_by_id_.contains (id);
427 }
428
434 void register_object (VariantT obj_ptr)
435 {
436 assert (is_main_thread ());
437
438 std::visit (
439 [&] (auto &&obj) {
440 if (contains (obj->get_uuid ()))
441 {
442 throw std::runtime_error (
443 fmt::format ("Object with id {} already exists", obj->get_uuid ()));
444 }
445 z_trace ("Registering (inserting) object {}", obj->get_uuid ());
446 obj->setParent (this);
447 objects_by_id_.emplace (obj->get_uuid (), obj);
448 },
449 obj_ptr);
450 }
451
452 template <typename ObjectT>
453 void register_object (ObjectT &obj)
454 requires std::derived_from<ObjectT, BaseT>
455 {
456 register_object (&obj);
457 }
458
464 auto reference_count (const UuidType &id) const
465 {
466 assert (is_main_thread ());
467
468 return ref_counts_.at (id);
469 }
470
471 void acquire_reference (const UuidType &id)
472 {
473 assert (is_main_thread ());
474
475 ref_counts_[id]++;
476 }
477
478 void release_reference (const UuidType &id)
479 {
480 assert (is_main_thread ());
481
482 if (--ref_counts_[id] <= 0)
483 {
484 delete_object_by_id (id);
485 }
486 }
487
488 auto &get_hash_map () const { return objects_by_id_; }
489
493 auto get_uuids () const
494 {
495 assert (is_main_thread ());
496
497 return objects_by_id_ | std::views::keys | std::ranges::to<std::vector> ();
498 }
499
500 size_t size () const
501 {
502 assert (is_main_thread ());
503
504 return objects_by_id_.size ();
505 }
506
507 friend void to_json (nlohmann::json &j, const OwningObjectRegistry &obj)
508 {
509 auto objects = nlohmann::json::array ();
510 for (const auto &var : obj.objects_by_id_ | std::views::values)
511 {
512 objects.push_back (var);
513 }
514 j[kObjectsKey] = objects;
515 }
516 template <ObjectBuilder BuilderT>
517 friend void from_json_with_builder (
518 const nlohmann::json &j,
519 OwningObjectRegistry &obj,
520 const BuilderT &builder)
521 {
522 auto objects = j.at (kObjectsKey);
523 for (const auto &object_var_json : objects)
524 {
525 VariantT object_var;
526 utils::serialization::variant_from_json_with_builder (
527 object_var_json, object_var, builder);
528 obj.register_object (object_var);
529 }
530 }
531
532private:
533 static constexpr const char * kObjectsKey = "objectsById";
534
541 VariantT unregister_object (const UuidType &id)
542 {
543 if (!objects_by_id_.contains (id))
544 {
545 throw std::runtime_error (
546 fmt::format ("Object with id {} not found", id));
547 }
548
549 z_trace ("Unregistering object with id {}", id);
550
551 auto obj_it = get_iterator_for_id (id);
552 auto obj_var = obj_it->second;
553 objects_by_id_.erase (obj_it);
554 std::visit ([&] (auto &&obj) { obj->setParent (nullptr); }, obj_var);
555 ref_counts_.erase (id);
556 return obj_var;
557 }
558
559 void delete_object_by_id (const UuidType &id)
560 {
561 auto obj_var = unregister_object (id);
562 std::visit ([&] (auto &&obj) { delete obj; }, obj_var);
563 }
564
565 bool is_main_thread () const { return main_thread_id_ == current_thread_id; }
566
567private:
573 boost::unordered::unordered_flat_map<UuidType, VariantT> objects_by_id_;
574
580 boost::unordered::unordered_flat_map<UuidType, int> ref_counts_;
581
582 RTThreadId main_thread_id_;
583};
584
591template <typename RegistryT>
593 : public std::ranges::view_interface<UuidIdentifiableObjectView<RegistryT>>
594{
595public:
596 using UuidType = typename RegistryT::UuidType;
597 using VariantType = typename RegistryT::VariantType;
598 using UuidRefType = UuidReference<RegistryT>;
599
600 // Proxy iterator implementation using Boost.STLInterfaces
601 class Iterator
602 : public boost::stl_interfaces::proxy_iterator_interface<
603#if !BOOST_STL_INTERFACES_USE_DEDUCED_THIS
604 Iterator,
605#endif
606 std::random_access_iterator_tag,
607 VariantType>
608 {
609 public:
610 using base_type = boost::stl_interfaces::proxy_iterator_interface<
611#if !BOOST_STL_INTERFACES_USE_DEDUCED_THIS
612 Iterator,
613#endif
614 std::random_access_iterator_tag,
615 VariantType>;
616 using difference_type = base_type::difference_type;
617
618 constexpr Iterator () noexcept = default;
619
620 // Constructor for direct object range
621 constexpr Iterator (std::span<const VariantType>::iterator it) noexcept
622 : it_var_ (it)
623 {
624 }
625
626 // Constructor for UuidReference range
627 constexpr Iterator (std::span<const UuidRefType>::iterator it) noexcept
628 : it_var_ (it)
629 {
630 }
631
632 // Constructor for Uuid + Registry range
633 constexpr Iterator (
634 std::span<const UuidType>::iterator it,
635 const RegistryT * registry) noexcept
636 : it_var_ (std::pair{ it, registry })
637 {
638 }
639
640 constexpr VariantType operator* () const
641 {
642 return std::visit (
643 [] (auto &&arg) {
644 using T = std::decay_t<decltype (arg)>;
645 if constexpr (
646 std::is_same_v<T, typename std::span<const VariantType>::iterator>)
647 {
648 return *arg;
649 }
650 else if constexpr (
651 std::is_same_v<T, typename std::span<const UuidRefType>::iterator>)
652 {
653 return arg->get_object ();
654 }
655 else if constexpr (
656 std::is_same_v<
657 T,
658 std::pair<
659 typename std::span<const UuidType>::iterator, const RegistryT *>>)
660 {
661 return arg.second->find_by_id_or_throw (*arg.first);
662 }
663 },
664 it_var_);
665 }
666
667 constexpr Iterator &operator+= (difference_type n) noexcept
668 {
669 std::visit (
670 [n] (auto &&arg) {
671 using T = std::decay_t<decltype (arg)>;
672 if constexpr (
673 std::is_same_v<T, typename std::span<const VariantType>::iterator>)
674 {
675 arg += n;
676 }
677 else if constexpr (
678 std::is_same_v<T, typename std::span<const UuidRefType>::iterator>)
679 {
680 arg += n;
681 }
682 else if constexpr (
683 std::is_same_v<
684 T,
685 std::pair<
686 typename std::span<const UuidType>::iterator, const RegistryT *>>)
687 {
688 arg.first += n;
689 }
690 },
691 it_var_);
692 return *this;
693 }
694
695 constexpr difference_type operator- (Iterator other) const
696 {
697 return std::visit (
698 [] (auto &&arg, auto &&other_arg) -> difference_type {
699 using T = std::decay_t<decltype (arg)>;
700 using OtherT = std::decay_t<decltype (other_arg)>;
701 if constexpr (!std::is_same_v<T, OtherT>)
702 {
703 throw std::runtime_error (
704 "Comparing iterators of different types");
705 }
706 else if constexpr (
707 std::is_same_v<T, typename std::span<const VariantType>::iterator>)
708 {
709 return arg - other_arg;
710 }
711 else if constexpr (
712 std::is_same_v<T, typename std::span<const UuidRefType>::iterator>)
713 {
714 return arg - other_arg;
715 }
716 else if constexpr (
717 std::is_same_v<
718 T,
719 std::pair<
720 typename std::span<const UuidType>::iterator, const RegistryT *>>)
721 {
722 return arg.first - other_arg.first;
723 }
724 else
725 {
726 return 0;
727 }
728 },
729 it_var_, other.it_var_);
730 }
731
732 constexpr auto operator<=> (const Iterator &other) const
733 {
734 return std::visit (
735 [] (auto &&arg, auto &&other_arg) -> std::strong_ordering {
736 using T = std::decay_t<decltype (arg)>;
737 using OtherT = std::decay_t<decltype (other_arg)>;
738 if constexpr (!std::is_same_v<T, OtherT>)
739 {
740 throw std::runtime_error (
741 "Comparing iterators of different types");
742 }
743 else if constexpr (
744 std::is_same_v<T, typename std::span<const VariantType>::iterator>)
745 {
746 return arg <=> other_arg;
747 }
748 else if constexpr (
749 std::is_same_v<T, typename std::span<const UuidRefType>::iterator>)
750 {
751 return arg <=> other_arg;
752 }
753 else if constexpr (
754 std::is_same_v<
755 T,
756 std::pair<
757 typename std::span<const UuidType>::iterator, const RegistryT *>>)
758 {
759 return arg.first <=> other_arg.first;
760 }
761 else
762 {
763 return std::strong_ordering::equal;
764 }
765 },
766 it_var_, other.it_var_);
767 }
768
769 private:
770 std::variant<
771 typename std::span<const VariantType>::iterator,
772 typename std::span<const UuidRefType>::iterator,
773 std::pair<typename std::span<const UuidType>::iterator, const RegistryT *>>
774 it_var_;
775 };
776
778 explicit UuidIdentifiableObjectView (std::span<const VariantType> objects)
779 : objects_ (objects)
780 {
781 }
782
784 explicit UuidIdentifiableObjectView (std::span<const UuidRefType> refs)
785 : refs_ (refs)
786 {
787 }
788
791 const RegistryT &registry,
792 std::span<const UuidType> uuids)
793 : uuids_ (uuids), registry_ (&registry)
794 {
795 }
796
798 explicit UuidIdentifiableObjectView (const VariantType &obj)
799 : objects_ (std::span<const VariantType> (&obj, 1))
800 {
801 }
802
803 Iterator begin () const
804 {
805 if (objects_)
806 {
807 return Iterator (objects_->begin ());
808 }
809 else if (refs_)
810 {
811 return Iterator (refs_->begin ());
812 }
813 else
814 {
815 return Iterator (uuids_->begin (), registry_);
816 }
817 }
818
819 Iterator end () const
820 {
821 if (objects_)
822 {
823 return Iterator (objects_->end ());
824 }
825 else if (refs_)
826 {
827 return Iterator (refs_->end ());
828 }
829 else
830 {
831 return Iterator (uuids_->end (), registry_);
832 }
833 }
834
835 VariantType operator[] (size_t index) const
836 {
837 if (objects_)
838 return (*objects_)[index];
839 if (refs_)
840 return (*refs_)[index].get_object ();
841 return registry_->find_by_id_or_throw ((*uuids_)[index]);
842 }
843
844 VariantType front () const { return *begin (); }
845 VariantType back () const
846 {
847 if (empty ())
848 throw std::out_of_range ("Cannot get back() of empty view");
849
850 auto it = end ();
851 --it;
852 return *it;
853 }
854 VariantType at (size_t index) const
855 {
856 if (index >= size ())
857 throw std::out_of_range (
858 "UuidIdentifiableObjectView::at index out of range");
859
860 return (*this)[index];
861 }
862
863 size_t size () const
864 {
865 if (objects_)
866 return objects_->size ();
867 if (refs_)
868 return refs_->size ();
869 return uuids_->size ();
870 }
871
872 bool empty () const { return size () == 0; }
873
874 // Projections and transformations
875 static UuidType uuid_projection (const VariantType &var)
876 {
877 return std::visit ([] (const auto &obj) { return obj->get_uuid (); }, var);
878 }
879
880 template <typename T> auto get_elements_by_type () const
881 {
882 return *this | std::views::filter ([] (const auto &var) {
883 return std::holds_alternative<T *> (var);
884 }) | std::views::transform ([] (const auto &var) {
885 return std::get<T *> (var);
886 });
887 }
888
889 static RegistryT::BaseType * base_projection (const VariantType &var)
890 {
891 return std::visit (
892 [] (const auto &ptr) -> RegistryT::BaseType * { return ptr; }, var);
893 }
894
895 auto as_base_type () const
896 {
897 return std::views::transform (*this, base_projection);
898 }
899
900 template <typename T> static auto type_projection (const VariantType &var)
901 {
902 return std::holds_alternative<T *> (var);
903 }
904
905 template <typename T> bool contains_type () const
906 {
907 return std::ranges::any_of (*this, type_projection<T>);
908 }
909
910 template <typename BaseType>
911 static auto derived_from_type_projection (const VariantType &var)
912 {
913 return std::visit (
914 [] (const auto &ptr) {
915 return std::derived_from<base_type<decltype (ptr)>, BaseType>;
916 },
917 var);
918 }
919
924 template <typename T> static auto type_transformation (const VariantType &var)
925 {
926 return std::get<T *> (var);
927 }
928
929 template <typename T>
930 static auto derived_from_type_transformation (const VariantType &var)
931 {
932 return std::visit (
933 [] (const auto &ptr) -> T * {
934 using ElementT = base_type<decltype (ptr)>;
935 if constexpr (std::derived_from<ElementT, T>)
936 return ptr;
937 throw std::runtime_error ("Not derived from type");
938 },
939 var);
940 }
941
942 // note: assumes all objects are of this type
943 template <typename T> auto as_type () const
944 {
945 return std::views::transform (*this, type_transformation<T>);
946 }
947
948 template <typename T> auto get_elements_derived_from () const
949 {
950 return *this | std::views::filter (derived_from_type_projection<T>)
951 | std::views::transform (derived_from_type_transformation<T>);
952 }
953
954private:
955 std::optional<std::span<const VariantType>> objects_;
956 std::optional<std::span<const UuidRefType>> refs_;
957 std::optional<std::span<const UuidType>> uuids_;
958 const RegistryT * registry_ = nullptr;
959};
960
961} // namespace zrythm::utils
962
963// Concept to detect UuidIdentifiableObject::Uuid types
964template <typename T>
965concept UuidType = requires (T t) {
966 typename T::UuidTag;
967 { type_safe::get (t) } -> std::convertible_to<QUuid>;
968};
969
970// Formatter for any UUID type from UuidIdentifiableObject
971template <UuidType T>
972struct fmt::formatter<T> : fmt::formatter<std::string_view>
973{
974 template <typename FormatContext>
975 auto format (const T &uuid, FormatContext &ctx) const
976 {
977 return fmt::formatter<QString>{}.format (
978 type_safe::get (uuid).toString (QUuid::WithoutBraces), ctx);
979 }
980};
void register_object(VariantT obj_ptr)
Registers an object.
auto reference_count(const UuidType &id) const
Returns the reference count of an object.
auto get_uuids() const
Returns a list of all UUIDs of the objects in the registry.
std::optional< VariantT > find_by_id(const UuidType &id) const noexcept
Returns an object by id.
UuidIdentifiableObjectView(std::span< const UuidRefType > refs)
Constructor for UuidReference range.
static auto type_transformation(const VariantType &var)
UuidIdentifiableObjectView(std::span< const VariantType > objects)
Constructor for direct object range.
UuidIdentifiableObjectView(const VariantType &obj)
Single object constructor.
UuidIdentifiableObjectView(const RegistryT &registry, std::span< const UuidType > uuids)
Constructor for Uuid + Registry.
A reference-counted RAII wrapper for a UUID in a registry.
void set_id(const UuidType &id)
To be used when using the Registry-only constructor.
String utilities.
Definition algorithms.h:12
@ NewIdentity
Creates a separately identified object.
Definition icloneable.h:30
bool is_null() const
Checks if the UUID is null.