HikoGUI
A low latency retained GUI
Loading...
Searching...
No Matches
group_ptr.hpp
1// Copyright Take Vos 2022.
2// Distributed under the Boost Software License, Version 1.0.
3// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)
4
5#pragma once
6
7#include "../utility/utility.hpp"
8#include "../concurrency/concurrency.hpp"
9#include "../concurrency/unfair_mutex.hpp" // XXX #616
10#include "../macros.hpp"
11#include <memory>
12#include <vector>
13#include <functional>
14#include <tuple>
15#include <mutex>
16
17hi_export_module(hikogui.observer.group_ptr);
18
19
20hi_export namespace hi::inline v1 {
21
22template<typename>
23class group_ptr;
24
39template<typename T, typename... Proto>
41
42template<typename T, typename... Args>
43class enable_group_ptr<T, void(Args...)> {
44public:
45#ifndef NDEBUG
47 {
48 hi_assert(_enable_group_ptr_owners.empty());
49 }
50#endif
51
56 void notify_group_ptr(Args const&...args) const noexcept
57 {
58 auto const owners_copy = [&] {
59 auto const lock = std::scoped_lock(_enable_group_ptr_mutex);
60 return _enable_group_ptr_owners;
61 }();
62
63 for (auto owner : owners_copy) {
64 hi_assert_not_null(owner);
65 if (owner->_notify) {
66 owner->_notify(args...);
67 }
68 }
69 }
70
71private:
72 using _enable_group_ptr_notify_proto = void(Args...);
73
74 std::vector<group_ptr<T> *> _enable_group_ptr_owners;
75 mutable unfair_mutex _enable_group_ptr_mutex;
76
77 [[nodiscard]] bool _enable_group_ptr_holds_invariant() const noexcept
78 {
79 hi_axiom(_enable_group_ptr_mutex.is_locked());
80
81 for (auto owner : _enable_group_ptr_owners) {
82 if (owner == nullptr or owner->_ptr == nullptr or owner->_ptr.get() != this or
83 owner->_ptr.use_count() < _enable_group_ptr_owners.size()) {
84 return false;
85 }
86 }
87 return true;
88 }
89
90 void _enable_group_ptr_add_owner(group_ptr<T> *owner) noexcept
91 {
92 auto const lock = std::scoped_lock(_enable_group_ptr_mutex);
93
94 _enable_group_ptr_owners.push_back(owner);
95 hi_axiom(_enable_group_ptr_holds_invariant());
96 }
97
98 void _enable_group_ptr_remove_owner(group_ptr<T> *owner) noexcept
99 {
100 auto const lock = std::scoped_lock(_enable_group_ptr_mutex);
101
102 auto const num_removed = std::erase(_enable_group_ptr_owners, owner);
103 hi_assert(num_removed == 1);
104 hi_axiom(_enable_group_ptr_holds_invariant());
105 }
106
113 void _enable_group_ptr_reseat(std::shared_ptr<enable_group_ptr> const& replacement) noexcept
114 {
115 auto const lock = std::scoped_lock(_enable_group_ptr_mutex);
116
117 hi_assert_not_null(replacement);
118 hi_assert(replacement.get() != this);
119
120 while (not _enable_group_ptr_owners.empty()) {
121 auto *owner = _enable_group_ptr_owners.back();
122 _enable_group_ptr_owners.pop_back();
123 owner->_ptr = replacement;
124 owner->_ptr->_enable_group_ptr_add_owner(owner);
125 }
126 hi_axiom(_enable_group_ptr_holds_invariant());
127 }
128
129 friend class group_ptr<T>;
130};
131
132template<typename T>
133class enable_group_ptr<T> : public enable_group_ptr<T, void()> {
134};
135
148template<typename T>
150public:
151 using notify_proto = T::_enable_group_ptr_notify_proto;
152 using element_type = T;
153
156 virtual ~group_ptr()
157 {
158 if (_ptr) {
159 _ptr->_enable_group_ptr_remove_owner(this);
160 }
161 }
162
172 group_ptr(group_ptr const& other) noexcept : _ptr(other._ptr)
173 {
174 if (_ptr) {
175 _ptr->_enable_group_ptr_add_owner(this);
176 }
177 }
178
194 group_ptr& operator=(group_ptr const& other) noexcept
195 {
196 if (_ptr == other._ptr) {
197 return *this;
198
199 } else if (_ptr and other._ptr) {
200 // Make copy because reseat() will overwrite _ptr.
201 auto tmp = _ptr;
202 tmp->_enable_group_ptr_reseat(other._ptr);
203 return *this;
204
205 } else if (_ptr) {
206 _ptr->_enable_group_ptr_remove_owner(this);
207 _ptr = nullptr;
208 return *this;
209
210 } else {
211 _ptr = other._ptr;
212 _ptr->_enable_group_ptr_add_owner(this);
213 return *this;
214 }
215 }
216
227 group_ptr(group_ptr&& other) noexcept : _ptr(std::move(other._ptr))
228 {
229 if (_ptr) {
230 _ptr->_enable_group_ptr_remove_owner(&other);
231 _ptr->_enable_group_ptr_add_owner(this);
232 }
233 }
234
251 group_ptr& operator=(group_ptr&& other) noexcept
252 {
253 hi_return_on_self_assignment(other);
254
255 if (_ptr == other._ptr) {
256 other._ptr->_enable_group_ptr_remove_owner(&other);
257 other._ptr = nullptr;
258 return *this;
259
260 } else if (_ptr and other._ptr) {
261 other._ptr->_enable_group_ptr_remove_owner(&other);
262 // Make copy because reseat() will overwrite _ptr.
263 auto tmp = _ptr;
264 tmp->_enable_group_ptr_reseat(std::exchange(other._ptr, nullptr));
265 return *this;
266
267 } else if (_ptr) {
268 _ptr->_enable_group_ptr_remove_owner(this);
269 _ptr = nullptr;
270 return *this;
271
272 } else {
273 other._ptr->_enable_group_ptr_remove_owner(&other);
274 _ptr = std::exchange(other._ptr, nullptr);
275 _ptr->_enable_group_ptr_add_owner(this);
276 return *this;
277 }
278 }
279
282 constexpr group_ptr() noexcept = default;
283
286 constexpr group_ptr(std::nullptr_t) noexcept {}
287
294 void reset() noexcept
295 {
296 if (_ptr) {
297 _ptr->_enable_group_ptr_remove_owner(this);
298 _ptr = nullptr;
299 }
300 }
301
309 template<forward_of<std::shared_ptr<enable_group_ptr<T, notify_proto>>> Ptr>
310 group_ptr(Ptr&& ptr) noexcept : _ptr(std::forward<Ptr>(ptr))
311 {
312 if (_ptr) {
313 _ptr->_enable_group_ptr_add_owner(this);
314 }
315 }
316
325 template<forward_of<std::shared_ptr<enable_group_ptr<T, notify_proto>>> Ptr>
326 group_ptr& operator=(Ptr&& ptr) noexcept
327 {
328 return *this = group_ptr{std::forward<Ptr>(ptr)};
329 }
330
336 [[nodiscard]] T *get() const noexcept
337 {
338 return static_cast<T *>(_ptr.get());
339 }
340
346 [[nodiscard]] T *operator->() const noexcept
347 {
348 return get();
349 }
350
356 [[nodiscard]] T& operator*() const noexcept
357 {
358 return *get();
359 }
360
366 explicit operator bool() const noexcept
367 {
368 return to_bool(_ptr);
369 }
370
381 template<forward_of<notify_proto> Func>
382 void subscribe(Func&& func) noexcept
383 {
384 _notify = std::forward<Func>(func);
385 }
386
389 void unsubscribe() noexcept
390 {
391 _notify = {};
392 }
393
394private:
396 std::function<notify_proto> _notify = {};
397
398 friend class enable_group_ptr<T, notify_proto>;
399};
400
401template<typename Context, typename Expected>
402struct is_forward_of<Context, group_ptr<Expected>> :
403 std::conditional_t<std::is_convertible_v<Context, group_ptr<Expected>>, std::true_type, std::false_type> {
404};
405
406} // namespace hi::inline v1
Definition of the unfair_mutex.
STL namespace.
DOXYGEN BUG.
Definition algorithm_misc.hpp:20
A smart pointer which manages ownership as a group.
Definition group_ptr.hpp:149
group_ptr & operator=(group_ptr &&other) noexcept
Move assign from another group_ptr.
Definition group_ptr.hpp:251
group_ptr & operator=(Ptr &&ptr) noexcept
Construct a group_ptr from a shared_ptr.
Definition group_ptr.hpp:326
void reset() noexcept
Reset the group_ptr and make it empty.
Definition group_ptr.hpp:294
group_ptr(group_ptr const &other) noexcept
Copy construct from another group_ptr.
Definition group_ptr.hpp:172
virtual ~group_ptr()
Destroy this group_ptr.
Definition group_ptr.hpp:156
constexpr group_ptr() noexcept=default
Construct an empty group_ptr.
void unsubscribe() noexcept
Unsubscribe the callback function.
Definition group_ptr.hpp:389
T * operator->() const noexcept
Dereference to member of the object that is owned by this group_ptr.
Definition group_ptr.hpp:346
void subscribe(Func &&func) noexcept
Subscribe a callback function.
Definition group_ptr.hpp:382
group_ptr & operator=(group_ptr const &other) noexcept
Copy assign from another group_ptr.
Definition group_ptr.hpp:194
T * get() const noexcept
Get the pointer to the object that this group_ptr owns.
Definition group_ptr.hpp:336
T & operator*() const noexcept
Dereference the object that is owned by this group_ptr.
Definition group_ptr.hpp:356
group_ptr(group_ptr &&other) noexcept
Move construct from another group_ptr.
Definition group_ptr.hpp:227
group_ptr(Ptr &&ptr) noexcept
Construct a group_ptr from a shared_ptr.
Definition group_ptr.hpp:310
Enable a class to be used in a group_ptr.
Definition group_ptr.hpp:40
void notify_group_ptr(Args const &...args) const noexcept
Call the callback which are registered with the owning group_ptrs.
Definition group_ptr.hpp:56
T back(T... args)
T empty(T... args)
T lock(T... args)
T move(T... args)
T pop_back(T... args)
T push_back(T... args)
T size(T... args)