28template <
typename Derived>
class UuidIdentifiableObject
32 : type_safe::strong_typedef<Uuid, QUuid>,
33 type_safe::strong_typedef_op::equality_comparison<Uuid>,
34 type_safe::strong_typedef_op::relational_comparison<Uuid>
37 using type_safe::strong_typedef<Uuid, QUuid>::strong_typedef;
39 explicit Uuid () =
default;
48 bool is_null ()
const {
return type_safe::get (*this).isNull (); }
50 void set_null () { type_safe::get (*
this) = QUuid (); }
52 std::size_t hash ()
const {
return qHash (type_safe::get (*
this)); }
54 static_assert (std::regular<Uuid>);
55 static_assert (
sizeof (Uuid) ==
sizeof (QUuid));
57 UuidIdentifiableObject () : uuid_ (Uuid (QUuid::createUuid ())) { }
58 UuidIdentifiableObject (
const Uuid &
id) : uuid_ (id) { }
59 UuidIdentifiableObject (
const UuidIdentifiableObject &other) =
default;
60 UuidIdentifiableObject &
61 operator= (
const UuidIdentifiableObject &other) =
default;
62 UuidIdentifiableObject (UuidIdentifiableObject &&other) =
default;
63 UuidIdentifiableObject &operator= (UuidIdentifiableObject &&other) =
default;
64 virtual ~UuidIdentifiableObject () =
default;
66 auto get_uuid ()
const {
return uuid_; }
68 friend void init_from (
69 UuidIdentifiableObject &obj,
70 const UuidIdentifiableObject &other,
75 obj.uuid_ =
Uuid (QUuid::createUuid ());
79 obj.uuid_ = other.uuid_;
83 friend void to_json (nlohmann::json &j,
const UuidIdentifiableObject &obj)
85 j[kUuidKey] = obj.uuid_;
87 friend void from_json (
const nlohmann::json &j, UuidIdentifiableObject &obj)
89 j.at (kUuidKey).get_to (obj.uuid_);
92 friend bool operator== (
93 const UuidIdentifiableObject &lhs,
94 const UuidIdentifiableObject &rhs)
96 return lhs.uuid_ == rhs.uuid_;
100 static constexpr std::string_view kUuidKey =
"id";
104 BOOST_DESCRIBE_CLASS (UuidIdentifiableObject, (), (), (), (uuid_))
138template <
typename RegistryT>
class UuidReference
141 using UuidType =
typename RegistryT::UuidType;
142 using VariantType =
typename RegistryT::VariantType;
145 UuidReference (RegistryT ®istry) : registry_ (registry) { }
147 UuidReference (
const UuidType &
id, RegistryT ®istry)
148 : id_ (
id), registry_ (registry)
153 UuidReference (
const UuidReference &other)
154 : UuidReference (other.get_registry ())
156 if (other.id_.has_value ())
158 set_id (other.id_.value ());
161 UuidReference &operator= (
const UuidReference &other)
166 registry_ = other.registry_;
172 UuidReference (UuidReference &&other)
173 : id_ (std::exchange (other.id_, std::nullopt)),
174 registry_ (std::exchange (other.registry_, std::nullopt))
180 UuidReference &operator= (UuidReference &&other)
188 id_ = std::exchange (other.id_, std::nullopt);
189 registry_ = std::exchange (other.registry_, std::nullopt);
196 ~UuidReference () { release_ref (); }
198 auto id ()
const ->
UuidType {
return *id_; }
209 if (id_.has_value ())
211 throw std::runtime_error (
212 "Cannot set id of UuidReference that already has an id");
218 VariantType get_object ()
const
220 return get_registry ().find_by_id_or_throw (
id ());
224 template <
typename ObjectT>
225 ObjectT * get_object_as () const
228 return std::get<ObjectT *> (get_object ());
231 RegistryT::BaseType * get_object_base ()
const
233 return get_registry ().find_by_id_as_base_or_throw (
id ());
236 template <
typename... Args>
237 UuidReference clone_new_identity (Args &&... args)
const
241 return get_registry ().clone_object (*obj, std::forward<Args> (args)...);
246 auto get_iterator_in_registry ()
const
248 return get_registry ().get_iterator_for_id (
id ());
251 auto get_iterator_in_registry ()
253 return get_registry ().get_iterator_for_id (
id ());
259 if (id_.has_value ())
261 get_registry ().acquire_reference (*id_);
266 if (id_.has_value ())
268 get_registry ().release_reference (*id_);
271 RegistryT &get_registry ()
const
273 return const_cast<RegistryT &
> (*registry_);
275 RegistryT &get_registry () {
return *registry_; }
277 friend void to_json (nlohmann::json &j,
const UuidReference &ref)
279 assert (ref.id_.has_value ());
282 friend void from_json (
const nlohmann::json &j, UuidReference &ref)
290 friend bool operator== (
const UuidReference &lhs,
const UuidReference &rhs)
292 return lhs.id () == rhs.id ();
296 std::optional<UuidType> id_;
297 utils::OptionalRef<RegistryT> registry_;
322class OwningObjectRegistry :
public QObject
326 using VariantType = VariantT;
327 using BaseType = BaseT;
329 OwningObjectRegistry (QObject * parent =
nullptr)
330 : QObject (parent), main_thread_id_ (current_thread_id)
334 ~OwningObjectRegistry ()
351 template <
typename CreateType,
typename... Args>
353 requires std::derived_from<CreateType, BaseT>
355 assert (is_main_thread ());
357 auto * obj =
new CreateType (std::forward<Args> (args)...);
359 "created object of type {} with ID {}",
typeid (CreateType).name (),
362 return { obj->get_uuid (), *
this };
366 template <
typename CreateType,
typename... Args>
367 auto clone_object (
const CreateType &other, Args &&... args)
369 requires std::derived_from<CreateType, BaseT>
371 assert (is_main_thread ());
373 CreateType * obj = clone_raw_ptr (
376 return { obj->get_uuid (), *
this };
379 [[gnu::hot]]
auto get_iterator_for_id (
const UuidType &
id)
const
384 return objects_by_id_.find (
id);
387 [[gnu::hot]]
auto get_iterator_for_id (
const UuidType &
id)
391 return objects_by_id_.find (
id);
399 [[gnu::hot]] std::optional<VariantT>
402 const auto it = get_iterator_for_id (
id);
403 if (it == objects_by_id_.end ())
410 [[gnu::hot]]
auto find_by_id_or_throw (
const UuidType &
id)
const
413 if (!val.has_value ())
415 throw std::runtime_error (
416 fmt::format (
"Object with id {} not found",
id));
421 BaseT * find_by_id_as_base_or_throw (
const UuidType &
id)
const
423 auto var = find_by_id_or_throw (
id);
424 return std::visit ([] (
auto &&val) -> BaseT * {
return val; }, var);
427 bool contains (
const UuidType &
id)
const
431 return objects_by_id_.contains (
id);
441 assert (is_main_thread ());
445 if (contains (obj->get_uuid ()))
447 throw std::runtime_error (
448 fmt::format (
"Object with id {} already exists", obj->get_uuid ()));
450 z_trace (
"Registering (inserting) object {}", obj->get_uuid ());
451 obj->setParent (
this);
452 objects_by_id_.emplace (obj->get_uuid (), obj);
457 template <
typename ObjectT>
459 requires std::derived_from<ObjectT, BaseT>
471 assert (is_main_thread ());
473 return ref_counts_.at (
id);
476 void acquire_reference (
const UuidType &
id)
478 assert (is_main_thread ());
483 void release_reference (
const UuidType &
id)
485 assert (is_main_thread ());
490 if (--ref_counts_[
id] <= 0)
492 delete_object_by_id (
id);
496 auto &get_hash_map ()
const {
return objects_by_id_; }
503 assert (is_main_thread ());
505 return objects_by_id_ | std::views::keys | std::ranges::to<std::vector> ();
510 assert (is_main_thread ());
512 return objects_by_id_.size ();
515 friend void to_json (nlohmann::json &j,
const OwningObjectRegistry &obj)
517 j = nlohmann::json::array ();
518 for (
const auto &var : obj.objects_by_id_ | std::views::values)
523 template <ObjectBuilder BuilderT>
524 friend void from_json_with_builder (
525 const nlohmann::json &j,
526 OwningObjectRegistry &obj,
527 const BuilderT &builder)
532 std::vector<std::pair<VariantT, nlohmann::json>> deferred;
533 deferred.reserve (j.size ());
534 for (
const auto &object_var_json : j)
537 utils::serialization::variant_create_object_only (
538 object_var_json, object_var, builder);
543 [&object_var_json] (
auto &&ptr) {
546 static_cast<utils::UuidIdentifiableObject<BaseT> &
> (*ptr));
550 obj.register_object (object_var);
551 deferred.emplace_back (object_var, object_var_json);
556 for (
auto &[object_var, json] : deferred)
558 utils::serialization::variant_deserialize_data (json, object_var);
569 VariantT unregister_object (
const UuidType &
id)
571 if (!objects_by_id_.contains (
id))
573 throw std::runtime_error (
574 fmt::format (
"Object with id {} not found",
id));
577 z_trace (
"Unregistering object with id {}",
id);
579 auto obj_it = get_iterator_for_id (
id);
580 auto obj_var = obj_it->second;
581 objects_by_id_.erase (obj_it);
582 std::visit ([&] (
auto &&obj) { obj->setParent (
nullptr); }, obj_var);
583 ref_counts_.erase (
id);
587 void delete_object_by_id (
const UuidType &
id)
589 auto obj_var = unregister_object (
id);
590 std::visit ([&] (
auto &&obj) {
delete obj; }, obj_var);
593 bool is_main_thread ()
const {
return main_thread_id_ == current_thread_id; }
601 boost::unordered::unordered_flat_map<UuidType, VariantT> objects_by_id_;
608 boost::unordered::unordered_flat_map<UuidType, int> ref_counts_;
610 RTThreadId main_thread_id_;
619 bool destroying_ =
false;
630 :
public std::ranges::view_interface<UuidIdentifiableObjectView<RegistryT>>
633 using UuidType =
typename RegistryT::UuidType;
634 using VariantType =
typename RegistryT::VariantType;
639 :
public boost::stl_interfaces::proxy_iterator_interface<
640#if !BOOST_STL_INTERFACES_USE_DEDUCED_THIS
643 std::random_access_iterator_tag,
647 using base_type = boost::stl_interfaces::proxy_iterator_interface<
648#if !BOOST_STL_INTERFACES_USE_DEDUCED_THIS
651 std::random_access_iterator_tag,
653 using difference_type = base_type::difference_type;
655 constexpr Iterator ()
noexcept =
default;
658 constexpr Iterator (std::span<const VariantType>::iterator it) noexcept
664 constexpr Iterator (std::span<const UuidRefType>::iterator it) noexcept
671 std::span<const UuidType>::iterator it,
672 const RegistryT * registry) noexcept
673 : it_var_ (std::pair{ it, registry })
677 constexpr VariantType operator* ()
const
681 using T = std::decay_t<
decltype (arg)>;
683 std::is_same_v<T, typename std::span<const VariantType>::iterator>)
688 std::is_same_v<T, typename std::span<const UuidRefType>::iterator>)
690 return arg->get_object ();
696 typename std::span<const UuidType>::iterator,
const RegistryT *>>)
698 return arg.second->find_by_id_or_throw (*arg.first);
704 constexpr Iterator &operator+= (difference_type n)
noexcept
708 using T = std::decay_t<
decltype (arg)>;
710 std::is_same_v<T, typename std::span<const VariantType>::iterator>)
715 std::is_same_v<T, typename std::span<const UuidRefType>::iterator>)
723 typename std::span<const UuidType>::iterator,
const RegistryT *>>)
732 constexpr difference_type operator- (Iterator other)
const
735 [] (
auto &&arg,
auto &&other_arg) -> difference_type {
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>)
740 throw std::runtime_error (
741 "Comparing iterators of different types");
744 std::is_same_v<T, typename std::span<const VariantType>::iterator>)
746 return arg - other_arg;
749 std::is_same_v<T, typename std::span<const UuidRefType>::iterator>)
751 return arg - other_arg;
757 typename std::span<const UuidType>::iterator,
const RegistryT *>>)
759 return arg.first - other_arg.first;
766 it_var_, other.it_var_);
769 constexpr auto operator<=> (
const Iterator &other)
const
772 [] (
auto &&arg,
auto &&other_arg) -> std::strong_ordering {
773 using T = std::decay_t<
decltype (arg)>;
774 using OtherT = std::decay_t<
decltype (other_arg)>;
775 if constexpr (!std::is_same_v<T, OtherT>)
777 throw std::runtime_error (
778 "Comparing iterators of different types");
781 std::is_same_v<T, typename std::span<const VariantType>::iterator>)
783 return arg <=> other_arg;
786 std::is_same_v<T, typename std::span<const UuidRefType>::iterator>)
788 return arg <=> other_arg;
794 typename std::span<const UuidType>::iterator,
const RegistryT *>>)
796 return arg.first <=> other_arg.first;
800 return std::strong_ordering::equal;
803 it_var_, other.it_var_);
808 typename std::span<const VariantType>::iterator,
809 typename std::span<const UuidRefType>::iterator,
810 std::pair<typename std::span<const UuidType>::iterator,
const RegistryT *>>
828 const RegistryT ®istry,
829 std::span<const UuidType> uuids)
830 : uuids_ (uuids), registry_ (®istry)
836 : objects_ (std::span<const VariantType> (&obj, 1))
840 Iterator begin ()
const
844 return Iterator (objects_->begin ());
848 return Iterator (refs_->begin ());
852 return Iterator (uuids_->begin (), registry_);
868 return Iterator (uuids_->end (), registry_);
872 VariantType operator[] (
size_t index)
const
875 return (*objects_)[index];
877 return (*refs_)[index].get_object ();
878 return registry_->find_by_id_or_throw ((*uuids_)[index]);
881 VariantType front ()
const {
return *begin (); }
882 VariantType back ()
const
885 throw std::out_of_range (
"Cannot get back() of empty view");
891 VariantType at (
size_t index)
const
893 if (index >= size ())
894 throw std::out_of_range (
895 "UuidIdentifiableObjectView::at index out of range");
897 return (*
this)[index];
903 return objects_->size ();
905 return refs_->size ();
906 return uuids_->size ();
909 bool empty ()
const {
return size () == 0; }
912 static UuidType uuid_projection (
const VariantType &var)
914 return std::visit ([] (
const auto &obj) {
return obj->get_uuid (); }, var);
917 template <
typename T>
auto get_elements_by_type ()
const
919 return *
this | std::views::filter ([] (
const auto &var) {
920 return std::holds_alternative<T *> (var);
921 }) | std::views::transform ([] (
const auto &var) {
922 return std::get<T *> (var);
926 static RegistryT::BaseType * base_projection (
const VariantType &var)
929 [] (
const auto &ptr) -> RegistryT::BaseType * {
return ptr; }, var);
932 auto as_base_type ()
const
934 return std::views::transform (*
this, base_projection);
937 template <
typename T>
static auto type_projection (
const VariantType &var)
939 return std::holds_alternative<T *> (var);
942 template <
typename T>
bool contains_type ()
const
944 return std::ranges::any_of (*
this, type_projection<T>);
947 template <
typename BaseType>
948 static auto derived_from_type_projection (
const VariantType &var)
951 [] (
const auto &ptr) {
952 return std::derived_from<base_type<
decltype (ptr)>, BaseType>;
963 return std::get<T *> (var);
966 template <
typename T>
967 static auto derived_from_type_transformation (
const VariantType &var)
970 [] (
const auto &ptr) -> T * {
971 using ElementT = base_type<
decltype (ptr)>;
972 if constexpr (std::derived_from<ElementT, T>)
974 throw std::runtime_error (
"Not derived from type");
980 template <
typename T>
auto as_type ()
const
985 template <
typename T>
auto get_elements_derived_from ()
const
987 return *
this | std::views::filter (derived_from_type_projection<T>)
988 | std::views::transform (derived_from_type_transformation<T>);
992 std::optional<std::span<const VariantType>> objects_;
993 std::optional<std::span<const UuidRefType>> refs_;
994 std::optional<std::span<const UuidType>> uuids_;
995 const RegistryT * registry_ =
nullptr;