27template <
typename Derived>
class UuidIdentifiableObject
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>
36 using type_safe::strong_typedef<Uuid, QUuid>::strong_typedef;
38 explicit Uuid () =
default;
47 bool is_null ()
const {
return type_safe::get (*this).isNull (); }
49 void set_null () { type_safe::get (*
this) = QUuid (); }
51 std::size_t hash ()
const {
return qHash (type_safe::get (*
this)); }
53 static_assert (std::regular<Uuid>);
54 static_assert (
sizeof (Uuid) ==
sizeof (QUuid));
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;
65 auto get_uuid ()
const {
return uuid_; }
67 friend void init_from (
68 UuidIdentifiableObject &obj,
69 const UuidIdentifiableObject &other,
74 obj.uuid_ =
Uuid (QUuid::createUuid ());
78 obj.uuid_ = other.uuid_;
82 friend void to_json (nlohmann::json &j,
const UuidIdentifiableObject &obj)
84 j[kUuidKey] = obj.uuid_;
86 friend void from_json (
const nlohmann::json &j, UuidIdentifiableObject &obj)
88 j.at (kUuidKey).get_to (obj.uuid_);
91 friend bool operator== (
92 const UuidIdentifiableObject &lhs,
93 const UuidIdentifiableObject &rhs)
95 return lhs.uuid_ == rhs.uuid_;
99 static constexpr std::string_view kUuidKey =
"id";
103 BOOST_DESCRIBE_CLASS (UuidIdentifiableObject, (), (), (), (uuid_))
137template <
typename RegistryT>
class UuidReference
140 using UuidType =
typename RegistryT::UuidType;
141 using VariantType =
typename RegistryT::VariantType;
144 UuidReference (RegistryT ®istry) : registry_ (registry) { }
146 UuidReference (
const UuidType &
id, RegistryT ®istry)
147 : id_ (
id), registry_ (registry)
152 UuidReference (
const UuidReference &other)
153 : UuidReference (other.get_registry ())
155 if (other.id_.has_value ())
157 set_id (other.id_.value ());
160 UuidReference &operator= (
const UuidReference &other)
165 registry_ = other.registry_;
171 UuidReference (UuidReference &&other)
172 : id_ (std::exchange (other.id_, std::nullopt)),
173 registry_ (std::exchange (other.registry_, std::nullopt))
179 UuidReference &operator= (UuidReference &&other)
187 id_ = std::exchange (other.id_, std::nullopt);
188 registry_ = std::exchange (other.registry_, std::nullopt);
195 ~UuidReference () { release_ref (); }
197 auto id ()
const ->
UuidType {
return *id_; }
208 if (id_.has_value ())
210 throw std::runtime_error (
211 "Cannot set id of UuidReference that already has an id");
217 VariantType get_object ()
const
219 return get_registry ().find_by_id_or_throw (
id ());
223 template <
typename ObjectT>
224 ObjectT * get_object_as () const
227 return std::get<ObjectT *> (get_object ());
230 RegistryT::BaseType * get_object_base ()
const
232 return get_registry ().find_by_id_as_base_or_throw (
id ());
235 template <
typename... Args>
236 UuidReference clone_new_identity (Args &&... args)
const
240 return get_registry ().clone_object (*obj, std::forward<Args> (args)...);
245 auto get_iterator_in_registry ()
const
247 return get_registry ().get_iterator_for_id (
id ());
250 auto get_iterator_in_registry ()
252 return get_registry ().get_iterator_for_id (
id ());
258 if (id_.has_value ())
260 get_registry ().acquire_reference (*id_);
265 if (id_.has_value ())
267 get_registry ().release_reference (*id_);
270 RegistryT &get_registry ()
const
272 return const_cast<RegistryT &
> (*registry_);
274 RegistryT &get_registry () {
return *registry_; }
276 friend void to_json (nlohmann::json &j,
const UuidReference &ref)
278 assert (ref.id_.has_value ());
281 friend void from_json (
const nlohmann::json &j, UuidReference &ref)
289 friend bool operator== (
const UuidReference &lhs,
const UuidReference &rhs)
291 return lhs.id () == rhs.id ();
295 std::optional<UuidType> id_;
296 OptionalRef<RegistryT> registry_;
321class OwningObjectRegistry :
public QObject
325 using VariantType = VariantT;
326 using BaseType = BaseT;
328 OwningObjectRegistry (QObject * parent =
nullptr)
329 : QObject (parent), main_thread_id_ (current_thread_id)
333 ~OwningObjectRegistry ()
350 template <
typename CreateType,
typename... Args>
352 requires std::derived_from<CreateType, BaseT>
354 assert (is_main_thread ());
356 auto * obj =
new CreateType (std::forward<Args> (args)...);
358 "created object of type {} with ID {}",
typeid (CreateType).name (),
361 return { obj->get_uuid (), *
this };
365 template <
typename CreateType,
typename... Args>
366 auto clone_object (
const CreateType &other, Args &&... args)
368 requires std::derived_from<CreateType, BaseT>
370 assert (is_main_thread ());
372 CreateType * obj = clone_raw_ptr (
375 return { obj->get_uuid (), *
this };
378 [[gnu::hot]]
auto get_iterator_for_id (
const UuidType &
id)
const
383 return objects_by_id_.find (
id);
386 [[gnu::hot]]
auto get_iterator_for_id (
const UuidType &
id)
390 return objects_by_id_.find (
id);
398 [[gnu::hot]] std::optional<VariantT>
401 const auto it = get_iterator_for_id (
id);
402 if (it == objects_by_id_.end ())
409 [[gnu::hot]]
auto find_by_id_or_throw (
const UuidType &
id)
const
412 if (!val.has_value ())
414 throw std::runtime_error (
415 fmt::format (
"Object with id {} not found",
id));
420 BaseT * find_by_id_as_base_or_throw (
const UuidType &
id)
const
422 auto var = find_by_id_or_throw (
id);
423 return std::visit ([] (
auto &&val) -> BaseT * {
return val; }, var);
426 bool contains (
const UuidType &
id)
const
430 return objects_by_id_.contains (
id);
440 assert (is_main_thread ());
444 if (contains (obj->get_uuid ()))
446 throw std::runtime_error (
447 fmt::format (
"Object with id {} already exists", obj->get_uuid ()));
449 z_trace (
"Registering (inserting) object {}", obj->get_uuid ());
450 obj->setParent (
this);
451 objects_by_id_.emplace (obj->get_uuid (), obj);
456 template <
typename ObjectT>
458 requires std::derived_from<ObjectT, BaseT>
470 assert (is_main_thread ());
472 return ref_counts_.at (
id);
475 void acquire_reference (
const UuidType &
id)
477 assert (is_main_thread ());
482 void release_reference (
const UuidType &
id)
484 assert (is_main_thread ());
489 if (--ref_counts_[
id] <= 0)
491 delete_object_by_id (
id);
495 auto &get_hash_map ()
const {
return objects_by_id_; }
502 assert (is_main_thread ());
504 return objects_by_id_ | std::views::keys | std::ranges::to<std::vector> ();
509 assert (is_main_thread ());
511 return objects_by_id_.size ();
514 friend void to_json (nlohmann::json &j,
const OwningObjectRegistry &obj)
516 j = nlohmann::json::array ();
517 for (
const auto &var : obj.objects_by_id_ | std::views::values)
522 template <ObjectBuilder BuilderT>
523 friend void from_json_with_builder (
524 const nlohmann::json &j,
525 OwningObjectRegistry &obj,
526 const BuilderT &builder)
531 std::vector<std::pair<VariantT, nlohmann::json>> deferred;
532 deferred.reserve (j.size ());
533 for (
const auto &object_var_json : j)
536 utils::serialization::variant_create_object_only (
537 object_var_json, object_var, builder);
542 [&object_var_json] (
auto &&ptr) {
545 static_cast<utils::UuidIdentifiableObject<BaseT> &
> (*ptr));
549 obj.register_object (object_var);
550 deferred.emplace_back (object_var, object_var_json);
555 for (
auto &[object_var, json] : deferred)
557 utils::serialization::variant_deserialize_data (json, object_var);
568 VariantT unregister_object (
const UuidType &
id)
570 if (!objects_by_id_.contains (
id))
572 throw std::runtime_error (
573 fmt::format (
"Object with id {} not found",
id));
576 z_trace (
"Unregistering object with id {}",
id);
578 auto obj_it = get_iterator_for_id (
id);
579 auto obj_var = obj_it->second;
580 objects_by_id_.erase (obj_it);
581 std::visit ([&] (
auto &&obj) { obj->setParent (
nullptr); }, obj_var);
582 ref_counts_.erase (
id);
586 void delete_object_by_id (
const UuidType &
id)
588 auto obj_var = unregister_object (
id);
589 std::visit ([&] (
auto &&obj) {
delete obj; }, obj_var);
592 bool is_main_thread ()
const {
return main_thread_id_ == current_thread_id; }
600 boost::unordered::unordered_flat_map<UuidType, VariantT> objects_by_id_;
607 boost::unordered::unordered_flat_map<UuidType, int> ref_counts_;
609 RTThreadId main_thread_id_;
618 bool destroying_ =
false;
629 :
public std::ranges::view_interface<UuidIdentifiableObjectView<RegistryT>>
632 using UuidType =
typename RegistryT::UuidType;
633 using VariantType =
typename RegistryT::VariantType;
638 :
public boost::stl_interfaces::proxy_iterator_interface<
639#if !BOOST_STL_INTERFACES_USE_DEDUCED_THIS
642 std::random_access_iterator_tag,
646 using base_type = boost::stl_interfaces::proxy_iterator_interface<
647#if !BOOST_STL_INTERFACES_USE_DEDUCED_THIS
650 std::random_access_iterator_tag,
652 using difference_type = base_type::difference_type;
654 constexpr Iterator ()
noexcept =
default;
657 constexpr Iterator (std::span<const VariantType>::iterator it) noexcept
663 constexpr Iterator (std::span<const UuidRefType>::iterator it) noexcept
670 std::span<const UuidType>::iterator it,
671 const RegistryT * registry) noexcept
672 : it_var_ (std::pair{ it, registry })
676 constexpr VariantType operator* ()
const
680 using T = std::decay_t<
decltype (arg)>;
682 std::is_same_v<T, typename std::span<const VariantType>::iterator>)
687 std::is_same_v<T, typename std::span<const UuidRefType>::iterator>)
689 return arg->get_object ();
695 typename std::span<const UuidType>::iterator,
const RegistryT *>>)
697 return arg.second->find_by_id_or_throw (*arg.first);
703 constexpr Iterator &operator+= (difference_type n)
noexcept
707 using T = std::decay_t<
decltype (arg)>;
709 std::is_same_v<T, typename std::span<const VariantType>::iterator>)
714 std::is_same_v<T, typename std::span<const UuidRefType>::iterator>)
722 typename std::span<const UuidType>::iterator,
const RegistryT *>>)
731 constexpr difference_type operator- (Iterator other)
const
734 [] (
auto &&arg,
auto &&other_arg) -> difference_type {
735 using T = std::decay_t<
decltype (arg)>;
736 using OtherT = std::decay_t<
decltype (other_arg)>;
737 if constexpr (!std::is_same_v<T, OtherT>)
739 throw std::runtime_error (
740 "Comparing iterators of different types");
743 std::is_same_v<T, typename std::span<const VariantType>::iterator>)
745 return arg - other_arg;
748 std::is_same_v<T, typename std::span<const UuidRefType>::iterator>)
750 return arg - other_arg;
756 typename std::span<const UuidType>::iterator,
const RegistryT *>>)
758 return arg.first - other_arg.first;
765 it_var_, other.it_var_);
768 constexpr auto operator<=> (
const Iterator &other)
const
771 [] (
auto &&arg,
auto &&other_arg) -> std::strong_ordering {
772 using T = std::decay_t<
decltype (arg)>;
773 using OtherT = std::decay_t<
decltype (other_arg)>;
774 if constexpr (!std::is_same_v<T, OtherT>)
776 throw std::runtime_error (
777 "Comparing iterators of different types");
780 std::is_same_v<T, typename std::span<const VariantType>::iterator>)
782 return arg <=> other_arg;
785 std::is_same_v<T, typename std::span<const UuidRefType>::iterator>)
787 return arg <=> other_arg;
793 typename std::span<const UuidType>::iterator,
const RegistryT *>>)
795 return arg.first <=> other_arg.first;
799 return std::strong_ordering::equal;
802 it_var_, other.it_var_);
807 typename std::span<const VariantType>::iterator,
808 typename std::span<const UuidRefType>::iterator,
809 std::pair<typename std::span<const UuidType>::iterator,
const RegistryT *>>
827 const RegistryT ®istry,
828 std::span<const UuidType> uuids)
829 : uuids_ (uuids), registry_ (®istry)
835 : objects_ (std::span<const VariantType> (&obj, 1))
839 Iterator begin ()
const
843 return Iterator (objects_->begin ());
847 return Iterator (refs_->begin ());
851 return Iterator (uuids_->begin (), registry_);
867 return Iterator (uuids_->end (), registry_);
871 VariantType operator[] (
size_t index)
const
874 return (*objects_)[index];
876 return (*refs_)[index].get_object ();
877 return registry_->find_by_id_or_throw ((*uuids_)[index]);
880 VariantType front ()
const {
return *begin (); }
881 VariantType back ()
const
884 throw std::out_of_range (
"Cannot get back() of empty view");
890 VariantType at (
size_t index)
const
892 if (index >= size ())
893 throw std::out_of_range (
894 "UuidIdentifiableObjectView::at index out of range");
896 return (*
this)[index];
902 return objects_->size ();
904 return refs_->size ();
905 return uuids_->size ();
908 bool empty ()
const {
return size () == 0; }
911 static UuidType uuid_projection (
const VariantType &var)
913 return std::visit ([] (
const auto &obj) {
return obj->get_uuid (); }, var);
916 template <
typename T>
auto get_elements_by_type ()
const
918 return *
this | std::views::filter ([] (
const auto &var) {
919 return std::holds_alternative<T *> (var);
920 }) | std::views::transform ([] (
const auto &var) {
921 return std::get<T *> (var);
925 static RegistryT::BaseType * base_projection (
const VariantType &var)
928 [] (
const auto &ptr) -> RegistryT::BaseType * {
return ptr; }, var);
931 auto as_base_type ()
const
933 return std::views::transform (*
this, base_projection);
936 template <
typename T>
static auto type_projection (
const VariantType &var)
938 return std::holds_alternative<T *> (var);
941 template <
typename T>
bool contains_type ()
const
943 return std::ranges::any_of (*
this, type_projection<T>);
946 template <
typename BaseType>
947 static auto derived_from_type_projection (
const VariantType &var)
950 [] (
const auto &ptr) {
951 return std::derived_from<base_type<
decltype (ptr)>, BaseType>;
962 return std::get<T *> (var);
965 template <
typename T>
966 static auto derived_from_type_transformation (
const VariantType &var)
969 [] (
const auto &ptr) -> T * {
970 using ElementT = base_type<
decltype (ptr)>;
971 if constexpr (std::derived_from<ElementT, T>)
973 throw std::runtime_error (
"Not derived from type");
979 template <
typename T>
auto as_type ()
const
984 template <
typename T>
auto get_elements_derived_from ()
const
986 return *
this | std::views::filter (derived_from_type_projection<T>)
987 | std::views::transform (derived_from_type_transformation<T>);
991 std::optional<std::span<const VariantType>> objects_;
992 std::optional<std::span<const UuidRefType>> refs_;
993 std::optional<std::span<const UuidType>> uuids_;
994 const RegistryT * registry_ =
nullptr;