@auto_js/utility 0.0.8 → 0.0.9
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 +2 -0
- package/_module.cc +1 -0
- package/assertions.cc +2 -2
- package/container/sealed_map.cc +2 -2
- package/functional/bind.cc +4 -4
- package/functional/flat_tuple.cc +16 -0
- package/functional/functional.cc +5 -5
- package/memory/noinit_allocator.cc +1 -1
- package/meta/algorithm.cc +17 -28
- package/package.json +1 -1
- package/platform/format.cc +178 -0
- package/platform/lockable.cc +3 -3
- package/platform/stop_token.cc +3 -3
- package/platform/timer.cc +2 -2
- package/type_traits/type_pack.cc +3 -6
- package/type_traits/type_traits.cc +6 -6
- package/utility/array.cc +40 -0
- package/utility/sequence.cc +18 -11
- package/utility/string.cc +6 -6
- package/utility/utility.cc +1 -0
- package/utility/variant.cc +2 -2
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
|
-
|
|
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([]
|
|
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
|
package/container/sealed_map.cc
CHANGED
|
@@ -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{[ & ]
|
|
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{[ & ]
|
|
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>{};
|
package/functional/bind.cc
CHANGED
|
@@ -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
|
-
|
|
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
|
-
|
|
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)
|
package/functional/flat_tuple.cc
CHANGED
|
@@ -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
|
package/functional/functional.cc
CHANGED
|
@@ -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
|
-
[ & ]
|
|
46
|
+
[ & ] -> decltype(auto) { return invoke(); },
|
|
47
47
|
[ & ](this const auto& self, auto value, auto... values) -> decltype(auto) {
|
|
48
|
-
return invoke(value, [ & ]
|
|
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
|
-
[ & ]
|
|
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, [ & ]
|
|
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
|
-
// [ & ]
|
|
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 = [ & ]
|
|
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
|
-
|
|
34
|
-
return
|
|
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
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
[](auto
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
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
|
-
[]
|
|
55
|
+
[] consteval -> auto { return type_pack{}; },
|
|
66
56
|
[](auto... types) consteval -> auto {
|
|
67
|
-
constexpr auto
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
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
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
export module util:platform.format;
|
|
2
|
+
import std;
|
|
3
|
+
|
|
4
|
+
namespace util {
|
|
5
|
+
|
|
6
|
+
#if defined(__GNUC__) && !defined(__clang__)
|
|
7
|
+
|
|
8
|
+
// Claude-generated `std::format` fill to avoid gcc 16.2.0 libstdc++ requirement.
|
|
9
|
+
|
|
10
|
+
template <class Type>
|
|
11
|
+
struct printf_arg {
|
|
12
|
+
static_assert(false, "type is not formattable by the `util::format` polyfill");
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
template <>
|
|
16
|
+
struct printf_arg<bool> {
|
|
17
|
+
constexpr static auto spec = std::string_view{"%s"};
|
|
18
|
+
constexpr static auto pass(bool value) { return std::tuple{value ? "true" : "false"}; }
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
template <>
|
|
22
|
+
struct printf_arg<char> {
|
|
23
|
+
constexpr static auto spec = std::string_view{"%c"};
|
|
24
|
+
constexpr static auto pass(char value) { return std::tuple{value}; }
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
template <class Type>
|
|
28
|
+
requires std::signed_integral<Type>
|
|
29
|
+
struct printf_arg<Type> {
|
|
30
|
+
constexpr static auto spec = std::string_view{"%lld"};
|
|
31
|
+
constexpr static auto pass(Type value) { return std::tuple{static_cast<long long>(value)}; }
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
template <class Type>
|
|
35
|
+
requires std::unsigned_integral<Type>
|
|
36
|
+
struct printf_arg<Type> {
|
|
37
|
+
constexpr static auto spec = std::string_view{"%llu"};
|
|
38
|
+
constexpr static auto pass(Type value) { return std::tuple{static_cast<unsigned long long>(value)}; }
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
template <class Type>
|
|
42
|
+
requires (std::same_as<Type, float> || std::same_as<Type, double>)
|
|
43
|
+
struct printf_arg<Type> {
|
|
44
|
+
constexpr static auto spec = std::string_view{"%g"};
|
|
45
|
+
constexpr static auto pass(Type value) { return std::tuple{static_cast<double>(value)}; }
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
template <>
|
|
49
|
+
struct printf_arg<long double> {
|
|
50
|
+
constexpr static auto spec = std::string_view{"%Lg"};
|
|
51
|
+
constexpr static auto pass(long double value) { return std::tuple{value}; }
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
template <>
|
|
55
|
+
struct printf_arg<const char*> {
|
|
56
|
+
constexpr static auto spec = std::string_view{"%s"};
|
|
57
|
+
constexpr static auto pass(const char* value) { return std::tuple{value}; }
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
template <>
|
|
61
|
+
struct printf_arg<char*> : printf_arg<const char*> {};
|
|
62
|
+
|
|
63
|
+
template <>
|
|
64
|
+
struct printf_arg<std::string_view> {
|
|
65
|
+
constexpr static auto spec = std::string_view{"%.*s"};
|
|
66
|
+
constexpr static auto pass(std::string_view value) {
|
|
67
|
+
return std::tuple{static_cast<int>(value.size()), value.data()};
|
|
68
|
+
}
|
|
69
|
+
};
|
|
70
|
+
|
|
71
|
+
template <>
|
|
72
|
+
struct printf_arg<std::string> : printf_arg<std::string_view> {};
|
|
73
|
+
|
|
74
|
+
template <>
|
|
75
|
+
struct printf_arg<const void*> {
|
|
76
|
+
constexpr static auto spec = std::string_view{"%p"};
|
|
77
|
+
constexpr static auto pass(const void* value) { return std::tuple{value}; }
|
|
78
|
+
};
|
|
79
|
+
|
|
80
|
+
template <>
|
|
81
|
+
struct printf_arg<void*> : printf_arg<const void*> {};
|
|
82
|
+
|
|
83
|
+
template <>
|
|
84
|
+
struct printf_arg<std::nullptr_t> {
|
|
85
|
+
constexpr static auto spec = std::string_view{"%p"};
|
|
86
|
+
constexpr static auto pass(std::nullptr_t /*value*/) { return std::tuple{static_cast<const void*>(nullptr)}; }
|
|
87
|
+
};
|
|
88
|
+
|
|
89
|
+
// Checks the format string against the argument types and rewrites `{}` replacement fields into
|
|
90
|
+
// printf conversion specifiers, all at compile time. Only plain `{}` fields are supported --
|
|
91
|
+
// `{0}`-style indices and `{:..}` format specifiers are rejected.
|
|
92
|
+
export template <class... Args>
|
|
93
|
+
class basic_format_string {
|
|
94
|
+
public:
|
|
95
|
+
// NOLINTNEXTLINE(modernize-avoid-c-arrays)
|
|
96
|
+
template <std::size_t Size>
|
|
97
|
+
consteval basic_format_string(const char (&format)[ Size ]) {
|
|
98
|
+
auto out = string_.begin();
|
|
99
|
+
auto put = [ & ](char character) {
|
|
100
|
+
if (out == string_.end() - 1) {
|
|
101
|
+
throw std::format_error{"format string exceeds polyfill capacity"};
|
|
102
|
+
}
|
|
103
|
+
*out++ = character;
|
|
104
|
+
};
|
|
105
|
+
auto emit = [ & ](std::string_view string) {
|
|
106
|
+
for (auto character : string) {
|
|
107
|
+
put(character);
|
|
108
|
+
}
|
|
109
|
+
};
|
|
110
|
+
constexpr auto specs = std::array<std::string_view, sizeof...(Args)>{printf_arg<std::decay_t<Args>>::spec...};
|
|
111
|
+
std::size_t argument = 0;
|
|
112
|
+
auto view = std::string_view{format, Size - 1};
|
|
113
|
+
for (auto it = view.begin(); it != view.end(); ++it) {
|
|
114
|
+
switch (*it) {
|
|
115
|
+
case '{':
|
|
116
|
+
if (++it == view.end()) {
|
|
117
|
+
throw std::format_error{"unmatched '{' in format string"};
|
|
118
|
+
} else if (*it == '{') {
|
|
119
|
+
put('{');
|
|
120
|
+
} else if (*it == '}') {
|
|
121
|
+
if (argument == specs.size()) {
|
|
122
|
+
throw std::format_error{"too few arguments for format string"};
|
|
123
|
+
}
|
|
124
|
+
emit(specs[ argument++ ]);
|
|
125
|
+
} else {
|
|
126
|
+
throw std::format_error{"format specifiers are not supported by the `util::format` polyfill"};
|
|
127
|
+
}
|
|
128
|
+
break;
|
|
129
|
+
case '}':
|
|
130
|
+
if (++it == view.end() || *it != '}') {
|
|
131
|
+
throw std::format_error{"unmatched '}' in format string"};
|
|
132
|
+
}
|
|
133
|
+
put('}');
|
|
134
|
+
break;
|
|
135
|
+
case '%':
|
|
136
|
+
emit("%%");
|
|
137
|
+
break;
|
|
138
|
+
default:
|
|
139
|
+
put(*it);
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
[[nodiscard]] constexpr auto get() const -> const char* { return string_.data(); }
|
|
145
|
+
|
|
146
|
+
private:
|
|
147
|
+
std::array<char, 256> string_{};
|
|
148
|
+
};
|
|
149
|
+
|
|
150
|
+
export template <class... Args>
|
|
151
|
+
using format_string = basic_format_string<std::type_identity_t<Args>...>;
|
|
152
|
+
|
|
153
|
+
export template <class... Args>
|
|
154
|
+
[[nodiscard]] auto format(format_string<Args...> format, Args&&... args) -> std::string {
|
|
155
|
+
return std::apply(
|
|
156
|
+
[ & ](auto... values) -> std::string {
|
|
157
|
+
auto length = std::max(0, std::snprintf(nullptr, 0, format.get(), values...));
|
|
158
|
+
auto result = std::string{};
|
|
159
|
+
result.resize(static_cast<std::size_t>(length));
|
|
160
|
+
std::snprintf(result.data(), result.size() + 1, format.get(), values...);
|
|
161
|
+
return result;
|
|
162
|
+
},
|
|
163
|
+
std::tuple_cat(printf_arg<std::decay_t<Args>>::pass(args)...));
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
#else
|
|
167
|
+
|
|
168
|
+
export template <class... Args>
|
|
169
|
+
using format_string = std::format_string<Args...>;
|
|
170
|
+
|
|
171
|
+
export template <class... Args>
|
|
172
|
+
[[nodiscard]] auto format(std::format_string<std::type_identity_t<Args>...> format, Args&&... args) -> std::string {
|
|
173
|
+
return std::format(format, std::forward<Args>(args)...);
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
#endif
|
|
177
|
+
|
|
178
|
+
} // namespace util
|
package/platform/lockable.cc
CHANGED
|
@@ -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 = []
|
|
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) {
|
package/platform/stop_token.cc
CHANGED
|
@@ -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{[ & ]
|
|
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
|
-
[]
|
|
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_{[ & ]
|
|
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 = [ & ]
|
|
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{[]
|
|
123
|
+
return *std::move(result).value_or(util::elide{[] -> result_type {
|
|
124
124
|
std::unreachable();
|
|
125
125
|
}});
|
|
126
126
|
}
|
package/type_traits/type_pack.cc
CHANGED
|
@@ -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
|
-
|
|
50
|
-
|
|
51
|
-
return
|
|
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(
|
|
31
|
-
static_assert(!
|
|
32
|
-
static_assert(!
|
|
33
|
-
static_assert(
|
|
34
|
-
static_assert(
|
|
35
|
-
static_assert(!
|
|
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>
|
package/utility/array.cc
ADDED
|
@@ -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
|
package/utility/sequence.cc
CHANGED
|
@@ -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
|
|
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
|
|
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{
|
|
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 = [ & ]
|
|
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 = [ & ]
|
|
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 = [ & ]
|
|
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 = []
|
|
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;
|
package/utility/utility.cc
CHANGED
package/utility/variant.cc
CHANGED
|
@@ -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
|
-
|
|
13
|
-
|
|
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 {
|