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 "utils/format.h"
7#include "utils/icloneable.h"
8#include "utils/initializable_object.h"
9#include "utils/logger.h"
10#include "utils/serialization.h"
11#include "utils/string.h"
12
13#include <QUuid>
14
15#include "type_safe/strong_typedef.hpp"
16#include <boost/stl_interfaces/iterator_interface.hpp>
17
18namespace zrythm::utils
19{
23template <typename Derived> class UuidIdentifiableObject
24{
25public:
26 struct Uuid final
27 : type_safe::strong_typedef<Uuid, QUuid>,
28 type_safe::strong_typedef_op::equality_comparison<Uuid>,
29 type_safe::strong_typedef_op::relational_comparison<Uuid>
30 {
31 using UuidTag = void; // used by the fmt formatter below
32 using type_safe::strong_typedef<Uuid, QUuid>::strong_typedef;
33
34 explicit Uuid () = default;
35
36 static_assert (StrongTypedef<Uuid>);
37 // static_assert (type_safe::is_strong_typedef<Uuid>::value);
38 // static_assert (std::is_same_v<type_safe::underlying_type<Uuid>, QUuid>);
39
43 bool is_null () const { return type_safe::get (*this).isNull (); }
44
45 void set_null () { type_safe::get (*this) = QUuid (); }
46
47 std::size_t hash () const { return qHash (type_safe::get (*this)); }
48 };
49 static_assert (std::regular<Uuid>);
50
51 UuidIdentifiableObject () : uuid_ (Uuid (QUuid::createUuid ())) { }
52 UuidIdentifiableObject (const Uuid &id) : uuid_ (id) { }
53 UuidIdentifiableObject (const UuidIdentifiableObject &other) = default;
54 UuidIdentifiableObject &
55 operator= (const UuidIdentifiableObject &other) = default;
56 UuidIdentifiableObject (UuidIdentifiableObject &&other) = default;
57 UuidIdentifiableObject &operator= (UuidIdentifiableObject &&other) = default;
58 virtual ~UuidIdentifiableObject () = default;
59
60 auto get_uuid () const { return uuid_; }
61
62 friend void init_from (
63 UuidIdentifiableObject &obj,
64 const UuidIdentifiableObject &other,
65 utils::ObjectCloneType clone_type)
66 {
68 {
69 obj.uuid_ = Uuid (QUuid::createUuid ());
70 }
71 else
72 {
73 obj.uuid_ = other.uuid_;
74 }
75 }
76
77 friend void to_json (nlohmann::json &j, const UuidIdentifiableObject &obj)
78 {
79 j[kUuidKey] = obj.uuid_;
80 }
81 friend void from_json (const nlohmann::json &j, UuidIdentifiableObject &obj)
82 {
83 j.at (kUuidKey).get_to (obj.uuid_);
84 }
85
86 friend bool operator== (
87 const UuidIdentifiableObject &lhs,
88 const UuidIdentifiableObject &rhs)
89 {
90 return lhs.uuid_ == rhs.uuid_;
91 }
92
93private:
94 static constexpr std::string_view kUuidKey = "id";
95
96 Uuid uuid_;
97
98 BOOST_DESCRIBE_CLASS (UuidIdentifiableObject, (), (), (), (uuid_))
99};
100
101template <typename T>
102concept UuidIdentifiable = std::derived_from<T, UuidIdentifiableObject<T>>;
103
104template <typename T>
106 UuidIdentifiable<T> && std::derived_from<T, QObject>;
107
108// specialization for std::hash and QHash
109#define DEFINE_UUID_HASH_SPECIALIZATION(UuidType) \
110 namespace std \
111 { \
112 template <> struct hash<UuidType> \
113 { \
114 size_t operator() (const UuidType &uuid) const \
115 { \
116 return uuid.hash (); \
117 } \
118 }; \
119 }
120
130template <typename RegistryT> class UuidReference
131{
132public:
133 using UuidType = typename RegistryT::UuidType;
134 using VariantType = typename RegistryT::VariantType;
135
136 static constexpr auto kIdKey = "id"sv;
137
138 // unengaged - need to call set_id to acquire a reference
139 UuidReference (RegistryT &registry) : registry_ (registry) { }
140
141 UuidReference (const UuidType &id, RegistryT &registry)
142 : id_ (id), registry_ (registry)
143 {
144 acquire_ref ();
145 }
146
147 UuidReference (const UuidReference &other)
148 : UuidReference (other.get_registry ())
149 {
150 if (other.id_.has_value ())
151 {
152 set_id (other.id_.value ());
153 }
154 }
155 UuidReference &operator= (const UuidReference &other)
156 {
157 if (this != &other)
158 {
159 id_ = other.id_;
160 registry_ = other.registry_;
161 acquire_ref ();
162 }
163 return *this;
164 }
165
166 UuidReference (UuidReference &&other)
167 : id_ (std::exchange (other.id_, std::nullopt)),
168 registry_ (std::exchange (other.registry_, std::nullopt))
169 {
170 // No need to acquire_ref() here because we're taking over the reference
171 // from 'other' which already holds it
172 }
173
174 UuidReference &operator= (UuidReference &&other)
175 {
176 if (this != &other)
177 {
178 // Release our current reference (if any)
179 release_ref ();
180
181 // Take ownership from other
182 id_ = std::exchange (other.id_, std::nullopt);
183 registry_ = std::exchange (other.registry_, std::nullopt);
184
185 // No need to acquire_ref() - we're taking over the reference
186 }
187 return *this;
188 }
189
190 ~UuidReference () { release_ref (); }
191
192 const UuidType &id () const { return *id_; }
193
201 void set_id (const UuidType &id)
202 {
203 if (id_.has_value ())
204 {
205 throw std::runtime_error (
206 "Cannot set id of UuidReference that already has an id");
207 }
208 id_ = id;
209 acquire_ref ();
210 }
211
212 VariantType get_object () const
213 {
214 return get_registry ().find_by_id_or_throw (id ());
215 }
216
217 // Convenience getter
218 template <typename ObjectT>
219 ObjectT * get_object_as () const
220 requires IsInVariant<ObjectT *, VariantType>
221 {
222 return std::get<ObjectT *> (get_object ());
223 }
224
225 template <typename... Args>
226 UuidReference clone_new_identity (Args &&... args) const
227 {
228 return std::visit (
229 [&] (auto &&obj) {
230 return get_registry ().clone_object (*obj, std::forward<Args> (args)...);
231 },
232 get_object ());
233 }
234
235 auto get_iterator_in_registry () const
236 {
237 return get_registry ().get_iterator_for_id (id ());
238 }
239
240 auto get_iterator_in_registry ()
241 {
242 return get_registry ().get_iterator_for_id (id ());
243 }
244
245private:
246 void acquire_ref ()
247 {
248 if (id_.has_value ())
249 {
250 get_registry ().acquire_reference (*id_);
251 }
252 }
253 void release_ref ()
254 {
255 if (id_.has_value ())
256 {
257 get_registry ().release_reference (*id_);
258 }
259 }
260 RegistryT &get_registry () const
261 {
262 return const_cast<RegistryT &> (*registry_);
263 }
264 RegistryT &get_registry () { return *registry_; }
265
266 friend void to_json (nlohmann::json &j, const UuidReference &ref)
267 {
268 assert (ref.id_.has_value ());
269 j[kIdKey] = *ref.id_;
270 }
271 friend void from_json (const nlohmann::json &j, UuidReference &ref)
272 {
273 auto tmp = ref;
274 j.at (kIdKey).get_to (ref.id_);
275 ref.acquire_ref ();
276 tmp.release_ref ();
277 }
278
279 friend bool operator== (const UuidReference &lhs, const UuidReference &rhs)
280 {
281 return lhs.id () == rhs.id ();
282 }
283
284private:
285 std::optional<UuidType> id_;
286 OptionalRef<RegistryT> registry_;
287
288 BOOST_DESCRIBE_CLASS (UuidReference<RegistryT>, (), (), (), (id_))
289};
290
291template <typename ReturnType, typename UuidType>
292using UuidIdentifiablObjectResolver =
293 std::function<ReturnType (const UuidType &)>;
294
311template <typename VariantT, UuidIdentifiable BaseT>
312class OwningObjectRegistry : public QObject
313{
314public:
316 using VariantType = VariantT;
317 using BaseType = BaseT;
318
319 OwningObjectRegistry (QObject * parent = nullptr) : QObject (parent) { }
320
321 // ========================================================================
322 // QML/QObject Interface
323 // ========================================================================
324
325 // TODO signals...
326
327 // ========================================================================
328
329 // Factory method that forwards constructor arguments
330 template <typename CreateType, typename... Args>
331 auto create_object (Args &&... args) -> UuidReference<OwningObjectRegistry>
332 requires std::derived_from<CreateType, BaseT>
333 {
334 CreateType * obj{};
336 {
337 auto obj_unique_ptr =
338 CreateType::create_unique (std::forward<Args> (args)...);
339 obj = obj_unique_ptr.release ();
340 }
341 else
342 {
343 obj = new CreateType (std::forward<Args> (args)...);
344 }
345 z_trace (
346 "created object of type {} with ID {}", typeid (CreateType).name (),
347 obj->get_uuid ());
348 register_object (obj);
349 return { obj->get_uuid (), *this };
350 }
351
352 // Creates a clone of the given object and registers it to the registry.
353 template <typename CreateType, typename... Args>
354 auto clone_object (const CreateType &other, Args &&... args)
356 requires std::derived_from<CreateType, BaseT>
357 {
358 CreateType * obj = clone_raw_ptr (
359 other, ObjectCloneType::NewIdentity, std::forward<Args> (args)...);
360 register_object (obj);
361 return { obj->get_uuid (), *this };
362 }
363
364 [[gnu::hot]] auto get_iterator_for_id (const UuidType &id) const
365 {
366 const auto &uuid = type_safe::get (id);
367 return objects_by_id_.constFind (uuid);
368 }
369
370 [[gnu::hot]] auto get_iterator_for_id (const UuidType &id)
371 {
372 const auto &uuid = type_safe::get (id);
373 return objects_by_id_.find (uuid);
374 }
375
381 [[gnu::hot]] std::optional<std::reference_wrapper<const VariantT>>
382 find_by_id (const UuidType &id) const
383 {
384 const auto it = get_iterator_for_id (id);
385 if (it == objects_by_id_.end ())
386 {
387 return std::nullopt;
388 }
389 return *it;
390 }
391
392 [[gnu::hot]] auto &find_by_id_or_throw (const UuidType &id) const
393 {
394 auto val = find_by_id (id);
395 if (!val.has_value ())
396 {
397 throw std::runtime_error (
398 fmt::format ("Object with id {} not found", id));
399 }
400 return val.value ().get ();
401 }
402
403 bool contains (const UuidType &id) const
404 {
405 const auto &uuid = type_safe::get (id);
406 return objects_by_id_.contains (uuid);
407 }
408
414 void register_object (VariantT obj_ptr)
415 {
416 std::visit (
417 [&] (auto &&obj) {
418 if (contains (obj->get_uuid ()))
419 {
420 throw std::runtime_error (
421 fmt::format ("Object with id {} already exists", obj->get_uuid ()));
422 }
423 z_trace ("Registering (inserting) object {}", obj->get_uuid ());
424 obj->setParent (this);
425 objects_by_id_.insert (type_safe::get (obj->get_uuid ()), obj);
426 },
427 obj_ptr);
428 }
429
430 template <typename ObjectT>
431 void register_object (ObjectT &obj)
432 requires std::derived_from<ObjectT, BaseT>
433 {
434 register_object (&obj);
435 }
436
442 auto reference_count (const UuidType &id) const
443 {
444 return ref_counts_[type_safe::get (id)];
445 }
446
447 void acquire_reference (const UuidType &id)
448 {
449 const auto &quuid = type_safe::get (id);
450 ref_counts_[quuid]++;
451 }
452
453 void release_reference (const UuidType &id)
454 {
455 const auto &quuid = type_safe::get (id);
456 if (--ref_counts_[quuid] <= 0)
457 {
458 delete_object_by_id (id);
459 }
460 }
461
462 auto &get_hash_map () const { return objects_by_id_; }
463
467 auto get_uuids () const
468 {
469 return objects_by_id_.keys () | std::views::transform ([] (const auto &uuid) {
470 return UuidType (uuid);
471 })
472 | std::ranges::to<std::vector> ();
473 }
474
475 size_t size () const { return objects_by_id_.size (); }
476
477 friend void to_json (nlohmann::json &j, const OwningObjectRegistry &obj)
478 {
479 auto objects = nlohmann::json::array ();
480 for (const auto &var : obj.objects_by_id_.values ())
481 {
482 objects.push_back (var);
483 }
484 j[kObjectsKey] = objects;
485 }
486 template <ObjectBuilder BuilderT>
487 friend void from_json_with_builder (
488 const nlohmann::json &j,
489 OwningObjectRegistry &obj,
490 const BuilderT &builder)
491 {
492 auto objects = j.at (kObjectsKey);
493 for (const auto &object_var_json : objects)
494 {
495 VariantT object_var;
496 utils::serialization::variant_from_json_with_builder (
497 object_var_json, object_var, builder);
498 obj.register_object (object_var);
499 }
500 }
501
502private:
503 static constexpr const char * kObjectsKey = "objectsById";
504
511 VariantT unregister_object (const UuidType &id)
512 {
513 if (!objects_by_id_.contains (type_safe::get (id)))
514 {
515 throw std::runtime_error (
516 fmt::format ("Object with id {} not found", id));
517 }
518
519 z_trace ("Unregistering object with id {}", id);
520
521 auto obj_var = objects_by_id_.take (type_safe::get (id));
522 std::visit ([&] (auto &&obj) { obj->setParent (nullptr); }, obj_var);
523 if (ref_counts_.contains (type_safe::get (id)))
524 {
525 ref_counts_.remove (type_safe::get (id));
526 }
527 return obj_var;
528 }
529
530 void delete_object_by_id (const UuidType &id)
531 {
532 auto obj_var = unregister_object (id);
533 std::visit ([&] (auto &&obj) { delete obj; }, obj_var);
534 }
535
536private:
537 QHash<QUuid, VariantT> objects_by_id_;
538 QHash<QUuid, int> ref_counts_;
539};
540
541template <typename RegistryT> class UuidIdentifiableObjectSelectionManager
542{
543 using UuidType = typename RegistryT::UuidType;
544
545public:
546 using UuidSet = std::unordered_set<UuidType>;
547
548 UuidIdentifiableObjectSelectionManager (
549 UuidSet &selected_objs,
550 const RegistryT &registry)
551 : selected_objects_ (selected_objs), registry_ (registry)
552 {
553 }
554 void append_to_selection (const UuidType &id)
555 {
556 if (!is_selected (id))
557 {
558 selected_objects_.insert (id);
559 emit_selection_changed_for_object (id);
560 }
561 }
562 void remove_from_selection (const UuidType &id)
563 {
564 if (is_selected (id))
565 {
566 selected_objects_.erase (id);
567 emit_selection_changed_for_object (id);
568 }
569 }
570 void select_unique (const UuidType &id)
571 {
572 clear_selection ();
573 append_to_selection (id);
574 }
575 bool is_selected (const UuidType &id) const
576 {
577 return selected_objects_.contains (id);
578 }
579 bool is_only_selection (const UuidType &id) const
580 {
581 return selected_objects_.size () == 1 && is_selected (id);
582 }
583 bool empty () const { return selected_objects_.empty (); }
584 auto size () const { return selected_objects_.size (); }
585
586 void clear_selection ()
587 { // Make a copy of the selected tracks to iterate over
588 auto selected_objs_copy = selected_objects_;
589 for (const auto &uuid : selected_objs_copy)
590 {
591 selected_objects_.erase (uuid);
592 emit_selection_changed_for_object (uuid);
593 }
594 }
595
596 template <RangeOf<UuidType> UuidRange>
597 void select_only_these (const UuidRange &uuids)
598 {
599 clear_selection ();
600 for (const auto &uuid : uuids)
601 {
602 append_to_selection (uuid);
603 }
604 }
605
606private:
607 auto get_object_for_id (const UuidType &id)
608 {
609 return registry_.find_by_id_or_throw (id);
610 }
611 void emit_selection_changed_for_object (const UuidType &id)
612 {
613 std::visit (
614 [&] (auto &&obj) { Q_EMIT obj->selectedChanged (is_selected (id)); },
615 get_object_for_id (id));
616 }
617
618private:
619 UuidSet &selected_objects_;
620 const RegistryT &registry_;
621};
622
629template <typename RegistryT>
631 : public std::ranges::view_interface<UuidIdentifiableObjectView<RegistryT>>
632{
633public:
634 using UuidType = typename RegistryT::UuidType;
635 using VariantType = typename RegistryT::VariantType;
636 using UuidRefType = UuidReference<RegistryT>;
637
638 // Proxy iterator implementation using Boost.STLInterfaces
639 class Iterator
640 : public boost::stl_interfaces::proxy_iterator_interface<
641#if !BOOST_STL_INTERFACES_USE_DEDUCED_THIS
642 Iterator,
643#endif
644 std::random_access_iterator_tag,
645 VariantType>
646 {
647 public:
648 using base_type = boost::stl_interfaces::proxy_iterator_interface<
649#if !BOOST_STL_INTERFACES_USE_DEDUCED_THIS
650 Iterator,
651#endif
652 std::random_access_iterator_tag,
653 VariantType>;
654 using difference_type = base_type::difference_type;
655
656 constexpr Iterator () noexcept = default;
657
658 // Constructor for direct object range
659 constexpr Iterator (std::span<const VariantType>::iterator it) noexcept
660 : it_var_ (it)
661 {
662 }
663
664 // Constructor for UuidReference range
665 constexpr Iterator (std::span<const UuidRefType>::iterator it) noexcept
666 : it_var_ (it)
667 {
668 }
669
670 // Constructor for Uuid + Registry range
671 constexpr Iterator (
672 std::span<const UuidType>::iterator it,
673 const RegistryT * registry) noexcept
674 : it_var_ (std::pair{ it, registry })
675 {
676 }
677
678 constexpr VariantType operator* () const
679 {
680 return std::visit (
681 [] (auto &&arg) {
682 using T = std::decay_t<decltype (arg)>;
683 if constexpr (
684 std::is_same_v<T, typename std::span<const VariantType>::iterator>)
685 {
686 return *arg;
687 }
688 else if constexpr (
689 std::is_same_v<T, typename std::span<const UuidRefType>::iterator>)
690 {
691 return arg->get_object ();
692 }
693 else if constexpr (
694 std::is_same_v<
695 T,
696 std::pair<
697 typename std::span<const UuidType>::iterator, const RegistryT *>>)
698 {
699 return arg.second->find_by_id_or_throw (*arg.first);
700 }
701 },
702 it_var_);
703 }
704
705 constexpr Iterator &operator+= (difference_type n) noexcept
706 {
707 std::visit (
708 [n] (auto &&arg) {
709 using T = std::decay_t<decltype (arg)>;
710 if constexpr (
711 std::is_same_v<T, typename std::span<const VariantType>::iterator>)
712 {
713 arg += n;
714 }
715 else if constexpr (
716 std::is_same_v<T, typename std::span<const UuidRefType>::iterator>)
717 {
718 arg += n;
719 }
720 else if constexpr (
721 std::is_same_v<
722 T,
723 std::pair<
724 typename std::span<const UuidType>::iterator, const RegistryT *>>)
725 {
726 arg.first += n;
727 }
728 },
729 it_var_);
730 return *this;
731 }
732
733 constexpr difference_type operator- (Iterator other) const
734 {
735 return std::visit (
736 [] (auto &&arg, auto &&other_arg) -> difference_type {
737 using T = std::decay_t<decltype (arg)>;
738 using OtherT = std::decay_t<decltype (other_arg)>;
739 if constexpr (!std::is_same_v<T, OtherT>)
740 {
741 throw std::runtime_error (
742 "Comparing iterators of different types");
743 }
744 else if constexpr (
745 std::is_same_v<T, typename std::span<const VariantType>::iterator>)
746 {
747 return arg - other_arg;
748 }
749 else if constexpr (
750 std::is_same_v<T, typename std::span<const UuidRefType>::iterator>)
751 {
752 return arg - other_arg;
753 }
754 else if constexpr (
755 std::is_same_v<
756 T,
757 std::pair<
758 typename std::span<const UuidType>::iterator, const RegistryT *>>)
759 {
760 return arg.first - other_arg.first;
761 }
762 else
763 {
764 return 0;
765 }
766 },
767 it_var_, other.it_var_);
768 }
769
770 constexpr auto operator<=> (const Iterator &other) const
771 {
772 return std::visit (
773 [] (auto &&arg, auto &&other_arg) -> std::strong_ordering {
774 using T = std::decay_t<decltype (arg)>;
775 using OtherT = std::decay_t<decltype (other_arg)>;
776 if constexpr (!std::is_same_v<T, OtherT>)
777 {
778 throw std::runtime_error (
779 "Comparing iterators of different types");
780 }
781 else if constexpr (
782 std::is_same_v<T, typename std::span<const VariantType>::iterator>)
783 {
784 return arg <=> other_arg;
785 }
786 else if constexpr (
787 std::is_same_v<T, typename std::span<const UuidRefType>::iterator>)
788 {
789 return arg <=> other_arg;
790 }
791 else if constexpr (
792 std::is_same_v<
793 T,
794 std::pair<
795 typename std::span<const UuidType>::iterator, const RegistryT *>>)
796 {
797 return arg.first <=> other_arg.first;
798 }
799 else
800 {
801 return std::strong_ordering::equal;
802 }
803 },
804 it_var_, other.it_var_);
805 }
806
807 private:
808 std::variant<
809 typename std::span<const VariantType>::iterator,
810 typename std::span<const UuidRefType>::iterator,
811 std::pair<typename std::span<const UuidType>::iterator, const RegistryT *>>
812 it_var_;
813 };
814
816 explicit UuidIdentifiableObjectView (std::span<const VariantType> objects)
817 : objects_ (objects)
818 {
819 }
820
822 explicit UuidIdentifiableObjectView (std::span<const UuidRefType> refs)
823 : refs_ (refs)
824 {
825 }
826
829 const RegistryT &registry,
830 std::span<const UuidType> uuids)
831 : uuids_ (uuids), registry_ (&registry)
832 {
833 }
834
836 explicit UuidIdentifiableObjectView (const VariantType &obj)
837 : objects_ (std::span<const VariantType> (&obj, 1))
838 {
839 }
840
841 Iterator begin () const
842 {
843 if (objects_)
844 {
845 return Iterator (objects_->begin ());
846 }
847 else if (refs_)
848 {
849 return Iterator (refs_->begin ());
850 }
851 else
852 {
853 return Iterator (uuids_->begin (), registry_);
854 }
855 }
856
857 Iterator end () const
858 {
859 if (objects_)
860 {
861 return Iterator (objects_->end ());
862 }
863 else if (refs_)
864 {
865 return Iterator (refs_->end ());
866 }
867 else
868 {
869 return Iterator (uuids_->end (), registry_);
870 }
871 }
872
873 VariantType operator[] (size_t index) const
874 {
875 if (objects_)
876 return (*objects_)[index];
877 if (refs_)
878 return (*refs_)[index].get_object ();
879 return registry_->find_by_id_or_throw ((*uuids_)[index]);
880 }
881
882 VariantType front () const { return *begin (); }
883 VariantType back () const
884 {
885 if (empty ())
886 throw std::out_of_range ("Cannot get back() of empty view");
887
888 auto it = end ();
889 --it;
890 return *it;
891 }
892 VariantType at (size_t index) const
893 {
894 if (index >= size ())
895 throw std::out_of_range (
896 "UuidIdentifiableObjectView::at index out of range");
897
898 return (*this)[index];
899 }
900
901 size_t size () const
902 {
903 if (objects_)
904 return objects_->size ();
905 if (refs_)
906 return refs_->size ();
907 return uuids_->size ();
908 }
909
910 bool empty () const { return size () == 0; }
911
912 // Projections and transformations
913 static UuidType uuid_projection (const VariantType &var)
914 {
915 return std::visit ([] (const auto &obj) { return obj->get_uuid (); }, var);
916 }
917
918 template <typename T> auto get_elements_by_type () const
919 {
920 return *this | std::views::filter ([] (const auto &var) {
921 return std::holds_alternative<T *> (var);
922 }) | std::views::transform ([] (const auto &var) {
923 return std::get<T *> (var);
924 });
925 }
926
927 static RegistryT::BaseType * base_projection (const VariantType &var)
928 {
929 return std::visit (
930 [] (const auto &ptr) -> RegistryT::BaseType * { return ptr; }, var);
931 }
932
933 auto as_base_type () const
934 {
935 return std::views::transform (*this, base_projection);
936 }
937
938 template <typename T> static auto type_projection (const VariantType &var)
939 {
940 return std::holds_alternative<T *> (var);
941 }
942
943 template <typename T> bool contains_type () const
944 {
945 return std::ranges::any_of (*this, type_projection<T>);
946 }
947
948 template <typename BaseType>
949 static auto derived_from_type_projection (const VariantType &var)
950 {
951 return std::visit (
952 [] (const auto &ptr) {
953 return std::derived_from<base_type<decltype (ptr)>, BaseType>;
954 },
955 var);
956 }
957
962 template <typename T> static auto type_transformation (const VariantType &var)
963 {
964 return std::get<T *> (var);
965 }
966
967 template <typename T>
968 static auto derived_from_type_transformation (const VariantType &var)
969 {
970 return std::visit (
971 [] (const auto &ptr) -> T * {
972 using ElementT = base_type<decltype (ptr)>;
973 if constexpr (std::derived_from<ElementT, T>)
974 return ptr;
975 throw std::runtime_error ("Not derived from type");
976 },
977 var);
978 }
979
980 // note: assumes all objects are of this type
981 template <typename T> auto as_type () const
982 {
983 return std::views::transform (*this, type_transformation<T>);
984 }
985
986 template <typename T> auto get_elements_derived_from () const
987 {
988 return *this | std::views::filter (derived_from_type_projection<T>)
989 | std::views::transform (derived_from_type_transformation<T>);
990 }
991
992private:
993 std::optional<std::span<const VariantType>> objects_;
994 std::optional<std::span<const UuidRefType>> refs_;
995 std::optional<std::span<const UuidType>> uuids_;
996 const RegistryT * registry_ = nullptr;
997};
998
999} // namespace zrythm::utils
1000
1001// Concept to detect UuidIdentifiableObject::Uuid types
1002template <typename T>
1003concept UuidType = requires (T t) {
1004 typename T::UuidTag;
1005 { type_safe::get (t) } -> std::convertible_to<QUuid>;
1006};
1007
1008// Formatter for any UUID type from UuidIdentifiableObject
1009template <UuidType T>
1010struct fmt::formatter<T> : fmt::formatter<std::string_view>
1011{
1012 template <typename FormatContext>
1013 auto format (const T &uuid, FormatContext &ctx) const
1014 {
1015 return fmt::formatter<QString>{}.format (
1016 type_safe::get (uuid).toString (QUuid::WithoutBraces), ctx);
1017 }
1018};
std::optional< std::reference_wrapper< const VariantT > > find_by_id(const UuidType &id) const
Returns an object by id.
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.
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:31
bool is_null() const
Checks if the UUID is null.