HikoGUI
A low latency retained GUI
Loading...
Searching...
No Matches
when_any.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 "required.hpp"
8#include "scoped_task.hpp"
9#include "notifier.hpp"
10#include "concepts.hpp"
11#include "type_traits.hpp"
12#include "awaitable_timer.hpp"
13#include <coroutine>
14#include <cstddef>
15#include <type_traits>
16#include <concepts>
17#include <variant>
18#include <tuple>
19#include <chrono>
20
21namespace hi::inline v1 {
22namespace detail {
23
24template<awaitable_direct T>
26 using type = std::conditional_t<std::is_same_v<await_resume_result_t<T>, void>, std::monostate, await_resume_result_t<T>>;
27};
28
29template<awaitable_direct T>
30using when_any_result_element_t = when_any_result_element<T>::type;
31
32} // namespace detail
33
36template<bool HasTimeout, typename... Ts>
38public:
39 using result_type = std::variant<std::monostate, detail::when_any_result_element_t<Ts>...>;
40 using awaiter_type = std::variant<std::monostate, Ts...>;
41
42 when_any_result() noexcept = default;
43 when_any_result(when_any_result const&) noexcept = default;
44 when_any_result(when_any_result&&) noexcept = default;
45 when_any_result& operator=(when_any_result const&) noexcept = default;
46 when_any_result& operator=(when_any_result&&) noexcept = default;
47
48 template<std::size_t I, typename Awaiter, typename... Result>
49 when_any_result(std::in_place_index_t<I>, Awaiter const& awaiter, Result&&...result) noexcept :
50 _result{std::in_place_index<I + 1>, std::forward<Result>(result)...}, _awaiters{std::in_place_index<I + 1>, awaiter}
51 {
52 hi_axiom(_result.index() == _awaiters.index());
53 }
54
55 [[nodiscard]] bool timeout() const noexcept requires(HasTimeout)
56 {
57 return index() == 0;
58 }
59
64 [[nodiscard]] std::size_t index() const noexcept
65 {
66 return _result.index() - 1;
67 }
68
71 [[nodiscard]] bool operator==(awaitable auto const& rhs) const noexcept
72 {
73 return compare_equal<1>(awaitable_cast(rhs));
74 }
75
78 template<typename T>
79 friend auto& get(when_any_result const& rhs) noexcept
80 {
81 return std::get<T>(rhs._result);
82 }
83
86 template<std::size_t I>
87 friend auto& get(when_any_result const& rhs) noexcept
88 {
89 return std::get<I + 1>(rhs._result);
90 }
91
92private:
93 result_type _result;
94 awaiter_type _awaiters;
95
96 template<size_t I>
97 [[nodiscard]] bool compare_equal(awaitable_direct auto const& rhs) const noexcept
98 {
99 if (I != _awaiters.index()) {
100 if constexpr (I + 1 < std::variant_size_v<awaiter_type>) {
101 return compare_equal<I + 1>(rhs);
102 } else {
103 hi_no_default();
104 }
105 } else {
106 using get_type = std::decay_t<decltype(get<I>(_awaiters))>;
107 using cmp_type = std::decay_t<decltype(rhs)>;
108
109 if constexpr (std::is_same_v<get_type, cmp_type>) {
110 return std::get<I>(_awaiters) == rhs;
111 } else {
112 return false;
113 }
114 }
115 }
116};
117
121template<bool HasTimeout, typename... Ts>
122class when_any {
123public:
124 using value_type = when_any_result<HasTimeout, Ts...>;
125
136 when_any(awaitable auto&&...others) noexcept : _awaiters(awaitable_cast(hi_forward(others))...) {}
137
148 template<typename Rep, typename Period>
149 when_any(std::chrono::duration<Rep, Period> timeout, awaitable auto&&...others) noexcept : when_any(awaitable_timer{timeout}, hi_forward(others)...)
150 {
151 }
152
153 ~when_any() {}
154
155 when_any(when_any&&) = delete;
156 when_any(when_any const&) = delete;
157 when_any& operator=(when_any&&) = delete;
158 when_any& operator=(when_any const&) = delete;
159
160 [[nodiscard]] constexpr bool await_ready() noexcept
161 {
162 static_assert(sizeof...(Ts) > 0);
163 return _await_ready<0>();
164 }
165
166 void await_suspend(std::coroutine_handle<> const& handle) noexcept
167 {
168 static_assert(sizeof...(Ts) > 0);
169 return _await_suspend<0>(handle);
170 }
171
172 value_type await_resume() noexcept
173 {
174 return _value;
175 }
176
177private:
178 std::tuple<Ts...> _awaiters;
180 // std::tuple<typename notifier<detail::when_any_result_element_t<Ts>>::token_type...> _task_cbts;
181 std::tuple<typename notifier<void(await_resume_result_t<Ts>)>::token_type...> _task_cbts;
182 value_type _value;
183
184 template<awaitable_direct Awaiter>
185 static scoped_task<await_resume_result_t<Awaiter>> _await_suspend_task(Awaiter& awaiter)
186 {
187 co_return co_await awaiter;
188 }
189
190 template<std::size_t I>
191 bool _await_ready() noexcept
192 {
193 auto& task = std::get<I>(_tasks) = _await_suspend_task(std::get<I>(_awaiters));
194
195 if (task.completed()) {
196 using arg_type = await_resume_result_t<decltype(std::get<I>(_awaiters))>;
197
198 if constexpr (std::is_same_v<arg_type, void>) {
199 _value = {std::in_place_index<I>, std::get<I>(_awaiters)};
200 } else {
201 _value = {std::in_place_index<I>, std::get<I>(_awaiters), task.value()};
202 }
203 return true;
204
205 } else if constexpr (I + 1 < sizeof...(Ts)) {
206 return _await_ready<I + 1>();
207
208 } else {
209 return false;
210 }
211 }
212
213 template<std::size_t I>
214 void _await_suspend(std::coroutine_handle<> const& handle) noexcept
215 {
216 using arg_type = await_resume_result_t<decltype(std::get<I>(_awaiters))>;
217
218 if constexpr (std::is_same_v<arg_type, void>) {
219 std::get<I>(_task_cbts) = std::get<I>(_tasks).subscribe([this, handle]() {
220 this->_value = {std::in_place_index<I>, std::get<I>(_awaiters)};
221 handle.resume();
222 });
223
224 } else {
225 std::get<I>(_task_cbts) = std::get<I>(_tasks).subscribe([this, handle](arg_type const& arg) {
226 this->_value = {std::in_place_index<I>, std::get<I>(_awaiters), arg};
227 handle.resume();
228 });
229 }
230
231 if constexpr (I + 1 < sizeof...(Ts)) {
232 _await_suspend<I + 1>(handle);
233 }
234 }
235
236 template<bool HasTimeout, typename... Args>
237 friend class when_any;
238};
239
240template<awaitable... Others>
241when_any(Others&&...) -> when_any<false, awaitable_cast_type_t<Others>...>;
242
243template<typename Rep, typename Period, awaitable... Others>
244when_any(std::chrono::duration<Rep, Period>, Others&&...) -> when_any<true, awaitable_timer, awaitable_cast_type_t<Others>...>;
245
246} // namespace hi::inline v1
await_resume_result< T >::type await_resume_result_t
Get the result type of an awaitable.
Definition type_traits.hpp:411
This file includes required definitions.
#define hi_forward(x)
Forward a value, based on the decltype of the value.
Definition required.hpp:29
Definition awaitable_timer.hpp:11
Definition when_any.hpp:25
Result of the when_any awaitable.
Definition when_any.hpp:37
std::size_t index() const noexcept
The index of the awaitable that was triggered.
Definition when_any.hpp:64
friend auto & get(when_any_result const &rhs) noexcept
Get the value returned by the awaitable that triggered when_any.
Definition when_any.hpp:79
bool operator==(awaitable auto const &rhs) const noexcept
Comparison to check if the awaitable was the one that triggered when_any.
Definition when_any.hpp:71
An awaitable that waits for any of the given awaitables to complete.
Definition when_any.hpp:122
when_any(awaitable auto &&...others) noexcept
Construct a when_any object from the given awaitables.
Definition when_any.hpp:136
when_any(std::chrono::duration< Rep, Period > timeout, awaitable auto &&...others) noexcept
Construct a when_any object from the given awaitables.
Definition when_any.hpp:149
Check if type can be directly co_await on.
Definition concepts.hpp:134
Check if type can be directly or indirectly co_await on.
Definition concepts.hpp:170