@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.
- package/CMakeLists.txt +45 -0
- package/LICENSE +13 -0
- package/_module.cc +14 -0
- package/assertions.cc +32 -0
- package/container/mutable_priority_queue.cc +35 -0
- package/container/sealed_map.cc +121 -0
- package/container/segmented_priority_queue.cc +105 -0
- package/container/tinker_queue.cc +73 -0
- package/functional/bind.cc +151 -0
- package/functional/elide.cc +52 -0
- package/functional/flat_tuple.cc +90 -0
- package/functional/function_constant.cc +58 -0
- package/functional/function_ref.cc +68 -0
- package/functional/functional.cc +107 -0
- package/functional/regular_return.cc +55 -0
- package/include/auto_js/chunk_view.h +363 -0
- package/include/auto_js/no_unique_address.h +8 -0
- package/memory/autorelease_pool.cc +281 -0
- package/memory/comparator.cc +53 -0
- package/memory/memory.cc +64 -0
- package/memory/noinit_allocator.cc +64 -0
- package/meta/algorithm.cc +74 -0
- package/package.json +11 -0
- package/platform/lockable.cc +261 -0
- package/platform/shim/darwin.cc +8 -0
- package/platform/stop_token.cc +107 -0
- package/platform/timer.cc +175 -0
- package/platform/timer.h.cc +36 -0
- package/type_traits/function_traits.cc +173 -0
- package/type_traits/transform.cc +60 -0
- package/type_traits/type_of.cc +72 -0
- package/type_traits/type_pack.cc +116 -0
- package/type_traits/type_traits.cc +5 -0
- package/utility/constant_wrapper.cc +57 -0
- package/utility/covariant_value.cc +33 -0
- package/utility/facade.cc +126 -0
- package/utility/hash.cc +37 -0
- package/utility/ranges.cc +55 -0
- package/utility/string.cc +328 -0
- package/utility/utility.cc +150 -0
- package/utility/variant.cc +26 -0
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
module;
|
|
2
|
+
#include <algorithm>
|
|
3
|
+
#include <array>
|
|
4
|
+
#include <chrono>
|
|
5
|
+
#include <functional>
|
|
6
|
+
#include <memory>
|
|
7
|
+
#include <ranges>
|
|
8
|
+
#include <stop_token>
|
|
9
|
+
#include <thread>
|
|
10
|
+
#include <utility>
|
|
11
|
+
#include <vector>
|
|
12
|
+
module util;
|
|
13
|
+
import :functional;
|
|
14
|
+
import :platform.lockable;
|
|
15
|
+
import :platform.timer;
|
|
16
|
+
|
|
17
|
+
namespace util {
|
|
18
|
+
|
|
19
|
+
namespace {
|
|
20
|
+
util::lockable_with<std::weak_ptr<timer_thread>, {.shared = true}> shared_thread;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
// Internal thread which manages timers
|
|
24
|
+
class timer_thread : util::non_moveable {
|
|
25
|
+
public:
|
|
26
|
+
class timer;
|
|
27
|
+
using clock_type = timer_stop_token::clock_type;
|
|
28
|
+
|
|
29
|
+
timer_thread();
|
|
30
|
+
auto insert(std::stop_source stop_source, clock_type::duration timeout) -> void;
|
|
31
|
+
static auto acquire() -> std::shared_ptr<timer_thread>;
|
|
32
|
+
|
|
33
|
+
private:
|
|
34
|
+
using timer_queue_type = std::vector<timer>;
|
|
35
|
+
using request_type = util::function_ref<auto(timer_queue_type&)->void>;
|
|
36
|
+
using request_queue_type = std::vector<request_type>;
|
|
37
|
+
|
|
38
|
+
template <class Invocable>
|
|
39
|
+
auto dispatch(Invocable invocable, auto&&... args);
|
|
40
|
+
|
|
41
|
+
auto dispatch(request_type request) -> void;
|
|
42
|
+
auto loop(std::stop_token stop_token) -> void;
|
|
43
|
+
|
|
44
|
+
util::lockable_with<request_queue_type, {.interuptable = true, .shared = true}> requests_;
|
|
45
|
+
timer_queue_type timers_;
|
|
46
|
+
std::jthread thread_;
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
// Internal thread-managed timer
|
|
50
|
+
class timer_thread::timer : public util::pointer_facade {
|
|
51
|
+
public:
|
|
52
|
+
explicit timer(clock_type::time_point time_point, std::stop_source&& stop_source) :
|
|
53
|
+
time_point_{time_point},
|
|
54
|
+
stop_source_{std::move(stop_source)} {}
|
|
55
|
+
|
|
56
|
+
auto operator*() & -> std::stop_source& { return stop_source_; }
|
|
57
|
+
auto operator*() const& -> const std::stop_source& { return stop_source_; }
|
|
58
|
+
|
|
59
|
+
[[nodiscard]] auto time_point() const -> clock_type::time_point { return time_point_; }
|
|
60
|
+
|
|
61
|
+
private:
|
|
62
|
+
clock_type::time_point time_point_;
|
|
63
|
+
std::stop_source stop_source_;
|
|
64
|
+
};
|
|
65
|
+
|
|
66
|
+
// ---
|
|
67
|
+
|
|
68
|
+
// `timer_thread`
|
|
69
|
+
timer_thread::timer_thread() :
|
|
70
|
+
thread_{[ & ](std::stop_token stop_token) -> void { loop(stop_token); }} {}
|
|
71
|
+
|
|
72
|
+
auto timer_thread::loop(std::stop_token stop_token) -> void {
|
|
73
|
+
auto lock = requests_.write_waitable(std::not_fn(&request_queue_type::empty));
|
|
74
|
+
while (!stop_token.stop_requested()) {
|
|
75
|
+
// wait for requests or timeout
|
|
76
|
+
const auto has_requests = [ & ]() -> bool {
|
|
77
|
+
if (timers_.empty()) {
|
|
78
|
+
return lock.wait(stop_token);
|
|
79
|
+
} else {
|
|
80
|
+
auto next = timers_.back().time_point();
|
|
81
|
+
return lock.wait(stop_token, next);
|
|
82
|
+
}
|
|
83
|
+
}();
|
|
84
|
+
// dispatch timer requests
|
|
85
|
+
if (has_requests) {
|
|
86
|
+
for (const auto& request : *lock) {
|
|
87
|
+
request(timers_);
|
|
88
|
+
}
|
|
89
|
+
lock->clear();
|
|
90
|
+
lock.notify_all();
|
|
91
|
+
}
|
|
92
|
+
// expire timers
|
|
93
|
+
auto now = clock_type::now();
|
|
94
|
+
auto expired_timer_view =
|
|
95
|
+
std::ranges::reverse_view{timers_} |
|
|
96
|
+
std::views::take_while([ & ](const auto& timer) {
|
|
97
|
+
return timer.time_point() <= now;
|
|
98
|
+
});
|
|
99
|
+
for (auto& timer : expired_timer_view) {
|
|
100
|
+
timer->request_stop();
|
|
101
|
+
timers_.pop_back();
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
auto timer_thread::dispatch(request_type request) -> void {
|
|
107
|
+
requests_.write_notify()->push_back(request);
|
|
108
|
+
constexpr auto cast = [](const request_type& item) -> auto {
|
|
109
|
+
return std::bit_cast<std::array<std::byte, sizeof(request_type)>>(item);
|
|
110
|
+
};
|
|
111
|
+
auto does_not_contain = [ & ](const auto& queue) -> bool {
|
|
112
|
+
return !std::ranges::contains(queue, cast(request), cast);
|
|
113
|
+
};
|
|
114
|
+
requests_.read_waitable(does_not_contain).wait();
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
template <class Invocable>
|
|
118
|
+
auto timer_thread::dispatch(Invocable invocable, auto&&... args) {
|
|
119
|
+
auto implementation = util::regular_return{
|
|
120
|
+
[ invocable = std::move(invocable),
|
|
121
|
+
... args = std::forward<decltype(args)>(args) ](
|
|
122
|
+
timer_queue_type& queue
|
|
123
|
+
) mutable -> void {
|
|
124
|
+
return invocable(queue, std::forward<decltype(args)>(args)...);
|
|
125
|
+
}
|
|
126
|
+
};
|
|
127
|
+
using result_type = std::invoke_result_t<decltype(implementation), timer_queue_type&>;
|
|
128
|
+
std::optional<result_type> result;
|
|
129
|
+
auto request = [ & ](timer_queue_type& queue) -> void {
|
|
130
|
+
result = implementation(queue);
|
|
131
|
+
};
|
|
132
|
+
dispatch(request_type{request});
|
|
133
|
+
return *std::move(result).value_or(util::elide{[]() -> result_type {
|
|
134
|
+
std::unreachable();
|
|
135
|
+
}});
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
auto timer_thread::insert(std::stop_source stop_source, clock_type::duration timeout) -> void {
|
|
139
|
+
dispatch([ & ](timer_queue_type& queue) -> void {
|
|
140
|
+
auto time_point = clock_type::now() + timeout;
|
|
141
|
+
constexpr auto projection = [](const timer_thread::timer& timer) -> timer_thread::clock_type::time_point {
|
|
142
|
+
return timer.time_point();
|
|
143
|
+
};
|
|
144
|
+
auto it = std::ranges::upper_bound(queue, time_point, std::greater{}, projection);
|
|
145
|
+
queue.emplace(it, time_point, std::move(stop_source));
|
|
146
|
+
});
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
auto timer_thread::acquire() -> std::shared_ptr<timer_thread> {
|
|
150
|
+
auto acquired = shared_thread.read()->lock();
|
|
151
|
+
if (!acquired) {
|
|
152
|
+
auto lock = shared_thread.write();
|
|
153
|
+
acquired = lock->lock();
|
|
154
|
+
if (!acquired) {
|
|
155
|
+
*lock = acquired = std::make_shared<timer_thread>();
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
return acquired;
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
// `timer_stop_token`
|
|
162
|
+
timer_stop_token::timer_stop_token(clock_type::duration duration) :
|
|
163
|
+
thread_{timer_thread::acquire()} {
|
|
164
|
+
thread_->insert(stop_source_, duration);
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
auto timer_stop_token::get_source() const -> const std::stop_source& {
|
|
168
|
+
return stop_source_;
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
auto timer_stop_token::get_token() const -> std::stop_token {
|
|
172
|
+
return stop_source_.get_token();
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
} // namespace util
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
module;
|
|
2
|
+
#include <chrono>
|
|
3
|
+
#include <memory>
|
|
4
|
+
#include <stop_token>
|
|
5
|
+
export module util:platform.timer;
|
|
6
|
+
|
|
7
|
+
namespace util {
|
|
8
|
+
|
|
9
|
+
class timer_thread;
|
|
10
|
+
|
|
11
|
+
// Manages a `std::stop_token` which is stopped after a duration, using a background thread.
|
|
12
|
+
export class timer_stop_token {
|
|
13
|
+
public:
|
|
14
|
+
using clock_type = std::chrono::steady_clock;
|
|
15
|
+
|
|
16
|
+
explicit timer_stop_token(clock_type::duration duration);
|
|
17
|
+
|
|
18
|
+
template <class Rep, class Period>
|
|
19
|
+
explicit timer_stop_token(std::chrono::duration<Rep, Period> duration) :
|
|
20
|
+
timer_stop_token(std::chrono::duration_cast<clock_type::duration>(duration)) {}
|
|
21
|
+
|
|
22
|
+
timer_stop_token(const timer_stop_token&) = delete;
|
|
23
|
+
timer_stop_token(timer_stop_token&&) noexcept = default;
|
|
24
|
+
~timer_stop_token() = default;
|
|
25
|
+
auto operator=(const timer_stop_token&) -> timer_stop_token& = delete;
|
|
26
|
+
auto operator=(timer_stop_token&&) -> timer_stop_token& = delete;
|
|
27
|
+
|
|
28
|
+
[[nodiscard]] auto get_source() const -> const std::stop_source&;
|
|
29
|
+
[[nodiscard]] auto get_token() const -> std::stop_token;
|
|
30
|
+
|
|
31
|
+
private:
|
|
32
|
+
std::shared_ptr<timer_thread> thread_;
|
|
33
|
+
std::stop_source stop_source_;
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
} // namespace util
|
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
module;
|
|
2
|
+
#include <concepts>
|
|
3
|
+
#include <type_traits>
|
|
4
|
+
export module util:type_traits.function_traits;
|
|
5
|
+
import :type_traits.transform;
|
|
6
|
+
|
|
7
|
+
namespace util {
|
|
8
|
+
|
|
9
|
+
// Apply the cvref qualifier of `From` to a function signature
|
|
10
|
+
export template <class From, class Fn>
|
|
11
|
+
struct apply_function_cvref;
|
|
12
|
+
|
|
13
|
+
export template <class From, class Fn>
|
|
14
|
+
using apply_function_cvref_t = apply_function_cvref<From, Fn>::type;
|
|
15
|
+
|
|
16
|
+
// Extract the cvref qualifier of a function signature applied to `int`. For example:
|
|
17
|
+
// `auto() const& -> void` == `const int&`
|
|
18
|
+
export template <class Fn>
|
|
19
|
+
struct extract_function_cvref;
|
|
20
|
+
|
|
21
|
+
export template <class Fn>
|
|
22
|
+
using extract_function_cvref_t = extract_function_cvref<Fn>::type;
|
|
23
|
+
|
|
24
|
+
// Remove cvref qualifier from a function signature. Used as a requirement it approximates
|
|
25
|
+
// `std::is_function`, without support for C-style variadic functions.
|
|
26
|
+
export template <class Fn>
|
|
27
|
+
struct remove_function_cvref;
|
|
28
|
+
|
|
29
|
+
export template <class Fn>
|
|
30
|
+
using remove_function_cvref_t = remove_function_cvref<Fn>::type;
|
|
31
|
+
|
|
32
|
+
// Generated specializations for `apply_function_cvref`, `extract_function_cvref`, and `remove_function_cvref`
|
|
33
|
+
// NOLINTBEGIN(bugprone-macro-parentheses, cppcoreguidelines-macro-usage)
|
|
34
|
+
#define MAKE_CVREF(CV, REF) \
|
|
35
|
+
template <class From, class... Args, bool Nx, class Result> \
|
|
36
|
+
struct apply_function_cvref<CV From REF, auto(Args...) noexcept(Nx)->Result> { \
|
|
37
|
+
using type = auto(Args...) CV REF noexcept(Nx) -> Result; \
|
|
38
|
+
}; \
|
|
39
|
+
template <class... Args, bool Nx, class Result> \
|
|
40
|
+
struct extract_function_cvref<auto(Args...) CV REF noexcept(Nx)->Result> { \
|
|
41
|
+
using type = CV int REF; \
|
|
42
|
+
}; \
|
|
43
|
+
template <class... Args, bool Nx, class Result> \
|
|
44
|
+
struct remove_function_cvref<auto(Args...) CV REF noexcept(Nx)->Result> { \
|
|
45
|
+
using type = auto(Args...) noexcept(Nx) -> Result; \
|
|
46
|
+
};
|
|
47
|
+
// NOLINTEND(bugprone-macro-parentheses, cppcoreguidelines-macro-usage)
|
|
48
|
+
|
|
49
|
+
MAKE_CVREF(, )
|
|
50
|
+
MAKE_CVREF(, &)
|
|
51
|
+
MAKE_CVREF(, &&)
|
|
52
|
+
MAKE_CVREF(const, )
|
|
53
|
+
MAKE_CVREF(const, &)
|
|
54
|
+
MAKE_CVREF(const, &&)
|
|
55
|
+
MAKE_CVREF(volatile, )
|
|
56
|
+
MAKE_CVREF(volatile, &)
|
|
57
|
+
MAKE_CVREF(volatile, &&)
|
|
58
|
+
MAKE_CVREF(const volatile, )
|
|
59
|
+
MAKE_CVREF(const volatile, &)
|
|
60
|
+
MAKE_CVREF(const volatile, &&)
|
|
61
|
+
#undef MAKE_CVREF
|
|
62
|
+
|
|
63
|
+
// Add the given arguments to the front of a function signature
|
|
64
|
+
template <class Signature, class... Types>
|
|
65
|
+
struct signature_args_push_front;
|
|
66
|
+
|
|
67
|
+
template <class... Args, bool Nx, class Result, class... Types>
|
|
68
|
+
struct signature_args_push_front<auto(Args...) noexcept(Nx)->Result, Types...> {
|
|
69
|
+
using type = auto(Types..., Args...) noexcept(Nx) -> Result;
|
|
70
|
+
};
|
|
71
|
+
|
|
72
|
+
// Given a member function pointer type, extract the required `this` object type
|
|
73
|
+
template <class Function>
|
|
74
|
+
struct member_invocable_type;
|
|
75
|
+
|
|
76
|
+
template <class Function>
|
|
77
|
+
using member_invocable_type_t = member_invocable_type<Function>::type;
|
|
78
|
+
|
|
79
|
+
// Traditional member function pointer
|
|
80
|
+
template <class Type, class Function>
|
|
81
|
+
struct member_invocable_type<Function Type::*>
|
|
82
|
+
: ensure_reference<apply_cvref_t<extract_function_cvref_t<Function>, Type>> {};
|
|
83
|
+
|
|
84
|
+
// Explicit-this member function
|
|
85
|
+
template <class Type, class... Args, bool Nx, class Result>
|
|
86
|
+
struct member_invocable_type<auto (*)(Type, Args...) noexcept(Nx)->Result> {
|
|
87
|
+
using type = Type;
|
|
88
|
+
};
|
|
89
|
+
|
|
90
|
+
// Convert a member function pointer type to a free function type, with the object type as the first
|
|
91
|
+
// parameter
|
|
92
|
+
template <class Type>
|
|
93
|
+
struct unbound_member_function_signature;
|
|
94
|
+
|
|
95
|
+
template <class Type>
|
|
96
|
+
using unbound_member_function_signature_t = unbound_member_function_signature<Type>::type;
|
|
97
|
+
|
|
98
|
+
// Traditional member function pointer
|
|
99
|
+
template <class Type, class Function>
|
|
100
|
+
struct unbound_member_function_signature<Function Type::*>
|
|
101
|
+
: signature_args_push_front<remove_function_cvref_t<Function>, member_invocable_type_t<Function Type::*>> {};
|
|
102
|
+
|
|
103
|
+
// Explicit-this member function
|
|
104
|
+
template <class... Args, bool Nx, class Result>
|
|
105
|
+
struct unbound_member_function_signature<auto (*)(Args...) noexcept(Nx)->Result> {
|
|
106
|
+
using type = auto(Args...) noexcept(Nx) -> Result;
|
|
107
|
+
};
|
|
108
|
+
|
|
109
|
+
// Function signature of any invocable type
|
|
110
|
+
export template <class Function>
|
|
111
|
+
struct function_signature;
|
|
112
|
+
|
|
113
|
+
export template <class Function>
|
|
114
|
+
using function_signature_t = function_signature<Function>::type;
|
|
115
|
+
|
|
116
|
+
template <class Type, class Function>
|
|
117
|
+
struct function_operator_signature;
|
|
118
|
+
|
|
119
|
+
// Function type. `std::decay_t` performs function-to-pointer conversion.
|
|
120
|
+
template <class Function>
|
|
121
|
+
requires requires { typename remove_function_cvref_t<std::remove_pointer_t<std::decay_t<Function>>>; }
|
|
122
|
+
struct function_signature<Function>
|
|
123
|
+
: remove_function_cvref<std::remove_pointer_t<std::decay_t<Function>>> {};
|
|
124
|
+
|
|
125
|
+
// Struct type with `operator()` (lambda, etc)
|
|
126
|
+
template <class Type>
|
|
127
|
+
requires requires { &std::remove_cvref_t<Type>::operator(); }
|
|
128
|
+
struct function_signature<Type>
|
|
129
|
+
: function_operator_signature<Type, unbound_member_function_signature_t<decltype(&std::remove_cvref_t<Type>::operator())>> {};
|
|
130
|
+
|
|
131
|
+
// Member function pointer
|
|
132
|
+
template <class Type, class Function>
|
|
133
|
+
struct function_signature<Function Type::*>
|
|
134
|
+
: unbound_member_function_signature<Function Type::*> {};
|
|
135
|
+
|
|
136
|
+
// Strips the first parameter, which is the unbound object type. Also checks invocability of `Type`,
|
|
137
|
+
// taking into account cvref qualifiers.
|
|
138
|
+
template <class That, class... Args, std::invocable<Args...> Type, bool Nx, class Result>
|
|
139
|
+
struct function_operator_signature<Type, auto(That, Args...) noexcept(Nx)->Result> {
|
|
140
|
+
using type = auto(Args...) noexcept(Nx) -> Result;
|
|
141
|
+
};
|
|
142
|
+
|
|
143
|
+
// Canonicalize the cvref type of an invocable. Functions are value types, invocables are whatever
|
|
144
|
+
// `this` expects.
|
|
145
|
+
export template <class Type>
|
|
146
|
+
struct invocable_type;
|
|
147
|
+
|
|
148
|
+
export template <class Type>
|
|
149
|
+
using invocable_type_t = invocable_type<Type>::type;
|
|
150
|
+
|
|
151
|
+
template <class Function>
|
|
152
|
+
requires requires { typename remove_function_cvref_t<std::remove_pointer_t<std::decay_t<Function>>>; }
|
|
153
|
+
struct invocable_type<Function>
|
|
154
|
+
: remove_function_cvref<std::remove_pointer_t<std::decay_t<Function>>> {};
|
|
155
|
+
|
|
156
|
+
template <class Type>
|
|
157
|
+
requires requires { &std::remove_cvref_t<Type>::operator(); }
|
|
158
|
+
struct invocable_type<Type>
|
|
159
|
+
: member_invocable_type<decltype(&std::remove_cvref_t<Type>::operator())> {};
|
|
160
|
+
|
|
161
|
+
// Result type of a function signature
|
|
162
|
+
export template <class Type>
|
|
163
|
+
struct signature_result;
|
|
164
|
+
|
|
165
|
+
export template <class Type>
|
|
166
|
+
using signature_result_t = signature_result<Type>::type;
|
|
167
|
+
|
|
168
|
+
template <class... Args, bool Nx, class Result>
|
|
169
|
+
struct signature_result<auto(Args...) noexcept(Nx)->Result> {
|
|
170
|
+
using type = Result;
|
|
171
|
+
};
|
|
172
|
+
|
|
173
|
+
} // namespace util
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
module;
|
|
2
|
+
#include <type_traits>
|
|
3
|
+
export module util:type_traits.transform;
|
|
4
|
+
|
|
5
|
+
namespace util {
|
|
6
|
+
|
|
7
|
+
// Add lvalue reference qualifier if it is not already a reference
|
|
8
|
+
export template <class Type>
|
|
9
|
+
struct ensure_reference;
|
|
10
|
+
|
|
11
|
+
export template <class Type>
|
|
12
|
+
using ensure_reference_t = ensure_reference<Type>::type;
|
|
13
|
+
|
|
14
|
+
template <class Type>
|
|
15
|
+
struct ensure_reference {
|
|
16
|
+
using type = Type&;
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
template <class Type>
|
|
20
|
+
struct ensure_reference<Type&&> {
|
|
21
|
+
using type = Type&&;
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
// Copy cvref qualifiers of `From` to `To`
|
|
25
|
+
template <class From, class To>
|
|
26
|
+
struct apply_cvref_impl;
|
|
27
|
+
|
|
28
|
+
export template <class From, class To>
|
|
29
|
+
requires(!(std::is_const_v<To> || std::is_volatile_v<To> || std::is_reference_v<To>))
|
|
30
|
+
struct apply_cvref : apply_cvref_impl<From, To> {};
|
|
31
|
+
|
|
32
|
+
export template <class From, class To>
|
|
33
|
+
using apply_cvref_t = apply_cvref<From, To>::type;
|
|
34
|
+
|
|
35
|
+
template <class From, class To>
|
|
36
|
+
struct apply_cvref_impl : std::type_identity<To> {};
|
|
37
|
+
|
|
38
|
+
// Generated specializations for `apply_ref_impl`
|
|
39
|
+
// NOLINTBEGIN(bugprone-macro-parentheses, cppcoreguidelines-macro-usage)
|
|
40
|
+
#define MAKE_CVREF(CV, REF) \
|
|
41
|
+
template <class From, class To> \
|
|
42
|
+
struct apply_cvref_impl<CV From REF, To> { \
|
|
43
|
+
using type = CV To REF; \
|
|
44
|
+
}; \
|
|
45
|
+
// NOLINTEND(bugprone-macro-parentheses, cppcoreguidelines-macro-usage)
|
|
46
|
+
|
|
47
|
+
MAKE_CVREF(, &)
|
|
48
|
+
MAKE_CVREF(, &&)
|
|
49
|
+
MAKE_CVREF(const, )
|
|
50
|
+
MAKE_CVREF(const, &)
|
|
51
|
+
MAKE_CVREF(const, &&)
|
|
52
|
+
MAKE_CVREF(volatile, )
|
|
53
|
+
MAKE_CVREF(volatile, &)
|
|
54
|
+
MAKE_CVREF(volatile, &&)
|
|
55
|
+
MAKE_CVREF(const volatile, )
|
|
56
|
+
MAKE_CVREF(const volatile, &)
|
|
57
|
+
MAKE_CVREF(const volatile, &&)
|
|
58
|
+
#undef MAKE_CVREF
|
|
59
|
+
|
|
60
|
+
} // namespace util
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
module;
|
|
2
|
+
#include <type_traits>
|
|
3
|
+
export module util:type_traits.type_of;
|
|
4
|
+
|
|
5
|
+
namespace util {
|
|
6
|
+
|
|
7
|
+
// "ADL firewall"
|
|
8
|
+
// https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2022/p2538r0.html#solution
|
|
9
|
+
template <class Type>
|
|
10
|
+
struct hidden {
|
|
11
|
+
struct of {
|
|
12
|
+
using type = Type;
|
|
13
|
+
};
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
template <class Type>
|
|
17
|
+
using hidden_t = hidden<Type>::of;
|
|
18
|
+
|
|
19
|
+
// Metaprogramming helper
|
|
20
|
+
// nb: Intentionally not exported, it should only be used via the `type` variable template.
|
|
21
|
+
template <class Hidden>
|
|
22
|
+
struct type_of;
|
|
23
|
+
|
|
24
|
+
} // namespace util
|
|
25
|
+
|
|
26
|
+
// `util::type_of<Type>{}` alias
|
|
27
|
+
export template <class Type>
|
|
28
|
+
constexpr auto type = util::type_of<util::hidden_t<Type>>{};
|
|
29
|
+
|
|
30
|
+
// Extract the `type` of a value metatype
|
|
31
|
+
export template <auto Type>
|
|
32
|
+
using type_t = decltype(Type)::type;
|
|
33
|
+
|
|
34
|
+
namespace util {
|
|
35
|
+
|
|
36
|
+
// `type_of`
|
|
37
|
+
template <class Hidden>
|
|
38
|
+
struct type_of : std::type_identity<typename Hidden::type> {
|
|
39
|
+
consteval type_of() = default;
|
|
40
|
+
|
|
41
|
+
template <class Type>
|
|
42
|
+
requires std::is_same_v<typename Type::type, typename Hidden::type>
|
|
43
|
+
explicit consteval type_of(Type /*type*/){};
|
|
44
|
+
|
|
45
|
+
// Equality operators
|
|
46
|
+
consteval auto operator==(type_of /*op*/) const -> bool { return true; }
|
|
47
|
+
|
|
48
|
+
template <class Op>
|
|
49
|
+
consteval auto operator==(type_of<Op> /*op*/) const -> bool { return false; }
|
|
50
|
+
|
|
51
|
+
template <class Op>
|
|
52
|
+
consteval auto operator==(Op /*op*/) const -> bool { return std::is_same_v<typename Hidden::type, typename Op::type>; }
|
|
53
|
+
|
|
54
|
+
// `true` represents a valid type
|
|
55
|
+
consteval explicit operator bool() const { return true; }
|
|
56
|
+
|
|
57
|
+
// Invoke result. Returns `std::false_type` if not invocable.
|
|
58
|
+
consteval auto operator()(auto... types) const {
|
|
59
|
+
if constexpr (requires { std::declval<typename Hidden::type>()(std::declval<type_t<types>>()...); }) {
|
|
60
|
+
return type<decltype(std::declval<typename Hidden::type>()(std::declval<type_t<types>>()...))>;
|
|
61
|
+
} else {
|
|
62
|
+
return std::false_type{};
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
};
|
|
66
|
+
|
|
67
|
+
export template <class Type>
|
|
68
|
+
consteval auto remove_cvref(Type /*type*/) {
|
|
69
|
+
return type<std::remove_cvref_t<typename Type::type>>;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
} // namespace util
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
module;
|
|
2
|
+
#include <tuple>
|
|
3
|
+
#include <type_traits>
|
|
4
|
+
export module util:type_traits.type_pack;
|
|
5
|
+
import :type_traits.type_of;
|
|
6
|
+
|
|
7
|
+
namespace util {
|
|
8
|
+
|
|
9
|
+
// Capture a pack of types into a single type
|
|
10
|
+
export template <class... Types>
|
|
11
|
+
struct type_pack;
|
|
12
|
+
|
|
13
|
+
// Prevent `type_pack{type_pack{}}` from flattening itself via copy construction
|
|
14
|
+
template <class... Types>
|
|
15
|
+
type_pack(type_pack<Types...>) -> type_pack<type_pack<Types...>>;
|
|
16
|
+
|
|
17
|
+
// Accept any `T::type` producing structure
|
|
18
|
+
template <class... Types>
|
|
19
|
+
type_pack(Types...) -> type_pack<typename Types::type...>;
|
|
20
|
+
|
|
21
|
+
// ADL-safe pack (not exported)
|
|
22
|
+
template <class... Hidden>
|
|
23
|
+
struct type_pack_of : type_pack<typename Hidden::type...> {
|
|
24
|
+
using type_pack<typename Hidden::type...>::type_pack;
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
// Prevent `type_pack_of{type_pack_of{}}` from flattening itself via copy construction
|
|
28
|
+
template <class... Types>
|
|
29
|
+
type_pack_of(type_pack_of<Types...>) -> type_pack_of<hidden_t<type_pack_of<Types...>>>;
|
|
30
|
+
|
|
31
|
+
// Accept any `T::type` producing structure
|
|
32
|
+
template <class... Types>
|
|
33
|
+
type_pack_of(Types...) -> type_pack_of<hidden_t<typename Types::type>...>;
|
|
34
|
+
|
|
35
|
+
// Capture a pack of types into a single type
|
|
36
|
+
export template <class... Types>
|
|
37
|
+
struct type_pack : std::type_identity<type_pack<Types...>> {
|
|
38
|
+
consteval type_pack() = default;
|
|
39
|
+
|
|
40
|
+
template <class... Args>
|
|
41
|
+
explicit consteval type_pack(Args... /*types*/)
|
|
42
|
+
requires((... && (type<typename Args::type> == type<Types>))) {}
|
|
43
|
+
|
|
44
|
+
[[nodiscard]] consteval auto at(auto index) const { return get<index>(); }
|
|
45
|
+
|
|
46
|
+
// Structured binding accessor
|
|
47
|
+
// Explicit return type causes error:
|
|
48
|
+
// "candidate template ignored: substitution failure [with Index = 0]: invalid index 0 for pack 'Types' of size 0"
|
|
49
|
+
// ...even with `requires(Index < sizeof...(Types))` on the template and/or signature
|
|
50
|
+
template <std::size_t Index>
|
|
51
|
+
[[nodiscard]] consteval auto get() const /* -> std::type_identity<Types... [ Index ]> */ {
|
|
52
|
+
// return {};
|
|
53
|
+
return type<Types...[ Index ]>;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
// Find the index of the given type, or `size()` if not found
|
|
57
|
+
template <class Type>
|
|
58
|
+
[[nodiscard]] consteval auto find(std::type_identity<Type> subject) const -> std::size_t {
|
|
59
|
+
constexpr auto type_switch = [ = ]<std::size_t Index>(this const auto& self, std::integral_constant<std::size_t, Index> /*index*/) -> std::size_t {
|
|
60
|
+
if constexpr (Index == sizeof...(Types)) {
|
|
61
|
+
return sizeof...(Types);
|
|
62
|
+
} else if constexpr (type<Types...[ Index ]> == subject) {
|
|
63
|
+
return Index;
|
|
64
|
+
} else {
|
|
65
|
+
return self(std::integral_constant<std::size_t, Index + 1>{});
|
|
66
|
+
}
|
|
67
|
+
};
|
|
68
|
+
return type_switch(std::integral_constant<std::size_t, 0>{});
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
[[nodiscard]] consteval auto size() const -> std::size_t { return sizeof...(Types); }
|
|
72
|
+
|
|
73
|
+
// Concatenate two `type_pack`s (into a `type_pack_of`)
|
|
74
|
+
template <class... Right>
|
|
75
|
+
[[nodiscard]] consteval auto operator+(type_pack<Right...> /*right*/) const
|
|
76
|
+
-> type_pack_of<hidden_t<Types>..., hidden_t<Right>...> {
|
|
77
|
+
return {};
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
template <class... Right>
|
|
81
|
+
[[nodiscard]] consteval auto operator==(type_pack<Right...> /*right*/) const -> bool {
|
|
82
|
+
if constexpr (sizeof...(Types) == 0 && sizeof...(Right) == 0) {
|
|
83
|
+
return true;
|
|
84
|
+
} else if constexpr (sizeof...(Types) != sizeof...(Right)) {
|
|
85
|
+
return false;
|
|
86
|
+
} else {
|
|
87
|
+
return ((type<Types> == type<Right>) && ...);
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
};
|
|
91
|
+
|
|
92
|
+
} // namespace util
|
|
93
|
+
|
|
94
|
+
namespace std {
|
|
95
|
+
|
|
96
|
+
// `util::type_pack` destructuring specializations
|
|
97
|
+
template <std::size_t Index, class... Types>
|
|
98
|
+
struct tuple_element<Index, util::type_pack<Types...>> {
|
|
99
|
+
using type = util::type_of<util::hidden_t<Types...[ Index ]>>;
|
|
100
|
+
};
|
|
101
|
+
|
|
102
|
+
template <class... Types>
|
|
103
|
+
struct tuple_size<util::type_pack<Types...>> {
|
|
104
|
+
constexpr static auto value = sizeof...(Types);
|
|
105
|
+
};
|
|
106
|
+
|
|
107
|
+
// `util::type_pack_of` destructuring specializations
|
|
108
|
+
template <std::size_t Index, class... Hidden>
|
|
109
|
+
struct tuple_element<Index, util::type_pack_of<Hidden...>>
|
|
110
|
+
: tuple_element<Index, util::type_pack<typename Hidden::type...>> {};
|
|
111
|
+
|
|
112
|
+
template <class... Hidden>
|
|
113
|
+
struct tuple_size<util::type_pack_of<Hidden...>>
|
|
114
|
+
: tuple_size<util::type_pack<typename Hidden::type...>> {};
|
|
115
|
+
|
|
116
|
+
} // namespace std
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
module;
|
|
2
|
+
#include <algorithm>
|
|
3
|
+
export module util:utility.constant_wrapper;
|
|
4
|
+
|
|
5
|
+
// Lifted from P2781R8:
|
|
6
|
+
// https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2025/p2781r8.html
|
|
7
|
+
|
|
8
|
+
// NOLINTBEGIN(cppcoreguidelines-pro-bounds-array-to-pointer-decay, modernize-avoid-c-arrays)
|
|
9
|
+
namespace util {
|
|
10
|
+
|
|
11
|
+
template <class Type>
|
|
12
|
+
struct fixed_value {
|
|
13
|
+
using type = Type;
|
|
14
|
+
// NOLINTNEXTLINE(google-explicit-constructor)
|
|
15
|
+
consteval fixed_value(type value) noexcept : value{value} {}
|
|
16
|
+
type value;
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
template <class Type, std::size_t Extent>
|
|
20
|
+
struct fixed_value<Type[ Extent ]> {
|
|
21
|
+
using type = Type[ Extent ];
|
|
22
|
+
// NOLINTNEXTLINE(google-explicit-constructor)
|
|
23
|
+
consteval fixed_value(const type& init) noexcept { std::copy_n(init, Extent, value); }
|
|
24
|
+
// NOLINTNEXTLINE(cppcoreguidelines-avoid-const-or-ref-data-members)
|
|
25
|
+
type value;
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
template <class Type, std::size_t Extent>
|
|
29
|
+
fixed_value(const Type (&)[ Extent ]) -> fixed_value<Type[ Extent ]>;
|
|
30
|
+
|
|
31
|
+
export template <fixed_value Value>
|
|
32
|
+
struct constant_wrapper;
|
|
33
|
+
|
|
34
|
+
template <class Value>
|
|
35
|
+
concept constexpr_param = requires { typename constant_wrapper<Value::value>; };
|
|
36
|
+
|
|
37
|
+
struct cw_operators {
|
|
38
|
+
template <constexpr_param Left, constexpr_param Right>
|
|
39
|
+
constexpr friend auto operator==(Left, Right) noexcept -> constant_wrapper<(Left::value == Right::value)> { return {}; }
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
template <fixed_value Value>
|
|
43
|
+
struct constant_wrapper : cw_operators {
|
|
44
|
+
using value_type = decltype(Value)::type;
|
|
45
|
+
using type = constant_wrapper;
|
|
46
|
+
|
|
47
|
+
constexpr static auto& value = Value.value;
|
|
48
|
+
|
|
49
|
+
// NOLINTNEXTLINE(google-explicit-constructor)
|
|
50
|
+
consteval operator const value_type&() const noexcept { return value; }
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
export template <fixed_value Value>
|
|
54
|
+
constexpr auto cw = constant_wrapper<Value>{};
|
|
55
|
+
|
|
56
|
+
} // namespace util
|
|
57
|
+
// NOLINTEND(cppcoreguidelines-pro-bounds-array-to-pointer-decay, modernize-avoid-c-arrays)
|