@auto_js/utility 0.0.8 → 0.0.10

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 CHANGED
@@ -26,6 +26,7 @@ target_sources(utility_js
26
26
  memory/memory.cc
27
27
  memory/noinit_allocator.cc
28
28
  meta/algorithm.cc
29
+ platform/format.cc
29
30
  platform/lockable.cc
30
31
  platform/stop_token.cc
31
32
  platform/timer.h.cc
@@ -34,6 +35,7 @@ target_sources(utility_js
34
35
  type_traits/type_of.cc
35
36
  type_traits/type_pack.cc
36
37
  type_traits/type_traits.cc
38
+ utility/array.cc
37
39
  utility/constant_wrapper.cc
38
40
  utility/covariant_value.cc
39
41
  utility/facade.cc
package/_module.cc CHANGED
@@ -7,6 +7,7 @@ export import :functional;
7
7
  export import :functional.flat_tuple;
8
8
  export import :memory;
9
9
  export import :meta.algorithm;
10
+ export import :platform.format;
10
11
  export import :platform.lockable;
11
12
  export import :platform.stop_token;
12
13
  export import :platform.timer;
package/assertions.cc CHANGED
@@ -1,7 +1,7 @@
1
1
  import std;
2
2
  import util;
3
3
 
4
- constexpr auto assert_strings_equal(auto... strings) {
4
+ consteval auto assert_strings_equal(auto... strings) {
5
5
  const auto [... left ] = std::tuple{std::basic_string_view{strings}...};
6
6
  const auto [... right ] = std::tuple{std::basic_string_view{strings}...};
7
7
  (..., ([ = ](auto left) -> void {
@@ -13,7 +13,7 @@ constexpr auto assert_strings_equal(auto... strings) {
13
13
  }(left)));
14
14
  }
15
15
 
16
- static_assert([]() -> int {
16
+ static_assert([] -> int {
17
17
  // https://en.cppreference.com/w/cpp/language/string_literal.html
18
18
 
19
19
  // Quick check of interpolation matrix
@@ -52,7 +52,7 @@ class sealed_map {
52
52
  requires std::default_initializable<Type> :
53
53
  sealed_map{
54
54
  sorted_equivalent_t{},
55
- util::elide{[ & ]() -> container_type {
55
+ util::elide{[ & ] -> container_type {
56
56
  auto keys_array = std::array<key_type, Size>{std::move(keys)...};
57
57
  std::ranges::sort(keys_array);
58
58
  auto [... sorted_keys ] = std::move(keys_array);
@@ -64,7 +64,7 @@ class sealed_map {
64
64
  explicit consteval sealed_map(std::in_place_t /*tag*/, auto... pairs) :
65
65
  sealed_map{
66
66
  sorted_equivalent_t{},
67
- util::elide{[ & ]() -> container_type {
67
+ util::elide{[ & ] -> container_type {
68
68
  // Sort without invoking `operator()=`, which might not exist on the value type
69
69
  auto entries = std::array<value_type, Size>{std::move(pairs)...};
70
70
  auto indices = std::array<std::size_t, Size>{};
@@ -9,8 +9,8 @@ namespace util {
9
9
 
10
10
  // Captures the given parameters in way such that empty objects yield an empty and
11
11
  // default-constructible invocable. See:
12
- // static_assert(std::is_empty_v<decltype([]() {})>);
13
- // static_assert(!std::is_empty_v<decltype([ a = std::monostate{} ]() {})>);
12
+ // static_assert(std::is_empty_v<decltype([] {})>);
13
+ // static_assert(!std::is_empty_v<decltype([ a = std::monostate{} ] {})>);
14
14
  export template <class Invocable, class... Bound>
15
15
  class bind;
16
16
 
@@ -58,7 +58,7 @@ class bind_overloaded : private bind_storage<Invocable, Bound...> {
58
58
 
59
59
  private:
60
60
  constexpr auto invoke(this auto&& self, auto&&... args) -> decltype(auto) {
61
- const auto [... indices ] = util::sequence<sizeof...(Bound)>;
61
+ constexpr auto [... indices ] = util::sequence<sizeof...(Bound)>;
62
62
  // NOLINTNEXTLINE(bugprone-use-after-move)
63
63
  return std::forward<decltype(self)>(self).invocable_(
64
64
  // NOLINTNEXTLINE(bugprone-use-after-move)
@@ -106,7 +106,7 @@ class bind_explicit<Invocable, auto(Args...) noexcept(Nx)->Result, Bound...> : p
106
106
 
107
107
  private:
108
108
  constexpr auto invoke(this auto&& self, Args /*&&*/... args) noexcept(Nx) -> Result {
109
- const auto [... indices ] = util::sequence<sizeof...(Bound)>;
109
+ constexpr auto [... indices ] = util::sequence<sizeof...(Bound)>;
110
110
  // NOLINTNEXTLINE(bugprone-use-after-move)
111
111
  return std::forward<this_type>(self).invocable_(
112
112
  // NOLINTNEXTLINE(bugprone-use-after-move)
@@ -70,6 +70,9 @@ struct flat_tuple : flat_tuple_storage_t<Types...> {
70
70
  using flat_tuple_storage_t<Types...>::flat_tuple_storage_t;
71
71
  };
72
72
 
73
+ template <class... Types>
74
+ flat_tuple(Types...) -> flat_tuple<Types...>;
75
+
73
76
  // `std::get` replacement for `flat_tuple`
74
77
  export template <std::size_t Index, class... Types>
75
78
  constexpr auto get(flat_tuple<Types...>& tuple) -> auto& {
@@ -87,3 +90,16 @@ constexpr auto get(const flat_tuple<Types...>& tuple) -> const auto& {
87
90
  }
88
91
 
89
92
  } // namespace util
93
+
94
+ namespace std {
95
+ // `util::flat_tuple` destructuring specializations
96
+ template <std::size_t Index, class... Types>
97
+ struct tuple_element<Index, util::flat_tuple<Types...>> {
98
+ using type = Types...[ Index ];
99
+ };
100
+
101
+ template <class... Types>
102
+ struct tuple_size<util::flat_tuple<Types...>> {
103
+ constexpr static auto value = sizeof...(Types);
104
+ };
105
+ } // namespace std
@@ -43,9 +43,9 @@ constexpr auto invoke_this_as(auto&& func, auto&&... args)
43
43
  // no parameters, returning that result.
44
44
  export constexpr auto template_traverse(auto values_pack, const auto& invoke) -> decltype(auto) {
45
45
  const auto fold = util::overloaded{
46
- [ & ]() -> decltype(auto) { return invoke(); },
46
+ [ & ] -> decltype(auto) { return invoke(); },
47
47
  [ & ](this const auto& self, auto value, auto... values) -> decltype(auto) {
48
- return invoke(value, [ & ]() -> decltype(auto) { return self(values...); });
48
+ return invoke(value, [ & ] -> decltype(auto) { return self(values...); });
49
49
  },
50
50
  };
51
51
  const auto [... values ] = values_pack;
@@ -56,7 +56,7 @@ export constexpr auto template_traverse(auto values_pack, const auto& invoke) ->
56
56
  // `identity` is ignored.
57
57
  export constexpr auto template_fold(auto values_pack, auto identity, const auto& invoke) -> decltype(auto) {
58
58
  const auto fold = util::overloaded{
59
- [ & ]() -> decltype(auto) { return identity; },
59
+ [ & ] -> decltype(auto) { return identity; },
60
60
  [ & ](auto left) -> decltype(auto) { return left; },
61
61
  [ & ](this const auto& self, auto left, auto right, auto... values) -> decltype(auto) {
62
62
  return self(invoke(left, right), values...);
@@ -102,14 +102,14 @@ export constexpr auto template_switch(const auto& value, auto case_pack, auto in
102
102
  // 37 | static_assert(transfer<double>(std::variant<int, double>{1.1}) == 1.1);
103
103
  // | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
104
104
  // /workspace/packages/utility/functional/functional.cc:73:11: note: subobject of type 'const util::overloaded<(lambda at /workspace/packages/utility/functional/functional.cc:85:4), (lambda at /workspace/packages/utility/functional/functional.cc:86:4)> &const' is not initialized
105
- // 73 | return invoke(value, [ & ]() -> decltype(auto) { return self(values...); });
105
+ // 73 | return invoke(value, [ & ] -> decltype(auto) { return self(values...); });
106
106
  //
107
107
  // export constexpr auto template_switch(const auto& value, auto case_pack, auto invoke) -> decltype(auto) {
108
108
  // return template_traverse(
109
109
  // case_pack,
110
110
  // util::overloaded{
111
111
  // [ & ](auto case_, auto next) -> decltype(auto) { return value == case_ ? invoke(case_) : next(); },
112
- // [ & ]() -> decltype(auto) { return invoke(); },
112
+ // [ & ] -> decltype(auto) { return invoke(); },
113
113
  // }
114
114
  // );
115
115
  // }
@@ -32,7 +32,7 @@ class noinit_allocator {
32
32
 
33
33
  constexpr auto construct(pointer ptr, auto&&... args) -> void
34
34
  requires std::constructible_from<Type, decltype(args)...> {
35
- auto construct = [ & ]() constexpr -> void {
35
+ auto construct = [ & ] constexpr -> void {
36
36
  std::allocator_traits<Allocator>::construct(allocator_, ptr, std::forward<decltype(args)>(args)...);
37
37
  };
38
38
  if consteval {
package/meta/algorithm.cc CHANGED
@@ -22,16 +22,10 @@ constexpr auto spread_type_pack = util::overloaded{
22
22
  },
23
23
  };
24
24
 
25
- // Concatenate 0..N `util::type_packs` together
26
- export constexpr auto pack_concat = util::overloaded{
27
- []() consteval -> auto { return type_pack{}; },
28
- [](auto&&... packs) consteval -> auto { return (... + make_type_pack(packs)); },
29
- };
30
-
31
25
  // Apply transformation function to each type in the pack
32
26
  export constexpr auto pack_transform = [](auto pack, auto unary) consteval -> auto {
33
- const auto [... types ] = pack;
34
- return pack_concat(unary(types)...);
27
+ constexpr auto [... types ] = pack;
28
+ return (type_pack{} + ... + make_type_pack(unary(types)));
35
29
  };
36
30
 
37
31
  // Return a new `type_pack` with types matching the predicate
@@ -44,37 +38,32 @@ export constexpr auto pack_filter = [](auto pack, auto predicate) consteval -> a
44
38
  return type_pack{};
45
39
  }
46
40
  };
47
- return pack_transform(pack, filter);
41
+ return pack_transform(make_type_pack(pack), filter);
48
42
  };
49
43
 
50
- // Return a nested `type_pack` of `tuple_pack`'s containing types filtered by the predicates
51
- // nb: `predicates` must be `util::fn<...>`
52
- export constexpr auto pack_partition = util::overloaded{
53
- [](auto pack) consteval -> auto { return type_pack{pack}; },
54
- []<class Predicate>(this auto pack_partition, auto pack, Predicate predicate, auto... predicates) consteval -> auto {
55
- // `std::not_fn()` doesn't work
56
- constexpr auto not_fn = util::fn<[](auto type) -> bool { return !Predicate{}(type); }>;
57
- const auto left = pack_filter(pack, predicate);
58
- const auto right = pack_filter(pack, not_fn);
59
- return pack_concat(pack_partition(left), pack_partition(right, predicates...));
60
- },
44
+ // Return a nested `type_pack` of two `tuple_pack`'s containing types filtered by the predicate.
45
+ export constexpr auto pack_partition = []<auto Predicate>(auto pack, function_constant<Predicate> predicate) consteval -> auto {
46
+ // `std::not_fn()` doesn't work
47
+ constexpr auto not_fn = util::fn<[](auto type) -> bool { return !Predicate(type); }>;
48
+ const auto left = pack_filter(pack, predicate);
49
+ const auto right = pack_filter(pack, not_fn);
50
+ return type_pack_of{left, right};
61
51
  };
62
52
 
63
53
  // Return unique types from the pack
64
54
  export constexpr auto pack_unique = util::overloaded{
65
- []() consteval -> auto { return type_pack{}; },
55
+ [] consteval -> auto { return type_pack{}; },
66
56
  [](auto... types) consteval -> auto {
67
- constexpr auto transform = [ = ](auto index) {
68
- constexpr auto subject = types...[ index ];
69
- const auto [... indices ] = util::sequence<index>;
70
- if constexpr ((... || (subject == types...[ indices ]))) {
57
+ constexpr auto [... ii ] = util::sequence<sizeof...(types)>;
58
+ return (... + [ & ] -> auto {
59
+ constexpr auto subject = types...[ ii ];
60
+ constexpr auto [... jj ] = util::sequence<ii>;
61
+ if constexpr ((... || (subject == types...[ jj ]))) {
71
62
  return type_pack_of{};
72
63
  } else {
73
64
  return type_pack_of{subject};
74
65
  }
75
- };
76
- const auto [... indices ] = util::sequence<sizeof...(types)>;
77
- return (... + transform(indices));
66
+ }());
78
67
  },
79
68
  };
80
69
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@auto_js/utility",
3
- "version": "0.0.8",
3
+ "version": "0.0.10",
4
4
  "author": "https://github.com/laverdet/",
5
5
  "homepage": "https://github.com/laverdet/isolated-vm/tree/experimental/packages/utility",
6
6
  "license": "ISC",
@@ -0,0 +1,181 @@
1
+ module;
2
+ #include <version>
3
+ export module util:platform.format;
4
+ import std;
5
+
6
+ namespace util {
7
+
8
+ #if __GLIBCXX__
9
+
10
+ // Claude-generated `std::format` fill to avoid gcc 16.2.0 libstdc++ requirement.
11
+
12
+ template <class Type>
13
+ struct printf_arg {
14
+ static_assert(false, "type is not formattable by the `util::format` polyfill");
15
+ };
16
+
17
+ template <>
18
+ struct printf_arg<bool> {
19
+ constexpr static auto spec = std::string_view{"%s"};
20
+ constexpr static auto pass(bool value) { return std::tuple{value ? "true" : "false"}; }
21
+ };
22
+
23
+ template <>
24
+ struct printf_arg<char> {
25
+ constexpr static auto spec = std::string_view{"%c"};
26
+ constexpr static auto pass(char value) { return std::tuple{value}; }
27
+ };
28
+
29
+ template <class Type>
30
+ requires std::signed_integral<Type>
31
+ struct printf_arg<Type> {
32
+ constexpr static auto spec = std::string_view{"%lld"};
33
+ constexpr static auto pass(Type value) { return std::tuple{static_cast<long long>(value)}; }
34
+ };
35
+
36
+ template <class Type>
37
+ requires std::unsigned_integral<Type>
38
+ struct printf_arg<Type> {
39
+ constexpr static auto spec = std::string_view{"%llu"};
40
+ constexpr static auto pass(Type value) { return std::tuple{static_cast<unsigned long long>(value)}; }
41
+ };
42
+
43
+ template <class Type>
44
+ requires(std::same_as<Type, float> || std::same_as<Type, double>)
45
+ struct printf_arg<Type> {
46
+ constexpr static auto spec = std::string_view{"%g"};
47
+ constexpr static auto pass(Type value) { return std::tuple{static_cast<double>(value)}; }
48
+ };
49
+
50
+ template <>
51
+ struct printf_arg<long double> {
52
+ constexpr static auto spec = std::string_view{"%Lg"};
53
+ constexpr static auto pass(long double value) { return std::tuple{value}; }
54
+ };
55
+
56
+ template <>
57
+ struct printf_arg<const char*> {
58
+ constexpr static auto spec = std::string_view{"%s"};
59
+ constexpr static auto pass(const char* value) { return std::tuple{value}; }
60
+ };
61
+
62
+ template <>
63
+ struct printf_arg<char*> : printf_arg<const char*> {};
64
+
65
+ template <>
66
+ struct printf_arg<std::string_view> {
67
+ constexpr static auto spec = std::string_view{"%.*s"};
68
+ constexpr static auto pass(std::string_view value) {
69
+ return std::tuple{static_cast<int>(value.size()), value.data()};
70
+ }
71
+ };
72
+
73
+ template <>
74
+ struct printf_arg<std::string> : printf_arg<std::string_view> {};
75
+
76
+ template <>
77
+ struct printf_arg<const void*> {
78
+ constexpr static auto spec = std::string_view{"%p"};
79
+ constexpr static auto pass(const void* value) { return std::tuple{value}; }
80
+ };
81
+
82
+ template <>
83
+ struct printf_arg<void*> : printf_arg<const void*> {};
84
+
85
+ template <>
86
+ struct printf_arg<std::nullptr_t> {
87
+ constexpr static auto spec = std::string_view{"%p"};
88
+ constexpr static auto pass(std::nullptr_t /*value*/) { return std::tuple{static_cast<const void*>(nullptr)}; }
89
+ };
90
+
91
+ // Checks the format string against the argument types and rewrites `{}` replacement fields into
92
+ // printf conversion specifiers, all at compile time. Only plain `{}` fields are supported --
93
+ // `{0}`-style indices and `{:..}` format specifiers are rejected.
94
+ export template <class... Args>
95
+ class basic_format_string {
96
+ public:
97
+ // NOLINTNEXTLINE(modernize-avoid-c-arrays)
98
+ template <std::size_t Size>
99
+ consteval basic_format_string(const char (&format)[ Size ]) {
100
+ auto out = string_.begin();
101
+ auto put = [ & ](char character) {
102
+ if (out == string_.end() - 1) {
103
+ throw std::format_error{"format string exceeds polyfill capacity"};
104
+ }
105
+ *out++ = character;
106
+ };
107
+ auto emit = [ & ](std::string_view string) {
108
+ for (auto character : string) {
109
+ put(character);
110
+ }
111
+ };
112
+ constexpr auto specs = std::array<std::string_view, sizeof...(Args)>{printf_arg<std::decay_t<Args>>::spec...};
113
+ std::size_t argument = 0;
114
+ auto view = std::string_view{format, Size - 1};
115
+ for (auto it = view.begin(); it != view.end(); ++it) {
116
+ switch (*it) {
117
+ case '{':
118
+ if (++it == view.end()) {
119
+ throw std::format_error{"unmatched '{' in format string"};
120
+ } else if (*it == '{') {
121
+ put('{');
122
+ } else if (*it == '}') {
123
+ if (argument == specs.size()) {
124
+ throw std::format_error{"too few arguments for format string"};
125
+ }
126
+ emit(specs[ argument++ ]);
127
+ } else {
128
+ throw std::format_error{"format specifiers are not supported by the `util::format` polyfill"};
129
+ }
130
+ break;
131
+ case '}':
132
+ if (++it == view.end() || *it != '}') {
133
+ throw std::format_error{"unmatched '}' in format string"};
134
+ }
135
+ put('}');
136
+ break;
137
+ case '%':
138
+ emit("%%");
139
+ break;
140
+ default:
141
+ put(*it);
142
+ }
143
+ }
144
+ }
145
+
146
+ [[nodiscard]] constexpr auto get() const -> const char* { return string_.data(); }
147
+
148
+ private:
149
+ std::array<char, 256> string_{};
150
+ };
151
+
152
+ export template <class... Args>
153
+ using format_string = basic_format_string<std::type_identity_t<Args>...>;
154
+
155
+ export template <class... Args>
156
+ [[nodiscard]] auto format(format_string<Args...> format, Args&&... args) -> std::string {
157
+ return std::apply(
158
+ [ & ](auto... values) -> std::string {
159
+ auto length = std::max(0, std::snprintf(nullptr, 0, format.get(), values...));
160
+ auto result = std::string{};
161
+ result.resize(static_cast<std::size_t>(length));
162
+ std::snprintf(result.data(), result.size() + 1, format.get(), values...);
163
+ return result;
164
+ },
165
+ std::tuple_cat(printf_arg<std::decay_t<Args>>::pass(args)...)
166
+ );
167
+ }
168
+
169
+ #else
170
+
171
+ export template <class... Args>
172
+ using format_string = std::format_string<Args...>;
173
+
174
+ export template <class... Args>
175
+ [[nodiscard]] auto format(std::format_string<std::type_identity_t<Args>...> format, Args&&... args) -> std::string {
176
+ return std::format(format, std::forward<Args>(args)...);
177
+ }
178
+
179
+ #endif
180
+
181
+ } // namespace util
@@ -230,15 +230,15 @@ struct lockable_traits {
230
230
  };
231
231
 
232
232
  template <class Resource, lockable_traits Traits = {}>
233
- constexpr auto make_lockable_traits = []() consteval {
234
- using mutex_type = type_t<[]() {
233
+ constexpr auto make_lockable_traits = [] consteval {
234
+ using mutex_type = type_t<[] {
235
235
  if constexpr (Traits.shared) {
236
236
  return type<std::shared_mutex>;
237
237
  } else {
238
238
  return type<std::mutex>;
239
239
  }
240
240
  }()>;
241
- using cv_type = type_t<[]() {
241
+ using cv_type = type_t<[] {
242
242
  if constexpr (Traits.interuptable) {
243
243
  return type<std::condition_variable_any>;
244
244
  } else if constexpr (Traits.notifiable) {
@@ -45,7 +45,7 @@ class composite_stop_token : private std::stop_source, public std::stop_token {
45
45
  template <class Source, class... Tokens>
46
46
  explicit composite_stop_token(Source&& source, int stoppable, Tokens&&... tokens) :
47
47
  std::stop_source{std::forward<Source>(source)},
48
- std::stop_token{util::elide{[ & ]() -> std::stop_token {
48
+ std::stop_token{util::elide{[ & ] -> std::stop_token {
49
49
  switch (stoppable) {
50
50
  // No `stop_token`
51
51
  case 0: return std::stop_token{};
@@ -53,7 +53,7 @@ class composite_stop_token : private std::stop_source, public std::stop_token {
53
53
  case 1:
54
54
  {
55
55
  constexpr auto fold = util::overloaded{
56
- []() -> std::stop_token { std::unreachable(); },
56
+ [] -> std::stop_token { std::unreachable(); },
57
57
  [](auto&& token) -> std::stop_token { return std::forward<decltype(token)>(token); },
58
58
  [](this auto& fold, auto&& token, auto&&... tokens) -> std::stop_token {
59
59
  if (token.stop_possible()) {
@@ -69,7 +69,7 @@ class composite_stop_token : private std::stop_source, public std::stop_token {
69
69
  default: return std::stop_source::get_token();
70
70
  }
71
71
  }}},
72
- callbacks_{[ & ]() -> callback_pack_type {
72
+ callbacks_{[ & ] -> callback_pack_type {
73
73
  switch (stoppable) {
74
74
  // No-op callback pack
75
75
  case 0:
package/platform/timer.cc CHANGED
@@ -63,7 +63,7 @@ auto timer_thread::loop(std::stop_token stop_token) -> void {
63
63
  auto lock = requests_.write_waitable(std::not_fn(&request_queue_type::empty));
64
64
  while (!stop_token.stop_requested()) {
65
65
  // wait for requests or timeout
66
- const auto has_requests = [ & ]() -> bool {
66
+ const auto has_requests = [ & ] -> bool {
67
67
  if (timers_.empty()) {
68
68
  return lock.wait(stop_token);
69
69
  } else {
@@ -120,7 +120,7 @@ auto timer_thread::dispatch(Invocable invocable, auto&&... args) {
120
120
  result = implementation(queue);
121
121
  };
122
122
  dispatch(request_type{request});
123
- return *std::move(result).value_or(util::elide{[]() -> result_type {
123
+ return *std::move(result).value_or(util::elide{[] -> result_type {
124
124
  std::unreachable();
125
125
  }});
126
126
  }
@@ -42,13 +42,10 @@ struct type_pack : std::type_identity<type_pack<Types...>> {
42
42
  [[nodiscard]] consteval auto at(auto index) const { return get<index>(); }
43
43
 
44
44
  // Structured binding accessor
45
- // Explicit return type causes error:
46
- // "candidate template ignored: substitution failure [with Index = 0]: invalid index 0 for pack 'Types' of size 0"
47
- // ...even with `requires(Index < sizeof...(Types))` on the template and/or signature
48
45
  template <std::size_t Index>
49
- [[nodiscard]] consteval auto get() const /* -> std::type_identity<Types... [ Index ]> */ {
50
- // return {};
51
- return type<Types...[ Index ]>;
46
+ // https://gcc.gnu.org/bugzilla/show_bug.cgi?id=126796
47
+ [[nodiscard]] consteval auto get() const -> auto {
48
+ return type_of<hidden_t<Types... [ Index ]>>{};
52
49
  }
53
50
 
54
51
  // Find the index of the given type, or `size()` if not found
@@ -27,12 +27,12 @@ struct safe_numeric_conversion {
27
27
  export template <class From, class To>
28
28
  constexpr auto safe_numeric_conversion_v = safe_numeric_conversion<From, To>::value;
29
29
 
30
- static_assert(safe_numeric_conversion<std::int32_t, std::int64_t>::value);
31
- static_assert(!safe_numeric_conversion<std::int32_t, std::uint64_t>::value);
32
- static_assert(!safe_numeric_conversion<std::int64_t, double>::value);
33
- static_assert(safe_numeric_conversion<std::int32_t, double>::value);
34
- static_assert(safe_numeric_conversion<float, double>::value);
35
- static_assert(!safe_numeric_conversion<double, float>::value);
30
+ static_assert(safe_numeric_conversion_v<std::int32_t, std::int64_t>);
31
+ static_assert(!safe_numeric_conversion_v<std::int32_t, std::uint64_t>);
32
+ static_assert(!safe_numeric_conversion_v<std::int64_t, double>);
33
+ static_assert(safe_numeric_conversion_v<std::int32_t, double>);
34
+ static_assert(safe_numeric_conversion_v<float, double>);
35
+ static_assert(!safe_numeric_conversion_v<double, float>);
36
36
 
37
37
  // Resolves to the type of every type in the pack. If any type differs then it is ill-formed.
38
38
  template <class... Type>
@@ -0,0 +1,40 @@
1
+ export module util:utility.array;
2
+ import std;
3
+
4
+ namespace util {
5
+
6
+ // Array value which can be destructured in constexpr
7
+ export template <class Type, std::size_t Size>
8
+ struct constexpr_array {
9
+ constexpr auto begin() -> Type* { return values; }
10
+ // NOLINTNEXTLINE(modernize-avoid-c-arrays)
11
+ Type values[ Size ];
12
+ };
13
+
14
+ template <class Type>
15
+ struct constexpr_array<Type, 0> {
16
+ constexpr auto begin() -> Type* { return nullptr; }
17
+ };
18
+
19
+ template <class... Types>
20
+ constexpr_array(Types...) -> constexpr_array<Types...[ 0 ], sizeof...(Types)>;
21
+
22
+ export template <std::size_t Index, class Type, std::size_t Size>
23
+ constexpr auto get(const constexpr_array<Type, Size>& array) -> Type {
24
+ return array.values[ Index ];
25
+ }
26
+
27
+ } // namespace util
28
+
29
+ namespace std {
30
+ // `util::constexpr_array` destructuring specializations
31
+ template <std::size_t Index, class Type, std::size_t Size>
32
+ struct tuple_element<Index, util::constexpr_array<Type, Size>> {
33
+ using type = Type;
34
+ };
35
+
36
+ template <class Type, std::size_t Size>
37
+ struct tuple_size<util::constexpr_array<Type, Size>> {
38
+ constexpr static auto value = Size;
39
+ };
40
+ } // namespace std
@@ -1,22 +1,29 @@
1
1
  export module util:utility.sequence;
2
+ import :utility.array;
3
+ import :utility.constant_wrapper;
2
4
  import std;
3
5
 
4
6
  namespace util {
5
7
 
6
- // Return a sequence of index constants
8
+ // Return a sequence of `std::integral_constant`.
9
+ // Keep an eye on P1789 and then replace with `std::index_sequence_for`.
7
10
  export template <std::size_t Size>
8
- constexpr auto sequence = []() consteval -> auto {
9
- // With C++26 P2686 we can do constexpr decomposition. So instead of the `tuple` of
10
- // `integral_constants` it can be an array of `std::size_t`.
11
- // https://clang.llvm.org/cxx_status.html
12
-
13
- // std::array<std::size_t, Size> result{};
14
- // std::ranges::copy(std::ranges::views::iota(std::size_t{0}, Size), result.begin());
15
- // return result;
16
-
11
+ constexpr auto sequence_cw = [] consteval -> auto {
17
12
  return []<std::size_t... Index>(std::index_sequence<Index...> /*sequence*/) consteval {
18
- return std::tuple{std::integral_constant<std::size_t, Index>{}...};
13
+ return std::tuple{util::cw<Index>...};
19
14
  }(std::make_index_sequence<Size>());
20
15
  }();
21
16
 
17
+ // Return a sequence of constexpr indices
18
+ export template <std::size_t Size>
19
+ constexpr auto sequence = [] consteval -> auto {
20
+ #if defined(__clang__)
21
+ return sequence_cw<Size>;
22
+ #else
23
+ constexpr_array<std::size_t, Size> result{};
24
+ std::ranges::copy(std::ranges::views::iota(std::size_t{0}, Size), result.begin());
25
+ return result;
26
+ #endif
27
+ }();
28
+
22
29
  } // namespace util
package/utility/string.cc CHANGED
@@ -23,14 +23,14 @@ consteval auto make_consteval_string_view(constant_wrapper<Value> /*cw*/) -> std
23
23
  export template <auto... Strings>
24
24
  consteval auto consteval_strcat(constant_wrapper<Strings>... /*strings*/) {
25
25
  using char_type = identical_type_t<std::remove_extent_t<typename constant_wrapper<Strings>::value_type>...>;
26
- constexpr auto chars = [ & ]() -> auto {
26
+ constexpr auto chars = [ & ] -> auto {
27
27
  constexpr auto size = (... + (std::extent_v<typename constant_wrapper<Strings>::value_type> - 1));
28
28
  std::array<char_type, size + 1> chars{};
29
29
  std::size_t offset = 0;
30
30
  (..., (std::ranges::copy(Strings.value, chars.data() + offset), offset += std::extent_v<typename constant_wrapper<Strings>::value_type> - 1));
31
31
  return chars;
32
32
  }();
33
- auto [... indices ] = sequence<chars.size()>;
33
+ constexpr auto [... indices ] = sequence<chars.size()>;
34
34
  constexpr char_type string[ chars.size() ] = {chars[ indices ]...};
35
35
  return cw<string>;
36
36
  }
@@ -191,7 +191,7 @@ class codepoint_utf8_forward_view {
191
191
  [[nodiscard]] constexpr auto eof() const -> bool { return pos_ == end_; }
192
192
  constexpr auto read() -> char32_t {
193
193
  // Check the expected length of the byte sequence from the leading byte's bit pattern
194
- auto sequence_length = [ & ]() -> unsigned {
194
+ auto sequence_length = [ & ] -> unsigned {
195
195
  auto byte0 = std::bit_cast<std::uint8_t>(*pos_);
196
196
  if (byte0 < 0x80) {
197
197
  return 1;
@@ -291,7 +291,7 @@ constexpr auto transcode_string(std::basic_string_view<From> from) -> std::basic
291
291
  // before.
292
292
  // TODO: It may be worth looking at the generated code for common cases and seeing if it needs
293
293
  // optimization.
294
- auto size = [ & ]() -> std::size_t {
294
+ auto size = [ & ] -> std::size_t {
295
295
  auto size_reader = reader;
296
296
  std::size_t size = 0;
297
297
  while (!size_reader.eof()) {
@@ -316,8 +316,8 @@ constexpr auto transcode_string(std::basic_string_view<From> from) -> std::basic
316
316
  // NOLINTNEXTLINE(modernize-avoid-c-arrays)
317
317
  export template <class To, class From, std::size_t Extent, fixed_value<From[ Extent ]> Value>
318
318
  constexpr auto transcode_string(util::constant_wrapper<Value> /*cw*/) {
319
- constexpr auto make = []() { return transcode_string<To>(std::basic_string_view{Value.value, Extent - 1}); };
320
- constexpr auto chars = [ = ]() {
319
+ constexpr auto make = [] { return transcode_string<To>(std::basic_string_view{Value.value, Extent - 1}); };
320
+ constexpr auto chars = [ = ] {
321
321
  std::array<To, make().size()> result{};
322
322
  std::ranges::copy(make(), result.data());
323
323
  return result;
@@ -1,4 +1,5 @@
1
1
  export module util:utility;
2
+ export import :utility.array;
2
3
  export import :utility.constant_wrapper;
3
4
  export import :utility.covariant_value;
4
5
  export import :utility.facade;
@@ -9,8 +9,8 @@ export auto map_variant(auto&& variant, auto visit) {
9
9
  constexpr auto result_from = []<class Type>(std::type_identity<Type> /*alternative*/) {
10
10
  return type<decltype(visit)>(type<util::apply_cvref_t<decltype(variant), Type>>);
11
11
  };
12
- const auto [... alternative_types ] = util::make_type_pack(util::remove_cvref(type<decltype(variant)>));
13
- const auto [... result_types ] = util::pack_unique(result_from(alternative_types)...);
12
+ constexpr auto [... alternative_types ] = util::make_type_pack(util::remove_cvref(type<decltype(variant)>));
13
+ constexpr auto [... result_types ] = util::pack_unique(result_from(alternative_types)...);
14
14
  using result_type = std::variant<type_t<result_types>...>;
15
15
  return std::visit(
16
16
  [ & ](auto&& value) -> result_type {