@auto_js/utility 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (41) hide show
  1. package/CMakeLists.txt +45 -0
  2. package/LICENSE +13 -0
  3. package/_module.cc +14 -0
  4. package/assertions.cc +32 -0
  5. package/container/mutable_priority_queue.cc +35 -0
  6. package/container/sealed_map.cc +121 -0
  7. package/container/segmented_priority_queue.cc +105 -0
  8. package/container/tinker_queue.cc +73 -0
  9. package/functional/bind.cc +151 -0
  10. package/functional/elide.cc +52 -0
  11. package/functional/flat_tuple.cc +90 -0
  12. package/functional/function_constant.cc +58 -0
  13. package/functional/function_ref.cc +68 -0
  14. package/functional/functional.cc +107 -0
  15. package/functional/regular_return.cc +55 -0
  16. package/include/auto_js/chunk_view.h +363 -0
  17. package/include/auto_js/no_unique_address.h +8 -0
  18. package/memory/autorelease_pool.cc +281 -0
  19. package/memory/comparator.cc +53 -0
  20. package/memory/memory.cc +64 -0
  21. package/memory/noinit_allocator.cc +64 -0
  22. package/meta/algorithm.cc +74 -0
  23. package/package.json +11 -0
  24. package/platform/lockable.cc +261 -0
  25. package/platform/shim/darwin.cc +8 -0
  26. package/platform/stop_token.cc +107 -0
  27. package/platform/timer.cc +175 -0
  28. package/platform/timer.h.cc +36 -0
  29. package/type_traits/function_traits.cc +173 -0
  30. package/type_traits/transform.cc +60 -0
  31. package/type_traits/type_of.cc +72 -0
  32. package/type_traits/type_pack.cc +116 -0
  33. package/type_traits/type_traits.cc +5 -0
  34. package/utility/constant_wrapper.cc +57 -0
  35. package/utility/covariant_value.cc +33 -0
  36. package/utility/facade.cc +126 -0
  37. package/utility/hash.cc +37 -0
  38. package/utility/ranges.cc +55 -0
  39. package/utility/string.cc +328 -0
  40. package/utility/utility.cc +150 -0
  41. package/utility/variant.cc +26 -0
@@ -0,0 +1,64 @@
1
+ module;
2
+ #include <concepts>
3
+ #include <functional>
4
+ #include <memory>
5
+ #include <type_traits>
6
+ export module util:memory;
7
+ export import :memory.autorelease_pool;
8
+ export import :memory.comparator;
9
+ export import :memory.noinit_allocator;
10
+
11
+ namespace util {
12
+
13
+ // Deleter object for base class which does not have a virtual destructor
14
+ export template <class Type>
15
+ class derived_delete {
16
+ public:
17
+ template <std::derived_from<Type> As>
18
+ explicit constexpr derived_delete(std::type_identity<As> /*type*/) noexcept :
19
+ delete_{[](Type* pointer) noexcept -> void {
20
+ delete static_cast<As*>(pointer);
21
+ }} {}
22
+
23
+ constexpr auto operator()(Type* value) const noexcept {
24
+ delete_(value);
25
+ }
26
+
27
+ private:
28
+ using function_type = auto(Type*) noexcept -> void;
29
+ function_type* delete_;
30
+ };
31
+
32
+ export template <class Type, class Deleter>
33
+ auto move_pointer_operation(
34
+ std::unique_ptr<Type, Deleter> unique,
35
+ std::invocable<Type*, std::unique_ptr<Type, Deleter>> auto function
36
+ ) -> decltype(auto) {
37
+ auto* ptr = unique.get();
38
+ return std::invoke(std::move(function), ptr, std::move(unique));
39
+ }
40
+
41
+ export template <class Type>
42
+ auto move_pointer_operation(
43
+ std::shared_ptr<Type> shared,
44
+ std::invocable<Type*, std::shared_ptr<Type>> auto function
45
+ ) -> decltype(auto) {
46
+ auto* ptr = shared.get();
47
+ return std::invoke(std::move(function), ptr, std::move(shared));
48
+ }
49
+
50
+ export template <class To, std::derived_from<To> Type, class Deleter>
51
+ auto safe_pointer_upcast(std::unique_ptr<Type, Deleter> unique) {
52
+ struct cast_deleter : Deleter {
53
+ cast_deleter() = default;
54
+ // NOLINTNEXTLINE(google-explicit-constructor)
55
+ cast_deleter(Deleter deleter) noexcept : Deleter{std::move(deleter)} {}
56
+ using Deleter::operator();
57
+ constexpr auto operator()(To* ptr) const noexcept {
58
+ (*this)(static_cast<Type*>(ptr));
59
+ }
60
+ };
61
+ return std::unique_ptr<To, cast_deleter>{std::move(unique)};
62
+ }
63
+
64
+ } // namespace util
@@ -0,0 +1,64 @@
1
+ module;
2
+ #include "auto_js/no_unique_address.h"
3
+ #include <concepts>
4
+ #include <memory>
5
+ #include <type_traits>
6
+ #include <utility>
7
+ export module util:memory.noinit_allocator;
8
+
9
+ namespace util {
10
+
11
+ // Allocator which skips default construction of trivial types during runtime.
12
+ export template <class Type, class Allocator = std::allocator<Type>>
13
+ class noinit_allocator {
14
+ public:
15
+ template <class, class> friend class noinit_allocator;
16
+ using value_type = Type;
17
+ using pointer = value_type*;
18
+
19
+ template <class T>
20
+ struct rebind {
21
+ using other = noinit_allocator<T, typename std::allocator_traits<Allocator>::template rebind_alloc<T>>;
22
+ };
23
+
24
+ noinit_allocator() = default;
25
+
26
+ template <class T>
27
+ constexpr explicit noinit_allocator(const noinit_allocator<T>& allocator) :
28
+ allocator_{allocator.allocator_} {}
29
+
30
+ auto operator==(const noinit_allocator&) const -> bool = default;
31
+
32
+ constexpr auto allocate(std::size_t count) -> pointer {
33
+ return allocator_.allocate(count);
34
+ }
35
+
36
+ constexpr auto construct(pointer ptr, auto&&... args) -> void
37
+ requires std::constructible_from<Type, decltype(args)...> {
38
+ auto construct = [ & ]() constexpr -> void {
39
+ std::allocator_traits<Allocator>::construct(allocator_, ptr, std::forward<decltype(args)>(args)...);
40
+ };
41
+ if consteval {
42
+ construct();
43
+ } else {
44
+ if constexpr (sizeof...(args) > 0) {
45
+ construct();
46
+ } else {
47
+ static_assert(std::is_trivially_constructible_v<Type>);
48
+ }
49
+ }
50
+ }
51
+
52
+ constexpr auto deallocate(pointer ptr, std::size_t count) -> void {
53
+ allocator_.deallocate(ptr, count);
54
+ }
55
+
56
+ constexpr auto destroy(pointer ptr) -> void {
57
+ std::allocator_traits<Allocator>::destroy(allocator_, ptr);
58
+ }
59
+
60
+ private:
61
+ NO_UNIQUE_ADDRESS Allocator allocator_;
62
+ };
63
+
64
+ } // namespace util
@@ -0,0 +1,74 @@
1
+ module;
2
+ #include <type_traits>
3
+ export module util:meta.algorithm;
4
+ import :functional;
5
+ import :utility;
6
+
7
+ namespace util {
8
+
9
+ // Makes a `util::type_pack<T...>` from `Something<T...>`
10
+ export constexpr auto make_type_pack = util::overloaded{
11
+ // nb: Prevent `make_type_pack{type_pack_of{...}}` from turning into a mess of hidden types
12
+ []<class... Types>(type_pack_of<Types...> pack) consteval -> auto { return pack; },
13
+ []<template <class> class Pack, class... Types>(std::type_identity<Pack<Types...>> /*type*/) consteval -> auto {
14
+ return type_pack_of{type<Types>...};
15
+ },
16
+ };
17
+
18
+ // Concatenate 0..N `util::type_packs` together
19
+ export constexpr auto pack_concat = util::overloaded{
20
+ []() consteval -> auto { return type_pack{}; },
21
+ [](auto&&... packs) consteval -> auto { return (... + make_type_pack(packs)); },
22
+ };
23
+
24
+ // Apply transformation function to each type in the pack
25
+ export constexpr auto pack_transform = [](auto pack, auto unary) consteval -> auto {
26
+ const auto [... types ] = pack;
27
+ return pack_concat(unary(types)...);
28
+ };
29
+
30
+ // Return a new `type_pack` with types matching the predicate
31
+ // nb: `predicate` must be `util::fn<...>`
32
+ export constexpr auto pack_filter = [](auto pack, auto predicate) consteval -> auto {
33
+ constexpr auto filter = [ = ](auto type) {
34
+ if constexpr (predicate(type)) {
35
+ return type_pack{type};
36
+ } else {
37
+ return type_pack{};
38
+ }
39
+ };
40
+ return pack_transform(pack, filter);
41
+ };
42
+
43
+ // Return a nested `type_pack` of `tuple_pack`'s containing types filtered by the predicates
44
+ // nb: `predicates` must be `util::fn<...>`
45
+ export constexpr auto pack_partition = util::overloaded{
46
+ [](auto pack) consteval -> auto { return type_pack{pack}; },
47
+ []<class Predicate>(this auto pack_partition, auto pack, Predicate predicate, auto... predicates) consteval -> auto {
48
+ // `std::not_fn()` doesn't work
49
+ constexpr auto not_fn = util::fn<[](auto type) -> bool { return !Predicate{}(type); }>;
50
+ const auto left = pack_filter(pack, predicate);
51
+ const auto right = pack_filter(pack, not_fn);
52
+ return pack_concat(pack_partition(left), pack_partition(right, predicates...));
53
+ },
54
+ };
55
+
56
+ // Return unique types from the pack
57
+ export constexpr auto pack_unique = util::overloaded{
58
+ []() consteval -> auto { return type_pack{}; },
59
+ [](auto... types) consteval -> auto {
60
+ constexpr auto transform = [ = ](auto index) {
61
+ constexpr auto subject = types...[ index ];
62
+ const auto [... indices ] = util::sequence<index>;
63
+ if constexpr ((... || (subject == types...[ indices ]))) {
64
+ return type_pack_of{};
65
+ } else {
66
+ return type_pack_of{subject};
67
+ }
68
+ };
69
+ const auto [... indices ] = util::sequence<sizeof...(types)>;
70
+ return (... + transform(indices));
71
+ },
72
+ };
73
+
74
+ } // namespace util
package/package.json ADDED
@@ -0,0 +1,11 @@
1
+ {
2
+ "name": "@auto_js/utility",
3
+ "version": "0.0.1",
4
+ "author": "https://github.com/laverdet/",
5
+ "homepage": "https://github.com/laverdet/isolated-vm/tree/experimental/packages/utility",
6
+ "license": "ISC",
7
+ "repository": {
8
+ "type": "git",
9
+ "url": "git+https://github.com/laverdet/isolated-vm.git"
10
+ }
11
+ }
@@ -0,0 +1,261 @@
1
+ module;
2
+ #include "auto_js/no_unique_address.h"
3
+ #include <cassert>
4
+ #include <concepts>
5
+ #include <condition_variable>
6
+ #include <functional>
7
+ #include <mutex>
8
+ #include <shared_mutex>
9
+ #include <stop_token>
10
+ #include <utility>
11
+ #include <variant>
12
+ export module util:platform.lockable;
13
+ import :type_traits;
14
+ import :utility;
15
+
16
+ namespace util {
17
+
18
+ template <class Type>
19
+ concept basic_lockable = requires(Type lock) {
20
+ { lock.lock() } -> std::same_as<void>;
21
+ { lock.unlock() } -> std::same_as<void>;
22
+ };
23
+
24
+ template <class Type>
25
+ concept shared_lockable =
26
+ basic_lockable<Type> &&
27
+ requires(Type lock) {
28
+ { lock.lock_shared() } -> std::same_as<void>;
29
+ { lock.unlock_shared() } -> std::same_as<void>;
30
+ };
31
+
32
+ template <class Type>
33
+ concept notifiable = requires(Type cv) {
34
+ { cv.notify_one() } -> std::same_as<void>;
35
+ { cv.notify_all() } -> std::same_as<void>;
36
+ };
37
+
38
+ template <class Type, class Lock>
39
+ concept waitable = requires(Type cv, Lock lock) {
40
+ { cv.wait(lock) } -> std::same_as<void>;
41
+ };
42
+
43
+ // Predicate wrapper, binding to resource reference
44
+ template <class Resource, std::predicate<const Resource&> Predicate>
45
+ class resource_predicate {
46
+ public:
47
+ resource_predicate(const Resource& resource, Predicate predicate) :
48
+ resource_{resource},
49
+ predicate_{std::move(predicate)} {}
50
+
51
+ auto operator()() const -> bool {
52
+ // nb: `std::invoke` used so member function predicates work
53
+ return std::invoke(predicate_, resource_.get());
54
+ }
55
+
56
+ private:
57
+ std::reference_wrapper<const Resource> resource_;
58
+ NO_UNIQUE_ADDRESS Predicate predicate_;
59
+ };
60
+
61
+ // This is the return value of `read`, `write`, etc
62
+ template <class Resource, class Lock>
63
+ class locked : public Lock, public pointer_facade {
64
+ public:
65
+ explicit locked(Resource& resource, auto&&... lock_args)
66
+ requires std::constructible_from<Lock, decltype(lock_args)...> :
67
+ Lock{std::forward<decltype(lock_args)>(lock_args)...},
68
+ resource_{resource} {}
69
+
70
+ auto operator*() const -> Resource& { return resource_.get(); }
71
+
72
+ private:
73
+ std::reference_wrapper<Resource> resource_;
74
+ };
75
+
76
+ // Holds a lock and on destruction notifies the given condition variable
77
+ template <std::predicate Predicate, class Lock, notifiable Notifiable>
78
+ class lock_notify {
79
+ public:
80
+ lock_notify(Predicate predicate, Notifiable& cv, auto&&... lock_args)
81
+ requires std::constructible_from<Lock, decltype(lock_args)...> :
82
+ predicate_{std::move(predicate)},
83
+ cv_{&cv},
84
+ lock_{std::forward<decltype(lock_args)>(lock_args)...} {}
85
+
86
+ lock_notify(const lock_notify&) = delete;
87
+ lock_notify(lock_notify&& that) noexcept :
88
+ cv_{std::exchange(that.cv_, nullptr)},
89
+ lock_{std::move(that).lock_} {};
90
+
91
+ ~lock_notify() {
92
+ if (cv_ != nullptr && predicate_()) {
93
+ cv_->notify_one();
94
+ }
95
+ }
96
+
97
+ auto operator=(const lock_notify&) = delete;
98
+ auto operator=(lock_notify&& that) = delete;
99
+
100
+ private:
101
+ Predicate predicate_;
102
+ Notifiable* cv_;
103
+ Lock lock_;
104
+ };
105
+
106
+ // Holds a lock with the option to wait on a condition variable
107
+ template <std::predicate Predicate, basic_lockable Lock, waitable<Lock> Waitable>
108
+ class lock_waitable : public Lock {
109
+ public:
110
+ lock_waitable(Predicate predicate, Waitable& cv, auto&&... lock_args)
111
+ requires std::constructible_from<Lock, decltype(lock_args)...> :
112
+ Lock{std::forward<decltype(lock_args)>(lock_args)...},
113
+ predicate_{std::move(predicate)},
114
+ cv_{&cv} {}
115
+
116
+ auto notify_all() -> void { cv_->notify_one(); }
117
+ auto notify_one() -> void { cv_->notify_one(); }
118
+
119
+ auto wait() -> void {
120
+ cv_->wait(*this, predicate_);
121
+ }
122
+
123
+ template <class Rep, class Period>
124
+ auto wait(std::chrono::duration<Rep, Period> duration) -> bool {
125
+ return cv_->wait_for(*this, duration, predicate_);
126
+ }
127
+
128
+ template <class Rep, class Period>
129
+ auto wait(std::chrono::time_point<Rep, Period> time_point) -> bool {
130
+ return cv_->wait_until(*this, time_point, predicate_);
131
+ }
132
+
133
+ auto wait(std::stop_token stop_token) -> bool
134
+ requires(type<Waitable> == type<std::condition_variable_any>) {
135
+ assert(stop_token.stop_possible());
136
+ return cv_->wait(*this, stop_token, predicate_);
137
+ }
138
+
139
+ template <class Rep, class Period>
140
+ auto wait(std::stop_token stop_token, std::chrono::duration<Rep, Period> duration) -> bool
141
+ requires(type<Waitable> == type<std::condition_variable_any>) {
142
+ return cv_->wait_for(*this, stop_token, duration, predicate_);
143
+ }
144
+
145
+ template <class Rep, class Period>
146
+ auto wait(std::stop_token stop_token, std::chrono::time_point<Rep, Period> time_point) -> bool
147
+ requires(type<Waitable> == type<std::condition_variable_any>) {
148
+ return cv_->wait_until(*this, stop_token, time_point, predicate_);
149
+ }
150
+
151
+ private:
152
+ Predicate predicate_;
153
+ Waitable* cv_;
154
+ };
155
+
156
+ // Lockable resource with configurable mutex and condition variable types
157
+ export template <class Resource, basic_lockable Mutex = std::mutex, class ConditionVariable = std::monostate>
158
+ class lockable {
159
+ public:
160
+ using mutex_type = Mutex;
161
+ using resource_type = Resource;
162
+
163
+ private:
164
+ using scoped_read_lock =
165
+ std::conditional_t<shared_lockable<mutex_type>, std::shared_lock<mutex_type>, std::scoped_lock<mutex_type>>;
166
+ using unique_read_lock =
167
+ std::conditional_t<shared_lockable<mutex_type>, std::shared_lock<mutex_type>, std::unique_lock<mutex_type>>;
168
+
169
+ using scoped_write_lock = std::scoped_lock<mutex_type>;
170
+ using unique_write_lock = std::unique_lock<mutex_type>;
171
+
172
+ template <class Lock, class Predicate>
173
+ using notify_type = lock_notify<resource_predicate<resource_type, Predicate>, Lock, ConditionVariable>;
174
+
175
+ template <class Lock, class Predicate>
176
+ using waitable_type = lock_waitable<resource_predicate<resource_type, Predicate>, Lock, ConditionVariable>;
177
+
178
+ public:
179
+ explicit lockable(auto&&... args) noexcept :
180
+ resource_{std::forward<decltype(args)>(args)...} {}
181
+
182
+ // `read`
183
+ using read_type = locked<const resource_type, scoped_read_lock>;
184
+
185
+ auto read() -> read_type {
186
+ return read_type{resource_, mutex_};
187
+ }
188
+
189
+ // `read_waitable`
190
+ template <class Predicate>
191
+ using read_waitable_type = locked<const resource_type, waitable_type<unique_read_lock, Predicate>>;
192
+
193
+ template <std::predicate<const resource_type&> Predicate>
194
+ auto read_waitable(Predicate predicate) -> read_waitable_type<Predicate> {
195
+ return read_waitable_type<Predicate>{resource_, resource_predicate{resource_, std::move(predicate)}, condition_variable_, mutex_};
196
+ }
197
+
198
+ // `write`
199
+ using write_type = locked<resource_type, scoped_write_lock>;
200
+
201
+ auto write() -> write_type {
202
+ return write_type{resource_, mutex_};
203
+ }
204
+
205
+ // `write_notify`
206
+ template <class Predicate>
207
+ using write_notify_type = locked<resource_type, notify_type<unique_write_lock, Predicate>>;
208
+
209
+ auto write_notify() {
210
+ return write_notify([](const auto&) -> bool { return true; });
211
+ }
212
+
213
+ template <std::predicate<const resource_type&> Predicate>
214
+ auto write_notify(Predicate predicate) -> write_notify_type<Predicate> {
215
+ return write_notify_type<Predicate>{resource_, resource_predicate{resource_, std::move(predicate)}, condition_variable_, mutex_};
216
+ }
217
+
218
+ // `write_waitable`
219
+ template <class Predicate>
220
+ using write_waitable_type = locked<resource_type, waitable_type<unique_write_lock, Predicate>>;
221
+
222
+ template <std::predicate<const resource_type&> Predicate>
223
+ auto write_waitable(Predicate predicate) -> write_waitable_type<Predicate> {
224
+ return write_waitable_type<Predicate>{resource_, resource_predicate{resource_, std::move(predicate)}, condition_variable_, mutex_};
225
+ }
226
+
227
+ private:
228
+ mutex_type mutex_;
229
+ Resource resource_;
230
+ NO_UNIQUE_ADDRESS ConditionVariable condition_variable_;
231
+ };
232
+
233
+ // Preset lockable types
234
+ struct lockable_traits {
235
+ bool interuptable{};
236
+ bool notifiable{};
237
+ bool shared{};
238
+ };
239
+
240
+ export template <class Resource, lockable_traits Traits = {}>
241
+ using lockable_with = type_t<[]() consteval {
242
+ using mutex_type = type_t<[]() {
243
+ if constexpr (Traits.shared) {
244
+ return type<std::shared_mutex>;
245
+ } else {
246
+ return type<std::mutex>;
247
+ }
248
+ }()>;
249
+ using cv_type = type_t<[]() {
250
+ if constexpr (Traits.interuptable) {
251
+ return type<std::condition_variable_any>;
252
+ } else if constexpr (Traits.notifiable) {
253
+ return type<std::condition_variable>;
254
+ } else {
255
+ return type<std::monostate>;
256
+ }
257
+ }()>;
258
+ return type<lockable<Resource, mutex_type, cv_type>>;
259
+ }()>;
260
+
261
+ } // namespace util
@@ -0,0 +1,8 @@
1
+ #include <__functional/hash.h>
2
+ #if _LIBCPP_AVAILABILITY_HAS_HASH_MEMORY
3
+ namespace std::inline __1 {
4
+ [[__gnu__::__pure__]] _LIBCPP_EXPORTED_FROM_ABI size_t __hash_memory(_LIBCPP_NOESCAPE const void* __ptr, size_t __size) _NOEXCEPT {
5
+ return __murmur2_or_cityhash<size_t>()(__ptr, __size);
6
+ }
7
+ } // namespace std::inline __1
8
+ #endif
@@ -0,0 +1,107 @@
1
+ module;
2
+ #include <stop_token>
3
+ #include <utility>
4
+ export module util:platform.stop_token;
5
+ import :functional;
6
+ import :utility;
7
+
8
+ namespace util {
9
+
10
+ // Combine multiple `std::stop_token`s into one `std::stop_token` that is stopped when any of the
11
+ // source tokens is stopped.
12
+ export template <size_t Size>
13
+ class composite_stop_token : private std::stop_source, public std::stop_token {
14
+ private:
15
+ static auto make_callback(std::stop_source stop_source) {
16
+ // I wonder what's better: copying the stop state into each callback, or using function
17
+ // pointer..
18
+ return [ stop_source = std::move(stop_source) ]() mutable -> void { stop_source.request_stop(); };
19
+ }
20
+
21
+ using callback_type = decltype(make_callback(std::declval<std::stop_source>()));
22
+ using stop_callback_type = std::stop_callback<callback_type>;
23
+ using callback_pack_type = std::array<stop_callback_type, Size>;
24
+
25
+ public:
26
+ using std::stop_token::stop_possible;
27
+ using std::stop_token::stop_requested;
28
+
29
+ // Check stoppability of the tokens
30
+ template <std::convertible_to<std::stop_source> Source, std::convertible_to<std::stop_token>... Tokens>
31
+ requires(Size == sizeof...(Tokens))
32
+ explicit composite_stop_token(Source&& source, Tokens&&... tokens) :
33
+ composite_stop_token{
34
+ std::forward<Source>(source),
35
+ (... + (tokens.stop_possible() ? 1 : 0)),
36
+ std::forward<Tokens>(tokens)...
37
+ } {}
38
+
39
+ // Makes a fresh `std::stop_source` internally
40
+ template <std::convertible_to<std::stop_token>... Tokens>
41
+ explicit composite_stop_token(Tokens&&... tokens) :
42
+ composite_stop_token{std::stop_source{}, std::forward<decltype(tokens)>(tokens)...} {}
43
+
44
+ private:
45
+ // Some finesse is required because `std::stop_callback` is not move constructible
46
+ template <class Source, class... Tokens>
47
+ explicit composite_stop_token(Source&& source, int stoppable, Tokens&&... tokens) :
48
+ std::stop_source{std::forward<Source>(source)},
49
+ std::stop_token{util::elide{[ & ]() -> std::stop_token {
50
+ switch (stoppable) {
51
+ // No `stop_token`
52
+ case 0: return std::stop_token{};
53
+ // Delegated `stop_token`
54
+ case 1:
55
+ {
56
+ constexpr auto fold = util::overloaded{
57
+ []() -> std::stop_token { std::unreachable(); },
58
+ [](auto&& token) -> std::stop_token { return std::forward<decltype(token)>(token); },
59
+ [](this auto& fold, auto&& token, auto&&... tokens) -> std::stop_token {
60
+ if (token.stop_possible()) {
61
+ return std::forward<decltype(token)>(token);
62
+ } else {
63
+ return fold(std::forward<decltype(tokens)>(tokens)...);
64
+ }
65
+ },
66
+ };
67
+ return fold(std::forward<decltype(tokens)>(tokens)...);
68
+ }
69
+ // Callback `stop_token`
70
+ default: return std::stop_source::get_token();
71
+ }
72
+ }}},
73
+ callbacks_{[ & ]() -> callback_pack_type {
74
+ switch (stoppable) {
75
+ // No-op callback pack
76
+ case 0:
77
+ case 1:
78
+ {
79
+ auto callback = make_callback(std::stop_source{});
80
+ return {stop_callback_type{(util::unused(tokens), std::stop_token{}), callback}...};
81
+ }
82
+ // Make callback pack
83
+ default:
84
+ {
85
+ auto callback = make_callback(std::move(static_cast<std::stop_source&>(*this)));
86
+ return {stop_callback_type{std::forward<decltype(tokens)>(tokens), callback}...};
87
+ }
88
+ }
89
+ }()} {}
90
+
91
+ // Default init callbacks with no tokens
92
+ callback_pack_type callbacks_;
93
+ };
94
+
95
+ template <std::convertible_to<std::stop_token>... Tokens>
96
+ composite_stop_token(Tokens&&... tokens) -> composite_stop_token<sizeof...(tokens)>;
97
+
98
+ template <std::convertible_to<std::stop_source> Source, std::convertible_to<std::stop_token>... Tokens>
99
+ composite_stop_token(Source&& source, Tokens&&... tokens) -> composite_stop_token<sizeof...(tokens)>;
100
+
101
+ // `std::stop_source` that stops itself on destruction.
102
+ export class scope_stop_source : util::non_copyable, public std::stop_source {
103
+ public:
104
+ ~scope_stop_source() { request_stop(); }
105
+ };
106
+
107
+ } // namespace util