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 // unengaged - need to call set_id to acquire a reference
144 UuidReference (RegistryT &registry) : registry_ (registry) { }
145
146 UuidReference (const UuidType &id, RegistryT &registry)
147 : id_ (id), registry_ (registry)
148 {
149 acquire_ref ();
150 }
151
152 UuidReference (const UuidReference &other)
153 : UuidReference (other.get_registry ())
154 {
155 if (other.id_.has_value ())
156 {
157 set_id (other.id_.value ());
158 }
159 }
160 UuidReference &operator= (const UuidReference &other)
161 {
162 if (this != &other)
163 {
164 id_ = other.id_;
165 registry_ = other.registry_;
166 acquire_ref ();
167 }
168 return *this;
169 }
170
171 UuidReference (UuidReference &&other)
172 : id_ (std::exchange (other.id_, std::nullopt)),
173 registry_ (std::exchange (other.registry_, std::nullopt))
174 {
175 // No need to acquire_ref() here because we're taking over the reference
176 // from 'other' which already holds it
177 }
178
179 UuidReference &operator= (UuidReference &&other)
180 {
181 if (this != &other)
182 {
183 // Release our current reference (if any)
184 release_ref ();
185
186 // Take ownership from other
187 id_ = std::exchange (other.id_, std::nullopt);
188 registry_ = std::exchange (other.registry_, std::nullopt);
189
190 // No need to acquire_ref() - we're taking over the reference
191 }
192 return *this;
193 }
194
195 ~UuidReference () { release_ref (); }
196
197 auto id () const -> UuidType { return *id_; }
198
206 void set_id (const UuidType &id)
207 {
208 if (id_.has_value ())
209 {
210 throw std::runtime_error (
211 "Cannot set id of UuidReference that already has an id");
212 }
213 id_ = id;
214 acquire_ref ();
215 }
216
217 VariantType get_object () const
218 {
219 return get_registry ().find_by_id_or_throw (id ());
220 }
221
222 // Convenience getter
223 template <typename ObjectT>
224 ObjectT * get_object_as () const
225 requires IsInVariant<ObjectT *, VariantType>
226 {
227 return std::get<ObjectT *> (get_object ());
228 }
229
230 RegistryT::BaseType * get_object_base () const
231 {
232 return get_registry ().find_by_id_as_base_or_throw (id ());
233 }
234
235 template <typename... Args>
236 UuidReference clone_new_identity (Args &&... args) const
237 {
238 return std::visit (
239 [&] (auto &&obj) {
240 return get_registry ().clone_object (*obj, std::forward<Args> (args)...);
241 },
242 get_object ());
243 }
244
245 auto get_iterator_in_registry () const
246 {
247 return get_registry ().get_iterator_for_id (id ());
248 }
249
250 auto get_iterator_in_registry ()
251 {
252 return get_registry ().get_iterator_for_id (id ());
253 }
254
255private:
256 void acquire_ref ()
257 {
258 if (id_.has_value ())
259 {
260 get_registry ().acquire_reference (*id_);
261 }
262 }
263 void release_ref ()
264 {
265 if (id_.has_value ())
266 {
267 get_registry ().release_reference (*id_);
268 }
269 }
270 RegistryT &get_registry () const
271 {
272 return const_cast<RegistryT &> (*registry_);
273 }
274 RegistryT &get_registry () { return *registry_; }
275
276 friend void to_json (nlohmann::json &j, const UuidReference &ref)
277 {
278 assert (ref.id_.has_value ());
279 j = ref.id_;
280 }
281 friend void from_json (const nlohmann::json &j, UuidReference &ref)
282 {
283 auto tmp = ref;
284 j.get_to (ref.id_);
285 ref.acquire_ref ();
286 tmp.release_ref ();
287 }
288
289 friend bool operator== (const UuidReference &lhs, const UuidReference &rhs)
290 {
291 return lhs.id () == rhs.id ();
292 }
293
294private:
295 std::optional<UuidType> id_;
296 OptionalRef<RegistryT> registry_;
297
298 BOOST_DESCRIBE_CLASS (UuidReference<RegistryT>, (), (), (), (id_))
299};
300
301template <typename ReturnType, typename UuidType>
302using UuidIdentifiablObjectResolver =
303 std::function<ReturnType (const UuidType &)>;
304
322template <typename VariantT, UuidIdentifiable BaseT>
323class OwningObjectRegistry : public QObject
324{
325public:
327 using VariantType = VariantT;
328 using BaseType = BaseT;
329
330 OwningObjectRegistry (QObject * parent = nullptr)
331 : QObject (parent), main_thread_id_ (current_thread_id)
332 {
333 }
334
335 // ========================================================================
336 // QML/QObject Interface
337 // ========================================================================
338
339 // TODO signals...
340
341 // ========================================================================
342
343 // Factory method that forwards constructor arguments
344 template <typename CreateType, typename... Args>
345 auto create_object (Args &&... args) -> UuidReference<OwningObjectRegistry>
346 requires std::derived_from<CreateType, BaseT>
347 {
348 assert (is_main_thread ());
349
350 auto * obj = new CreateType (std::forward<Args> (args)...);
351 z_trace (
352 "created object of type {} with ID {}", typeid (CreateType).name (),
353 obj->get_uuid ());
354 register_object (obj);
355 return { obj->get_uuid (), *this };
356 }
357
358 // Creates a clone of the given object and registers it to the registry.
359 template <typename CreateType, typename... Args>
360 auto clone_object (const CreateType &other, Args &&... args)
362 requires std::derived_from<CreateType, BaseT>
363 {
364 assert (is_main_thread ());
365
366 CreateType * obj = clone_raw_ptr (
367 other, ObjectCloneType::NewIdentity, std::forward<Args> (args)...);
368 register_object (obj);
369 return { obj->get_uuid (), *this };
370 }
371
372 [[gnu::hot]] auto get_iterator_for_id (const UuidType &id) const
373 {
374 // TODO: some failures left to fix
375 // assert (is_main_thread ());
376
377 return objects_by_id_.find (id);
378 }
379
380 [[gnu::hot]] auto get_iterator_for_id (const UuidType &id)
381 {
382 // assert (is_main_thread ());
383
384 return objects_by_id_.find (id);
385 }
386
392 [[gnu::hot]] std::optional<VariantT>
393 find_by_id (const UuidType &id) const noexcept [[clang::blocking]]
394 {
395 const auto it = get_iterator_for_id (id);
396 if (it == objects_by_id_.end ())
397 {
398 return std::nullopt;
399 }
400 return it->second;
401 }
402
403 [[gnu::hot]] auto find_by_id_or_throw (const UuidType &id) const
404 {
405 auto val = find_by_id (id);
406 if (!val.has_value ())
407 {
408 throw std::runtime_error (
409 fmt::format ("Object with id {} not found", id));
410 }
411 return val.value ();
412 }
413
414 BaseT * find_by_id_as_base_or_throw (const UuidType &id) const
415 {
416 auto var = find_by_id_or_throw (id);
417 return std::visit ([] (auto &&val) -> BaseT * { return val; }, var);
418 }
419
420 bool contains (const UuidType &id) const
421 {
422 // assert (is_main_thread ());
423
424 return objects_by_id_.contains (id);
425 }
426
432 void register_object (VariantT obj_ptr)
433 {
434 assert (is_main_thread ());
435
436 std::visit (
437 [&] (auto &&obj) {
438 if (contains (obj->get_uuid ()))
439 {
440 throw std::runtime_error (
441 fmt::format ("Object with id {} already exists", obj->get_uuid ()));
442 }
443 z_trace ("Registering (inserting) object {}", obj->get_uuid ());
444 obj->setParent (this);
445 objects_by_id_.emplace (obj->get_uuid (), obj);
446 },
447 obj_ptr);
448 }
449
450 template <typename ObjectT>
451 void register_object (ObjectT &obj)
452 requires std::derived_from<ObjectT, BaseT>
453 {
454 register_object (&obj);
455 }
456
462 auto reference_count (const UuidType &id) const
463 {
464 assert (is_main_thread ());
465
466 return ref_counts_.at (id);
467 }
468
469 void acquire_reference (const UuidType &id)
470 {
471 assert (is_main_thread ());
472
473 ref_counts_[id]++;
474 }
475
476 void release_reference (const UuidType &id)
477 {
478 assert (is_main_thread ());
479
480 if (--ref_counts_[id] <= 0)
481 {
482 delete_object_by_id (id);
483 }
484 }
485
486 auto &get_hash_map () const { return objects_by_id_; }
487
491 auto get_uuids () const
492 {
493 assert (is_main_thread ());
494
495 return objects_by_id_ | std::views::keys | std::ranges::to<std::vector> ();
496 }
497
498 size_t size () const
499 {
500 assert (is_main_thread ());
501
502 return objects_by_id_.size ();
503 }
504
505 friend void to_json (nlohmann::json &j, const OwningObjectRegistry &obj)
506 {
507 j = nlohmann::json::array ();
508 for (const auto &var : obj.objects_by_id_ | std::views::values)
509 {
510 j.push_back (var);
511 }
512 }
513 template <ObjectBuilder BuilderT>
514 friend void from_json_with_builder (
515 const nlohmann::json &j,
516 OwningObjectRegistry &obj,
517 const BuilderT &builder)
518 {
519 for (const auto &object_var_json : j)
520 {
521 VariantT object_var;
522 utils::serialization::variant_from_json_with_builder (
523 object_var_json, object_var, builder);
524 obj.register_object (object_var);
525 }
526 }
527
528private:
535 VariantT unregister_object (const UuidType &id)
536 {
537 if (!objects_by_id_.contains (id))
538 {
539 throw std::runtime_error (
540 fmt::format ("Object with id {} not found", id));
541 }
542
543 z_trace ("Unregistering object with id {}", id);
544
545 auto obj_it = get_iterator_for_id (id);
546 auto obj_var = obj_it->second;
547 objects_by_id_.erase (obj_it);
548 std::visit ([&] (auto &&obj) { obj->setParent (nullptr); }, obj_var);
549 ref_counts_.erase (id);
550 return obj_var;
551 }
552
553 void delete_object_by_id (const UuidType &id)
554 {
555 auto obj_var = unregister_object (id);
556 std::visit ([&] (auto &&obj) { delete obj; }, obj_var);
557 }
558
559 bool is_main_thread () const { return main_thread_id_ == current_thread_id; }
560
561private:
567 boost::unordered::unordered_flat_map<UuidType, VariantT> objects_by_id_;
568
574 boost::unordered::unordered_flat_map<UuidType, int> ref_counts_;
575
576 RTThreadId main_thread_id_;
577};
578
585template <typename RegistryT>
587 : public std::ranges::view_interface<UuidIdentifiableObjectView<RegistryT>>
588{
589public:
590 using UuidType = typename RegistryT::UuidType;
591 using VariantType = typename RegistryT::VariantType;
592 using UuidRefType = UuidReference<RegistryT>;
593
594 // Proxy iterator implementation using Boost.STLInterfaces
595 class Iterator
596 : public boost::stl_interfaces::proxy_iterator_interface<
597#if !BOOST_STL_INTERFACES_USE_DEDUCED_THIS
598 Iterator,
599#endif
600 std::random_access_iterator_tag,
601 VariantType>
602 {
603 public:
604 using base_type = boost::stl_interfaces::proxy_iterator_interface<
605#if !BOOST_STL_INTERFACES_USE_DEDUCED_THIS
606 Iterator,
607#endif
608 std::random_access_iterator_tag,
609 VariantType>;
610 using difference_type = base_type::difference_type;
611
612 constexpr Iterator () noexcept = default;
613
614 // Constructor for direct object range
615 constexpr Iterator (std::span<const VariantType>::iterator it) noexcept
616 : it_var_ (it)
617 {
618 }
619
620 // Constructor for UuidReference range
621 constexpr Iterator (std::span<const UuidRefType>::iterator it) noexcept
622 : it_var_ (it)
623 {
624 }
625
626 // Constructor for Uuid + Registry range
627 constexpr Iterator (
628 std::span<const UuidType>::iterator it,
629 const RegistryT * registry) noexcept
630 : it_var_ (std::pair{ it, registry })
631 {
632 }
633
634 constexpr VariantType operator* () const
635 {
636 return std::visit (
637 [] (auto &&arg) {
638 using T = std::decay_t<decltype (arg)>;
639 if constexpr (
640 std::is_same_v<T, typename std::span<const VariantType>::iterator>)
641 {
642 return *arg;
643 }
644 else if constexpr (
645 std::is_same_v<T, typename std::span<const UuidRefType>::iterator>)
646 {
647 return arg->get_object ();
648 }
649 else if constexpr (
650 std::is_same_v<
651 T,
652 std::pair<
653 typename std::span<const UuidType>::iterator, const RegistryT *>>)
654 {
655 return arg.second->find_by_id_or_throw (*arg.first);
656 }
657 },
658 it_var_);
659 }
660
661 constexpr Iterator &operator+= (difference_type n) noexcept
662 {
663 std::visit (
664 [n] (auto &&arg) {
665 using T = std::decay_t<decltype (arg)>;
666 if constexpr (
667 std::is_same_v<T, typename std::span<const VariantType>::iterator>)
668 {
669 arg += n;
670 }
671 else if constexpr (
672 std::is_same_v<T, typename std::span<const UuidRefType>::iterator>)
673 {
674 arg += n;
675 }
676 else if constexpr (
677 std::is_same_v<
678 T,
679 std::pair<
680 typename std::span<const UuidType>::iterator, const RegistryT *>>)
681 {
682 arg.first += n;
683 }
684 },
685 it_var_);
686 return *this;
687 }
688
689 constexpr difference_type operator- (Iterator other) const
690 {
691 return std::visit (
692 [] (auto &&arg, auto &&other_arg) -> difference_type {
693 using T = std::decay_t<decltype (arg)>;
694 using OtherT = std::decay_t<decltype (other_arg)>;
695 if constexpr (!std::is_same_v<T, OtherT>)
696 {
697 throw std::runtime_error (
698 "Comparing iterators of different types");
699 }
700 else if constexpr (
701 std::is_same_v<T, typename std::span<const VariantType>::iterator>)
702 {
703 return arg - other_arg;
704 }
705 else if constexpr (
706 std::is_same_v<T, typename std::span<const UuidRefType>::iterator>)
707 {
708 return arg - other_arg;
709 }
710 else if constexpr (
711 std::is_same_v<
712 T,
713 std::pair<
714 typename std::span<const UuidType>::iterator, const RegistryT *>>)
715 {
716 return arg.first - other_arg.first;
717 }
718 else
719 {
720 return 0;
721 }
722 },
723 it_var_, other.it_var_);
724 }
725
726 constexpr auto operator<=> (const Iterator &other) const
727 {
728 return std::visit (
729 [] (auto &&arg, auto &&other_arg) -> std::strong_ordering {
730 using T = std::decay_t<decltype (arg)>;
731 using OtherT = std::decay_t<decltype (other_arg)>;
732 if constexpr (!std::is_same_v<T, OtherT>)
733 {
734 throw std::runtime_error (
735 "Comparing iterators of different types");
736 }
737 else if constexpr (
738 std::is_same_v<T, typename std::span<const VariantType>::iterator>)
739 {
740 return arg <=> other_arg;
741 }
742 else if constexpr (
743 std::is_same_v<T, typename std::span<const UuidRefType>::iterator>)
744 {
745 return arg <=> other_arg;
746 }
747 else if constexpr (
748 std::is_same_v<
749 T,
750 std::pair<
751 typename std::span<const UuidType>::iterator, const RegistryT *>>)
752 {
753 return arg.first <=> other_arg.first;
754 }
755 else
756 {
757 return std::strong_ordering::equal;
758 }
759 },
760 it_var_, other.it_var_);
761 }
762
763 private:
764 std::variant<
765 typename std::span<const VariantType>::iterator,
766 typename std::span<const UuidRefType>::iterator,
767 std::pair<typename std::span<const UuidType>::iterator, const RegistryT *>>
768 it_var_;
769 };
770
772 explicit UuidIdentifiableObjectView (std::span<const VariantType> objects)
773 : objects_ (objects)
774 {
775 }
776
778 explicit UuidIdentifiableObjectView (std::span<const UuidRefType> refs)
779 : refs_ (refs)
780 {
781 }
782
785 const RegistryT &registry,
786 std::span<const UuidType> uuids)
787 : uuids_ (uuids), registry_ (&registry)
788 {
789 }
790
792 explicit UuidIdentifiableObjectView (const VariantType &obj)
793 : objects_ (std::span<const VariantType> (&obj, 1))
794 {
795 }
796
797 Iterator begin () const
798 {
799 if (objects_)
800 {
801 return Iterator (objects_->begin ());
802 }
803 else if (refs_)
804 {
805 return Iterator (refs_->begin ());
806 }
807 else
808 {
809 return Iterator (uuids_->begin (), registry_);
810 }
811 }
812
813 Iterator end () const
814 {
815 if (objects_)
816 {
817 return Iterator (objects_->end ());
818 }
819 else if (refs_)
820 {
821 return Iterator (refs_->end ());
822 }
823 else
824 {
825 return Iterator (uuids_->end (), registry_);
826 }
827 }
828
829 VariantType operator[] (size_t index) const
830 {
831 if (objects_)
832 return (*objects_)[index];
833 if (refs_)
834 return (*refs_)[index].get_object ();
835 return registry_->find_by_id_or_throw ((*uuids_)[index]);
836 }
837
838 VariantType front () const { return *begin (); }
839 VariantType back () const
840 {
841 if (empty ())
842 throw std::out_of_range ("Cannot get back() of empty view");
843
844 auto it = end ();
845 --it;
846 return *it;
847 }
848 VariantType at (size_t index) const
849 {
850 if (index >= size ())
851 throw std::out_of_range (
852 "UuidIdentifiableObjectView::at index out of range");
853
854 return (*this)[index];
855 }
856
857 size_t size () const
858 {
859 if (objects_)
860 return objects_->size ();
861 if (refs_)
862 return refs_->size ();
863 return uuids_->size ();
864 }
865
866 bool empty () const { return size () == 0; }
867
868 // Projections and transformations
869 static UuidType uuid_projection (const VariantType &var)
870 {
871 return std::visit ([] (const auto &obj) { return obj->get_uuid (); }, var);
872 }
873
874 template <typename T> auto get_elements_by_type () const
875 {
876 return *this | std::views::filter ([] (const auto &var) {
877 return std::holds_alternative<T *> (var);
878 }) | std::views::transform ([] (const auto &var) {
879 return std::get<T *> (var);
880 });
881 }
882
883 static RegistryT::BaseType * base_projection (const VariantType &var)
884 {
885 return std::visit (
886 [] (const auto &ptr) -> RegistryT::BaseType * { return ptr; }, var);
887 }
888
889 auto as_base_type () const
890 {
891 return std::views::transform (*this, base_projection);
892 }
893
894 template <typename T> static auto type_projection (const VariantType &var)
895 {
896 return std::holds_alternative<T *> (var);
897 }
898
899 template <typename T> bool contains_type () const
900 {
901 return std::ranges::any_of (*this, type_projection<T>);
902 }
903
904 template <typename BaseType>
905 static auto derived_from_type_projection (const VariantType &var)
906 {
907 return std::visit (
908 [] (const auto &ptr) {
909 return std::derived_from<base_type<decltype (ptr)>, BaseType>;
910 },
911 var);
912 }
913
918 template <typename T> static auto type_transformation (const VariantType &var)
919 {
920 return std::get<T *> (var);
921 }
922
923 template <typename T>
924 static auto derived_from_type_transformation (const VariantType &var)
925 {
926 return std::visit (
927 [] (const auto &ptr) -> T * {
928 using ElementT = base_type<decltype (ptr)>;
929 if constexpr (std::derived_from<ElementT, T>)
930 return ptr;
931 throw std::runtime_error ("Not derived from type");
932 },
933 var);
934 }
935
936 // note: assumes all objects are of this type
937 template <typename T> auto as_type () const
938 {
939 return std::views::transform (*this, type_transformation<T>);
940 }
941
942 template <typename T> auto get_elements_derived_from () const
943 {
944 return *this | std::views::filter (derived_from_type_projection<T>)
945 | std::views::transform (derived_from_type_transformation<T>);
946 }
947
948private:
949 std::optional<std::span<const VariantType>> objects_;
950 std::optional<std::span<const UuidRefType>> refs_;
951 std::optional<std::span<const UuidType>> uuids_;
952 const RegistryT * registry_ = nullptr;
953};
954
955} // namespace zrythm::utils
956
957// Concept to detect UuidIdentifiableObject::Uuid types
958template <typename T>
959concept UuidType = requires (T t) {
960 typename T::UuidTag;
961 { type_safe::get (t) } -> std::convertible_to<QUuid>;
962};
963
964// Formatter for any UUID type from UuidIdentifiableObject
965template <UuidType T>
966struct fmt::formatter<T> : fmt::formatter<std::string_view>
967{
968 template <typename FormatContext>
969 auto format (const T &uuid, FormatContext &ctx) const
970 {
971 return fmt::formatter<QString>{}.format (
972 type_safe::get (uuid).toString (QUuid::WithoutBraces), ctx);
973 }
974};
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.