HikoGUI
A low latency retained GUI
Loading...
Searching...
No Matches
observer_intf.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 "observed.hpp"
8#include "../utility/utility.hpp"
9#include "../dispatch/dispatch.hpp"
10#include "../concurrency/thread.hpp" // XXX #616
11#include "../macros.hpp"
12#include <concepts>
13#include <utility>
14#include <format>
15#include <new>
16#include <algorithm>
17#include <memory>
18
19hi_export_module(hikogui.observer : observer_intf);
20
21hi_export namespace hi { inline namespace v1 {
22
31template<typename T>
32class observer {
33public:
34 using value_type = T;
35 using notifier_type = notifier<void(value_type)>;
36 using callback_type = notifier_type::callback_type;
37 using awaiter_type = notifier_type::awaiter_type;
38 using path_type = observable_msg::path_type;
39
45 class proxy_type {
46 public:
47 using reference = value_type &;
48 using const_reference = value_type const &;
49 using pointer = value_type *;
50 using const_pointer = value_type const *;
51
58 {
59 if (_observer != nullptr) {
60 hi_axiom_not_null(_ptr);
61 if (_original_value) {
62 if constexpr (requires(value_type const& a, value_type const& b) { a != b; }) {
63 if (*_original_value != *_ptr) {
64 _observer->notify();
65 }
66 } else {
67 _observer->notify();
68 }
69 }
70 }
71 }
72
73 proxy_type(proxy_type const&) = delete;
74 proxy_type& operator=(proxy_type const&) = delete;
75
77 _observer(std::exchange(other._observer, nullptr)),
78 _ptr(std::exchange(other._ptr, nullptr)),
79 _original_value(std::exchange(other._original_value, std::nullopt))
80 {
81 }
82
83 proxy_type& operator=(proxy_type&& other) noexcept
84 {
85 notify();
86 _observer = std::exchange(other._observer, nullptr);
87 _ptr = std::exchange(other._ptr, nullptr);
88 _original_value = std::exchange(other._original_value, std::nullopt);
89 return *this;
90 }
91
98 proxy_type(observer *observer, value_type *ptr) noexcept :
99 _observer(observer), _ptr(ptr), _original_value(std::nullopt)
100 {
101 hi_axiom_not_null(_observer);
102 hi_axiom_not_null(_ptr);
103 }
104
112 _observer(nullptr), _ptr(nullptr), _original_value(std::nullopt)
113 {
114 }
115
122 reference operator*() noexcept
123 {
124 start_write();
125 return *_ptr;
126 }
127
134 const_reference operator*() const noexcept
135 {
136 hi_axiom_not_null(_ptr);
137 return *_ptr;
138 }
139
148 {
149 start_write();
150 return _ptr;
151 }
152
160 const_pointer operator->() const noexcept
161 {
162 hi_axiom_not_null(_ptr);
163 return _ptr;
164 }
165
174 {
175 start_write();
176 return _ptr;
177 }
178
186 const_pointer operator&() const noexcept
187 {
188 hi_axiom_not_null(_ptr);
189 return _ptr;
190 }
191
192 // clang-format off
193
194 // prefix operators
195#define X(op) \
196 template<typename Rhs> \
197 decltype(auto) operator op() noexcept \
198 requires requires(value_type& a) { op a; } \
199 { \
200 start_write(); \
201 return op (*_ptr); \
202 }
203
204 X(++)
205 X(--)
206#undef X
207
208 // suffix operators
209#define X(op) \
210 template<typename Rhs> \
211 auto operator op(int) noexcept \
212 requires requires(value_type& a) { a op; } \
213 { \
214 start_write(); \
215 return (*_ptr) op; \
216 }
217
218 X(++)
219 X(--)
220#undef X
221
222 // inplace operators
223#define X(op) \
224 template<typename Rhs> \
225 decltype(auto) operator op(Rhs const& rhs) noexcept \
226 requires requires(value_type& a, Rhs const& b) { a op b; } \
227 { \
228 start_write(); \
229 return (*_ptr) op rhs; \
230 }
231
232 X(+=)
233 X(-=)
234 X(*=)
235 X(/=)
236 X(%=)
237 X(&=)
238 X(|=)
239 X(^=)
240 X(<<=)
241 X(>>=)
242#undef X
243
244 // mono operators
245#define X(op) \
246 template<typename Rhs> \
247 auto operator op() const noexcept \
248 requires requires(value_type const& a) { op a; } \
249 { \
250 hi_axiom_not_null(_ptr); \
251 return op (*_ptr); \
252 }
253
254 X(-)
255 X(+)
256 X(~)
257 X(!)
258#undef X
259
260 // binary operators
261#define X(op) \
262 template<typename Rhs> \
263 auto operator op(Rhs const& rhs) const noexcept \
264 requires requires(value_type const& a, Rhs const& b) { a op b; } \
265 { \
266 hi_axiom_not_null(_ptr); \
267 return (*_ptr) op rhs; \
268 }
269
270 X(==)
271 X(<=>)
272 X(+)
273 X(-)
274 X(*)
275 X(/)
276 X(%)
277 X(&)
278 X(|)
279 X(^)
280 X(<<)
281 X(>>)
282#undef X
283
284 // call operator
285 template<typename... Args>
286 auto operator()(Args &&... args) const noexcept
287 requires requires(value_type const & a, Args &&...args) { a(std::forward<Args>(args)...); }
288 {
289 hi_axiom_not_null(_ptr);
290 return (*_ptr)(std::forward<Args>(args)...);
291 }
292
293 template<typename... Args>
294 decltype(auto) operator()(Args &&... args) noexcept
295 requires requires(value_type & a, Args &&...args) { a(std::forward<Args>(args)...); }
296 {
297 start_write();
298 return (*_ptr)(std::forward<Args>(args)...);
299 }
300
301 // index operator
302 // XXX c++23
303 template<typename Arg>
304 auto operator[](Arg && arg) const noexcept
305 requires requires(value_type const & a, Arg &&arg) { a[std::forward<Arg>(arg)]; }
306 {
307 hi_axiom_not_null(_ptr);
308 return (*_ptr)[std::forward<Arg>(arg)];
309 }
310
311 // clang-format on
312
313 private:
314 observer *_observer;
315 value_type *_ptr;
316 std::optional<value_type> _original_value;
317
318 void start_write() noexcept
319 {
320 if (not _original_value) {
321 hi_axiom_not_null(_ptr);
322 _original_value = *_ptr;
323 }
324 }
325
326 friend class observer;
327 };
328
329 using const_reference = value_type const &;
330 using pointer = proxy_type;
331 using const_pointer = value_type const *;
332
333 constexpr ~observer() = default;
334
339 template<forward_of<std::shared_ptr<hi::observed_base>> Observed>
341 observer(group_ptr<hi::observed_base>{std::forward<Observed>(observed_base)}, path_type{}, [](void *base) {
342 return base;
343 })
344 {
345 }
346
351 constexpr explicit observer() noexcept : observer(std::make_shared<observed<value_type>>()) {}
352
355 template<std::convertible_to<value_type> Value>
356 constexpr observer(Value && value) noexcept :
357 observer(std::make_shared<observed<value_type>>(std::forward<Value>(value)))
358 {
359 }
360
366 constexpr observer(observer const& other) noexcept : observer(other._observed, other._path, other._convert) {}
367
374 observer(std::move(other._observed), std::move(other._path), std::move(other._convert))
375 {
376 other.reset();
377 }
378
385 constexpr observer& operator=(observer const& other) noexcept
386 {
387 _observed = other._observed;
388 _path = other._path;
389 _convert = other._convert;
390
391 // Rewire the callback subscriptions and notify listeners to this observer.
392 update_state_callback();
393 _observed->notify_group_ptr(observable_msg{_observed->get(), _path});
394 return *this;
395 }
396
404 constexpr observer& operator=(observer&& other) noexcept
405 {
406 _observed = std::move(other._observed);
407 _path = std::move(other._path);
408 _convert = std::move(other._convert);
409 other.reset();
410
411 // Rewire the callback subscriptions and notify listeners to this observer.
412 update_state_callback();
413 _observed->notify_group_ptr(observable_msg{_observed->get(), _path});
414 return *this;
415 }
416
422 {
423 _observed = std::make_shared<observed<value_type>>();
424 _path = {};
425 _convert = [](void *base) {
426 return base;
427 };
428 update_state_callback();
429 }
430
435 [[nodiscard]] const_pointer get() const noexcept
436 {
437 return convert(_observed->get());
438 }
439
445 {
446 return proxy_type{this, convert(_observed->get())};
447 }
448
455 template<forward_of<void(value_type)> Func>
456 [[nodiscard]] callback<void(value_type)> subscribe(Func &&func, callback_flags flags = callback_flags::synchronous) noexcept
457 {
458 return _notifier.subscribe(std::forward<Func>(func), flags);
459 }
460
461 awaiter_type operator co_await() const noexcept
462 {
463 return _notifier.operator co_await();
464 }
465
471 [[nodiscard]] auto sub(auto const& index) const noexcept
472 requires(requires() { std::declval<value_type>()[index]; })
473 {
474 using result_type = std::decay_t<decltype(std::declval<value_type>()[index])>;
475
476 auto new_path = _path;
477 new_path.push_back(std::format("[{}]", index));
479 _observed, std::move(new_path), [convert_copy = this->_convert, index](void *base) -> void * {
480 return std::addressof((*std::launder(static_cast<value_type *>(convert_copy(base))))[index]);
481 }};
482 }
483
490 template<fixed_string Name>
492 {
493 using result_type = std::decay_t<decltype(selector<value_type>{}.template get<Name>(std::declval<value_type&>()))>;
494
495 auto new_path = _path;
497 // clang-format off
499 _observed,
501 [convert_copy = this->_convert](void *base) -> void * {
503 *std::launder(static_cast<value_type *>(convert_copy(base)))));
504 });
505 // clang-format on
506 }
507
508 //
509 // Convenient functions / operators working on temporary proxies.
510 //
511
512 // clang-format off
513
516 template<typename Rhs>
517 observer& operator=(Rhs&& rhs) noexcept
518 requires requires (value_type &a, Rhs &&b) { a = std::forward<Rhs>(b); }
519 {
520 *get() = std::forward<Rhs>(rhs);
521 return *this;
522 }
523
526 const_reference operator*() const noexcept
527 {
528 // This returns a copy of the dereferenced value of the proxy.
529 // The proxy's lifetime will be extended for the copy to be made.
530 return *get();
531 }
532
533 const_pointer operator&() const noexcept
534 {
535 return get();
536 }
537
540 const_pointer operator->() const noexcept
541 {
542 return get();
543 }
544
548 {
549 return get();
550 }
551
552 // prefix operators
553#define X(op) \
554 template<typename Rhs> \
555 observer& operator op() noexcept \
556 requires requires(value_type& a) { op a; } \
557 { \
558 op *get(); \
559 return *this; \
560 }
561
562 X(++)
563 X(--)
564#undef X
565
566 // suffix operators
567#define X(op) \
568 template<typename Rhs> \
569 auto operator op(int) noexcept \
570 requires requires(value_type& a) { a op; } \
571 { \
572 return *get() op; \
573 }
574
575 X(++)
576 X(--)
577#undef X
578
579 // inplace operators
580#define X(op) \
581 template<typename Rhs> \
582 observer& operator op(Rhs const& rhs) noexcept \
583 requires requires(value_type& a, Rhs const& b) { a op b; } \
584 { \
585 *get() op rhs; \
586 return *this; \
587 }
588
589 X(+=)
590 X(-=)
591 X(*=)
592 X(/=)
593 X(%=)
594 X(&=)
595 X(|=)
596 X(^=)
597 X(<<=)
598 X(>>=)
599#undef X
600
601 // mono operators
602#define X(op) \
603 template<typename Rhs> \
604 auto operator op() const noexcept \
605 requires requires(value_type const& a) { op a; } \
606 { \
607 return op *get(); \
608 }
609
610 X(-)
611 X(+)
612 X(~)
613 X(!)
614#undef X
615
616 // binary operators
617#define X(op) \
618 template<typename Rhs> \
619 auto operator op(Rhs const& rhs) const noexcept \
620 requires requires(value_type const& a, Rhs const& b) { a op b; } \
621 { \
622 return *get() op rhs; \
623 }
624
625 X(==)
626 X(<=>)
627 X(+)
628 X(-)
629 X(*)
630 X(/)
631 X(%)
632 X(&)
633 X(|)
634 X(^)
635 X(<<)
636 X(>>)
637#undef X
638
639 // call operator
640 template<typename... Args>
641 auto operator()(Args &&... args) const noexcept
642 requires requires(value_type const & a, Args &&...args) { a(std::forward<Args>(args)...); }
643 {
644 return (*get())(std::forward<Args>(args)...);
645 }
646
647 // index operator
648 // XXX c++23
649 template<typename Arg>
650 auto operator[](Arg && arg) const noexcept
651 requires requires(value_type const & a, Arg &&arg) { a[std::forward<Arg>(arg)]; }
652 {
653 return (*get())[std::forward<Arg>(arg)];
654 }
655
656 // clang-format on
657
658private:
660 observed_type _observed = {};
661 path_type _path = {};
662 std::function<void *(void *)> _convert = {};
663 notifier_type _notifier;
664#ifndef NDEBUG
665 value_type _debug_value;
666#endif
667
670 template<forward_of<observed_type> ObservedBase, forward_of<path_type> Path, forward_of<void *(void *)> Converter>
673 Path&& path,
675 _observed(std::forward<ObservedBase>(observed_base)), _path(std::forward<Path>(path)), _convert(std::forward<Converter>(converter)), _notifier()
676 {
677 update_state_callback();
678 }
679
680 void notify() const noexcept
681 {
682 _observed->notify_group_ptr(observable_msg{_observed->get(), _path});
683 }
684
685 value_type *convert(void *base) const noexcept
686 {
687 return std::launder(static_cast<value_type *>(_convert(base)));
688 }
689
690 value_type const *convert(void const *base) const noexcept
691 {
692 return std::launder(static_cast<value_type const *>(_convert(const_cast<void *>(base))));
693 }
694
695 void update_state_callback() noexcept
696 {
697 _observed.subscribe([this](observable_msg const& msg) {
698 auto const [msg_it, this_it] = std::mismatch(msg.path.cbegin(), msg.path.cend(), _path.cbegin(), _path.cend());
699 // If the message's path is fully within the this' path, then this is a sub-path.
700 // If this' path is fully within the message's path, then this is along the path.
701 if (msg_it == msg.path.cend() or this_it == _path.cend()) {
702#ifndef NDEBUG
703 _debug_value = *convert(msg.ptr);
704#endif
705 _notifier(*convert(msg.ptr));
706 }
707 });
708
709#ifndef NDEBUG
710 _debug_value = *convert(_observed->get());
711#endif
712 }
713
714 // It is possible to make sub-observables.
715 template<typename>
716 friend class observer;
717};
718
731template<typename T>
733 using type = std::remove_cvref_t<T>;
734};
735
736// clang-format off
737template<std::equality_comparable T> struct observer_decay<observer<T>> { using type = T; };
738template<std::equality_comparable T> struct observer_decay<observer<T> &> { using type = T; };
739template<std::equality_comparable T> struct observer_decay<observer<T> const &> { using type = T; };
740template<std::equality_comparable T> struct observer_decay<observer<T> &&> { using type = T; };
741
742// clang-format on
743
744template<typename T>
745using observer_decay_t = observer_decay<T>::type;
746
747template<typename Context, typename Expected>
748struct is_forward_of<Context, observer<Expected>> :
749 std::conditional_t<std::is_convertible_v<Context, observer<Expected>>, std::true_type, std::false_type> {};
750
751
752template<typename Context, typename Expected>
754
755}} // namespace hi::inline v1
Functions and types for accessing operating system threads.
@ other
The gui_event does not have associated data.
STL namespace.
The HikoGUI namespace.
Definition array_generic.hpp:20
DOXYGEN BUG.
Definition algorithm_misc.hpp:20
Definition callback.hpp:77
Definition observed.hpp:17
std::vector< std::string > path_type
The type of the path used for notifying observers.
Definition observed.hpp:20
An abstract observed object.
Definition observed.hpp:30
Definition observed.hpp:53
A observer pointing to the whole or part of a observed_base.
Definition observer_intf.hpp:32
void reset() noexcept
Reset the observer.
Definition observer_intf.hpp:421
const_pointer get() const noexcept
Read the observed_base value.
Definition observer_intf.hpp:435
observer & operator=(Rhs &&rhs) noexcept
Assign a new value to the observed_base value.
Definition observer_intf.hpp:517
observer(Observed &&observed_base) noexcept
Create an observer from an observed_base.
Definition observer_intf.hpp:340
const_pointer operator->() const noexcept
Constant pointer-to-member of the value being observed_base.
Definition observer_intf.hpp:540
pointer operator->() noexcept
Constant pointer-to-member of the value being observed_base.
Definition observer_intf.hpp:547
constexpr observer(observer &&other) noexcept
Move construct.
Definition observer_intf.hpp:373
constexpr observer & operator=(observer &&other) noexcept
Move assign.
Definition observer_intf.hpp:404
constexpr observer(Value &&value) noexcept
Create a observer linked to an anonymous observed_base-value.
Definition observer_intf.hpp:356
pointer get() noexcept
Make a copy of the observed_base value for modification.
Definition observer_intf.hpp:444
auto sub() const noexcept
Create a sub-observer by selecting a member-variable of the value.
Definition observer_intf.hpp:491
const_reference operator*() const noexcept
Get the value being observed_base.
Definition observer_intf.hpp:526
constexpr observer & operator=(observer const &other) noexcept
Copy assign.
Definition observer_intf.hpp:385
callback< void(value_type)> subscribe(Func &&func, callback_flags flags=callback_flags::synchronous) noexcept
Subscribe a callback to this observer.
Definition observer_intf.hpp:456
constexpr observer(observer const &other) noexcept
Copy construct.
Definition observer_intf.hpp:366
constexpr observer() noexcept
Create a observer linked to an anonymous default initialized observed_base-value.
Definition observer_intf.hpp:351
observer(ObservedBase &&observed_base, Path &&path, Converter &&converter) noexcept
Construct an observer from an observed_base.
Definition observer_intf.hpp:671
auto sub(auto const &index) const noexcept
Create a sub-observer by indexing into the value.
Definition observer_intf.hpp:471
A proxy object of the observer.
Definition observer_intf.hpp:45
reference operator*() noexcept
Dereference the value.
Definition observer_intf.hpp:122
proxy_type() noexcept
Create a proxy object.
Definition observer_intf.hpp:111
pointer operator&() noexcept
Pointer dereference the value.
Definition observer_intf.hpp:173
const_pointer operator&() const noexcept
Pointer dereference the value.
Definition observer_intf.hpp:186
~proxy_type() noexcept
Commits and destruct the proxy object.
Definition observer_intf.hpp:57
pointer operator->() noexcept
Pointer dereference the value.
Definition observer_intf.hpp:147
const_pointer operator->() const noexcept
Pointer dereference the value.
Definition observer_intf.hpp:160
const_reference operator*() const noexcept
Dereference the value.
Definition observer_intf.hpp:134
proxy_type(observer *observer, value_type *ptr) noexcept
Create a proxy object.
Definition observer_intf.hpp:98
A type-trait for observer arguments.
Definition observer_intf.hpp:732
Is context a form of the expected type.
Definition type_traits.hpp:593
This selector allows access to member variable by name.
Definition type_traits.hpp:691
Definition observer_intf.hpp:753
True if T is a forwarded type of Forward.
Definition concepts.hpp:137
T addressof(T... args)
T make_shared(T... args)
T mismatch(T... args)
T move(T... args)
T push_back(T... args)