Iros
 
Loading...
Searching...
No Matches
lazy.h
Go to the documentation of this file.
1#pragma once
2
10#include "di/meta/core.h"
11#include "di/meta/language.h"
12#include "di/meta/operations.h"
13#include "di/meta/vocab.h"
14#include "di/platform/prelude.h"
15#include "di/types/void.h"
16#include "di/util/coroutine.h"
17#include "di/util/exchange.h"
18#include "di/util/unreachable.h"
25
26namespace di::execution {
27namespace lazy_ns {
28 template<typename T = void>
29 class Lazy;
30
31 struct AllocFailed {};
32
33 template<typename Self, typename T>
34 class PromiseBase : public WithAwaitableSenders<Self> {
35 public:
36 PromiseBase() = default;
37
38 auto operator new(usize size) noexcept -> void* { return ::operator new(size, std::nothrow); }
39 void operator delete(void* ptr, usize size) noexcept { ::operator delete(ptr, size); }
40
41 auto initial_suspend() noexcept -> SuspendAlways { return {}; }
42 auto final_suspend() noexcept { return FinalAwaiter {}; }
43
44 template<concepts::ConvertibleTo<T> U>
45 void return_value(U&& value) {
46 m_data.emplace(util::forward<U>(value));
47 }
48
51 {
52 m_data.emplace();
53 }
54
55 template<typename E>
58 m_data = util::move(error);
59 }
60
61 void return_value(Stopped) { m_data = vocab::Unexpected(BasicError::OperationCanceled); }
62
64
65 private:
66 template<typename>
67 friend class Lazy;
68
69 struct FinalAwaiter {
70 auto await_ready() noexcept -> bool { return false; }
71
72 template<typename Promise>
73 auto await_suspend(CoroutineHandle<Promise> coroutine) noexcept -> CoroutineHandle<> {
74 PromiseBase& current = coroutine.promise();
75
76 auto was_error = !current.m_data.has_value();
77 if (was_error) {
78 if (current.m_data == vocab::Unexpected(BasicError::OperationCanceled)) {
79 return current.unhandled_stopped();
80 }
81 return current.unhandled_error(util::move(current.m_data).error());
82 }
83
84 return current.continuation() ? current.continuation() : noop_coroutine();
85 }
86
87 void await_resume() noexcept {}
88 };
89
90 struct Awaiter {
92
93 auto await_ready() noexcept -> bool { return false; }
94
95 template<typename OtherPromise>
96 auto await_suspend(CoroutineHandle<OtherPromise> continuation) noexcept -> CoroutineHandle<> {
97 // If we don't have a coroutine, it is because allocating it failed. Since the continuation is already
98 // suspended, we can just report the error.
99 if (!coroutine) {
100 return continuation.promise().unhandled_error(vocab::Error(BasicError::NotEnoughMemory));
101 }
102
103 // Otherwise, we can just resume the coroutine after setting the continuation.
104 coroutine.promise().set_continuation(continuation);
105 return coroutine;
106 }
107
108 auto await_resume() -> T {
109 DI_ASSERT(coroutine);
110 auto& promise = static_cast<PromiseBase&>(coroutine.promise());
111 DI_ASSERT(promise.m_data);
112 return *util::move(promise.m_data);
113 }
114 };
115
116 vocab::Result<T> m_data { vocab::Unexpected(BasicError::OperationCanceled) };
117 };
118
119 template<typename T>
120 class [[nodiscard]] Lazy {
121 private:
122 struct Promise;
123
124 using PromiseBase = lazy_ns::PromiseBase<Promise, T>;
125
126 struct Promise : PromiseBase {
127 auto get_return_object() noexcept -> Lazy { return Lazy { CoroutineHandle<Promise>::from_promise(*this) }; }
128 static auto get_return_object_on_allocation_failure() noexcept -> Lazy { return Lazy { AllocFailed {} }; }
129 };
130
131 using Handle = CoroutineHandle<Promise>;
132 using ParentHandle = CoroutineHandle<PromiseBase>;
133 using Awaiter = PromiseBase::Awaiter;
134
135 public:
136 using promise_type = Promise;
137
138 Lazy(Lazy&& other) : m_handle(util::exchange(other.m_handle, {})) {}
139
141 if (m_handle) {
142 m_handle.destroy();
143 }
144 }
145
146 auto operator co_await() -> Awaiter {
147 if (!m_handle) {
148 return Awaiter { nullptr };
149 }
150 auto& promise = static_cast<PromiseBase&>(m_handle.promise());
151 return Awaiter { ParentHandle::from_promise(promise) };
152 }
153
154 private:
155 explicit Lazy(Handle handle) : m_handle(handle) {}
156 explicit Lazy(AllocFailed) {}
157
158 Handle m_handle;
159 };
160}
161
162using lazy_ns::Lazy;
163}
164
165namespace di::vocab {
166template<concepts::Expected T, typename Promise>
167constexpr auto tag_invoke(types::Tag<execution::as_awaitable>, T&& value, Promise& promise) -> decltype(auto) {
168 return execution::as_awaitable(execution::just_or_error(util::forward<T>(value)), promise);
169}
170
171template<concepts::Unexpected T, typename Promise>
172constexpr auto tag_invoke(types::Tag<execution::as_awaitable>, T&& value, Promise& promise) -> decltype(auto) {
173 return execution::as_awaitable(execution::just_error(util::forward<T>(value).error()), promise);
174}
175}
176
177namespace di {
178using execution::Lazy;
179}
#define DI_ASSERT(...)
Definition assert_bool.h:7
Definition with_awaitable_senders.h:8
auto unhandled_error(Error error) -> CoroutineHandle<>
Definition with_awaitable_senders.h:39
auto unhandled_stopped() -> CoroutineHandle<>
Definition with_awaitable_senders.h:38
auto continuation() const -> CoroutineHandle<>
Definition with_awaitable_senders.h:37
Definition lazy.h:120
~Lazy()
Definition lazy.h:140
Lazy(Lazy &&other)
Definition lazy.h:138
Promise promise_type
Definition lazy.h:136
void return_value(U &&value)
Definition lazy.h:45
auto initial_suspend() noexcept -> SuspendAlways
Definition lazy.h:41
void return_value(Stopped)
Definition lazy.h:61
void return_value(vocab::Unexpected< E > &&error)
Definition lazy.h:57
friend class Lazy
Definition lazy.h:67
auto final_suspend() noexcept
Definition lazy.h:42
void return_value(types::Void)
Definition lazy.h:49
void unhandled_exception()
Definition lazy.h:63
Definition unexpected.h:14
Definition operations.h:11
Definition core.h:128
Definition lazy.h:27
Definition bulk.h:30
decltype(just_stopped()) Stopped
Definition just.h:90
constexpr auto just_error
Definition just.h:87
constexpr auto just_or_error
Definition just_or_error.h:86
constexpr as_awaitable_ns::Function as_awaitable
Definition as_awaitable.h:102
size_t usize
Definition integers.h:33
di::meta::Decay< decltype(T)> Tag
Definition tag_invoke.h:28
Definition vocab.h:96
void unreachable()
Definition unreachable.h:4
Definition lazy.h:165
Expected< T, Error > Result
Definition result.h:8
StatusCode< Erased< long > > Error
Definition error.h:8
Unexpected(E &&) -> Unexpected< meta::UnwrapRefDecay< E > >
Definition zstring_parser.h:9
constexpr tag_invoke_detail::TagInvokeFn tag_invoke
Definition tag_invoke.h:22
constexpr auto exchange(T &object, U &&new_value) -> T
Definition exchange.h:8
std::coroutine_handle< Promise > CoroutineHandle
Definition coroutine.h:164
constexpr auto size
Definition size.h:54
std::suspend_always SuspendAlways
Definition coroutine.h:169
@ T
Definition key.h:29
Definition void.h:6