| Line | Branch | Exec | Source |
|---|---|---|---|
| 1 | //====================================================================================================================== | ||
| 2 | /* | ||
| 3 | KUMI - Compact Tuple Tools | ||
| 4 | Copyright : KUMI Project Contributors | ||
| 5 | SPDX-License-Identifier: BSL-1.0 | ||
| 6 | */ | ||
| 7 | //====================================================================================================================== | ||
| 8 | #pragma once | ||
| 9 | |||
| 10 | namespace kumi | ||
| 11 | { | ||
| 12 | /// @brief This is a special value equal to the largest value representable by the type std::size_t. | ||
| 13 | inline constexpr auto npos = static_cast<std::size_t>(-1); | ||
| 14 | |||
| 15 | //==================================================================================================================== | ||
| 16 | /** | ||
| 17 | @ingroup kumi_types | ||
| 18 | @class str | ||
| 19 | @brief Static string used to create named fields. | ||
| 20 | |||
| 21 | @tparam N Number of characters in the string (the storage holds N + 1 chars, for the null terminator). | ||
| 22 | |||
| 23 | @note the `.` character is reserved for internal manipulation. | ||
| 24 | |||
| 25 | kumi::str provides a way to define compile time names. A literal "abc" is deduced as kumi::str<3>. | ||
| 26 | Member functions taking another string accept a character array (`char const (&)[M]`, M including the | ||
| 27 | terminator), so they work with literals as well as with `other.data_`. | ||
| 28 | **/ | ||
| 29 | //==================================================================================================================== | ||
| 30 | template<std::size_t N> struct str | ||
| 31 | { | ||
| 32 | static constexpr char separator = '.'; | ||
| 33 | |||
| 34 | char data_[N + 1] = {0}; | ||
| 35 | |||
| 36 | 1 | constexpr str() = default; | |
| 37 | |||
| 38 | KUMI_ABI constexpr str(char const (&s)[N + 1]) | ||
| 39 | { | ||
| 40 | for (std::size_t i = 0; i < N + 1; ++i) data_[i] = s[i]; | ||
| 41 | } | ||
| 42 | |||
| 43 | 13 | KUMI_ABI constexpr std::size_t size() const noexcept { return N; } | |
| 44 | |||
| 45 | 1 | KUMI_ABI constexpr auto data() const noexcept { return data_; } | |
| 46 | |||
| 47 | template<typename T> | ||
| 48 | requires requires { T{data_, std::size_t{N}}; } | ||
| 49 | KUMI_ABI constexpr auto as() const | ||
| 50 | { | ||
| 51 | return T{data_, std::size_t{N}}; | ||
| 52 | } | ||
| 53 | |||
| 54 | template<typename CharT, typename Traits> | ||
| 55 | 773 | friend std::basic_ostream<CharT, Traits>& operator<<(std::basic_ostream<CharT, Traits>& os, str const& s) noexcept | |
| 56 | { | ||
| 57 | 773 | os << '\''; | |
| 58 |
2/2✓ Branch 1 taken 934 times.
✓ Branch 2 taken 594 times.
|
2166 | for (std::size_t i = 0; i < N; ++i) os << s.data_[i]; |
| 59 | 773 | return os << '\''; | |
| 60 | } | ||
| 61 | |||
| 62 | // The size of the result depends on the arguments, so they are passed as std::integral_constant. | ||
| 63 | template<kumi::concepts::index Pos, kumi::concepts::index Count> | ||
| 64 | KUMI_ABI constexpr auto substr(Pos p, Count c) const | ||
| 65 | { | ||
| 66 | constexpr std::size_t pos = static_cast<std::size_t>(p); | ||
| 67 | constexpr std::size_t count = static_cast<std::size_t>(c); | ||
| 68 | static_assert(pos <= N, "Out of range"); | ||
| 69 | constexpr std::size_t len = (count == kumi::npos || pos + count > N) ? (N - pos) : count; | ||
| 70 | str<len> res{}; | ||
| 71 | for (std::size_t i = 0; i < len; ++i) res.data_[i] = data_[pos + i]; | ||
| 72 | return res; | ||
| 73 | } | ||
| 74 | |||
| 75 | template<kumi::concepts::index Pos> KUMI_ABI constexpr auto substr(Pos p) const | ||
| 76 | { | ||
| 77 | return substr(p, std::integral_constant<std::size_t, kumi::npos>{}); | ||
| 78 | } | ||
| 79 | |||
| 80 | KUMI_ABI constexpr auto substr() const { return substr(std::integral_constant<std::size_t, 0>{}); } | ||
| 81 | |||
| 82 | template<kumi::concepts::index Size> KUMI_ABI constexpr auto remove_prefix(Size s) const | ||
| 83 | { | ||
| 84 | static_assert(static_cast<std::size_t>(s) <= N, "Out of range"); | ||
| 85 | return substr(std::integral_constant<std::size_t, static_cast<std::size_t>(s)>{}, | ||
| 86 | std::integral_constant<std::size_t, N - static_cast<std::size_t>(s)>{}); | ||
| 87 | } | ||
| 88 | |||
| 89 | template<kumi::concepts::index Size> KUMI_ABI constexpr auto remove_suffix(Size s) const | ||
| 90 | { | ||
| 91 | static_assert(static_cast<std::size_t>(s) <= N, "Out of range"); | ||
| 92 | return substr(std::integral_constant<std::size_t, 0>{}, | ||
| 93 | std::integral_constant<std::size_t, N - static_cast<std::size_t>(s)>{}); | ||
| 94 | } | ||
| 95 | |||
| 96 | template<std::size_t M> KUMI_ABI constexpr bool starts_with(char const (&s)[M]) const | ||
| 97 | { | ||
| 98 | 9 | constexpr std::size_t n = M - 1; | |
| 99 | 1 | if (n > N) return false; | |
| 100 |
10/10✓ Branch 0 taken 3 times.
✓ Branch 1 taken 1 times.
✓ Branch 2 taken 5 times.
✓ Branch 3 taken 1 times.
✓ Branch 4 taken 4 times.
✓ Branch 5 taken 2 times.
✓ Branch 6 taken 4 times.
✓ Branch 7 taken 2 times.
✓ Branch 8 taken 5 times.
✓ Branch 9 taken 1 times.
|
28 | for (std::size_t i = 0; i < n; ++i) |
| 101 |
7/10✓ Branch 0 taken 1 times.
✓ Branch 1 taken 2 times.
✓ Branch 2 taken 1 times.
✓ Branch 3 taken 4 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 4 times.
✗ Branch 6 not taken.
✓ Branch 7 taken 4 times.
✗ Branch 8 not taken.
✓ Branch 9 taken 5 times.
|
21 | if (data_[i] != s[i]) return false; |
| 102 | 7 | return true; | |
| 103 | } | ||
| 104 | |||
| 105 | template<std::size_t M> KUMI_ABI constexpr bool ends_with(char const (&s)[M]) const | ||
| 106 | { | ||
| 107 | 12 | constexpr std::size_t n = M - 1; | |
| 108 | if (n > N) return false; | ||
| 109 |
14/20✓ Branch 0 taken 7 times.
✓ Branch 1 taken 1 times.
✓ Branch 2 taken 2 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 2 times.
✓ Branch 5 taken 1 times.
✓ Branch 6 taken 1 times.
✓ Branch 7 taken 1 times.
✓ Branch 8 taken 1 times.
✓ Branch 9 taken 1 times.
✗ Branch 10 not taken.
✗ Branch 11 not taken.
✓ Branch 12 taken 1 times.
✗ Branch 13 not taken.
✓ Branch 14 taken 1 times.
✓ Branch 15 taken 1 times.
✓ Branch 16 taken 1 times.
✓ Branch 17 taken 1 times.
✗ Branch 18 not taken.
✗ Branch 19 not taken.
|
22 | for (std::size_t i = 0; i < n; ++i) |
| 110 |
10/20✓ Branch 0 taken 2 times.
✓ Branch 1 taken 5 times.
✓ Branch 2 taken 2 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 1 times.
✓ Branch 5 taken 1 times.
✗ Branch 6 not taken.
✓ Branch 7 taken 1 times.
✗ Branch 8 not taken.
✓ Branch 9 taken 1 times.
✗ Branch 10 not taken.
✗ Branch 11 not taken.
✓ Branch 12 taken 1 times.
✗ Branch 13 not taken.
✗ Branch 14 not taken.
✓ Branch 15 taken 1 times.
✗ Branch 16 not taken.
✓ Branch 17 taken 1 times.
✗ Branch 18 not taken.
✗ Branch 19 not taken.
|
16 | if (data_[N - n + i] != s[i]) return false; |
| 111 | 6 | return true; | |
| 112 | } | ||
| 113 | |||
| 114 | 2 | template<std::size_t M> KUMI_ABI constexpr bool contains(char const (&s)[M]) const { return find(s) != kumi::npos; } | |
| 115 | |||
| 116 | template<std::size_t M> KUMI_ABI constexpr std::size_t find(char const (&s)[M], std::size_t pos = 0) const | ||
| 117 | { | ||
| 118 | 9 | constexpr std::size_t n = M - 1; | |
| 119 | if (n == 0) return pos <= N ? pos : kumi::npos; | ||
| 120 | 1 | if (n > N) return kumi::npos; | |
| 121 |
7/10✓ Branch 0 taken 10 times.
✓ Branch 1 taken 1 times.
✓ Branch 2 taken 8 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 6 times.
✓ Branch 5 taken 1 times.
✓ Branch 6 taken 3 times.
✗ Branch 7 not taken.
✓ Branch 8 taken 2 times.
✗ Branch 9 not taken.
|
31 | for (std::size_t i = pos; i <= N - n; ++i) |
| 122 | { | ||
| 123 | 29 | bool match = true; | |
| 124 |
10/10✓ Branch 0 taken 12 times.
✓ Branch 1 taken 1 times.
✓ Branch 2 taken 10 times.
✓ Branch 3 taken 2 times.
✓ Branch 4 taken 6 times.
✓ Branch 5 taken 1 times.
✓ Branch 6 taken 3 times.
✓ Branch 7 taken 1 times.
✓ Branch 8 taken 2 times.
✓ Branch 9 taken 1 times.
|
39 | for (std::size_t j = 0; j < n; ++j) |
| 125 |
10/10✓ Branch 0 taken 9 times.
✓ Branch 1 taken 3 times.
✓ Branch 2 taken 6 times.
✓ Branch 3 taken 4 times.
✓ Branch 4 taken 5 times.
✓ Branch 5 taken 1 times.
✓ Branch 6 taken 2 times.
✓ Branch 7 taken 1 times.
✓ Branch 8 taken 1 times.
✓ Branch 9 taken 1 times.
|
33 | if (data_[i + j] != s[j]) |
| 126 | { | ||
| 127 | 23 | match = false; | |
| 128 | 23 | break; | |
| 129 | } | ||
| 130 |
10/10✓ Branch 0 taken 1 times.
✓ Branch 1 taken 9 times.
✓ Branch 2 taken 2 times.
✓ Branch 3 taken 6 times.
✓ Branch 4 taken 1 times.
✓ Branch 5 taken 5 times.
✓ Branch 6 taken 1 times.
✓ Branch 7 taken 2 times.
✓ Branch 8 taken 1 times.
✓ Branch 9 taken 1 times.
|
29 | if (match) return i; |
| 131 | } | ||
| 132 | 2 | return kumi::npos; | |
| 133 | } | ||
| 134 | |||
| 135 | template<std::size_t M> KUMI_ABI constexpr int compare(char const (&other)[M]) const noexcept | ||
| 136 | { | ||
| 137 | 25 | constexpr std::size_t n = M - 1; | |
| 138 | 25 | constexpr std::size_t min_size = (N < n) ? N : n; | |
| 139 | |||
| 140 |
12/14✓ Branch 0 taken 39 times.
✓ Branch 1 taken 12 times.
✓ Branch 2 taken 5 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 4 times.
✓ Branch 5 taken 1 times.
✓ Branch 6 taken 4 times.
✓ Branch 7 taken 2 times.
✓ Branch 8 taken 3 times.
✗ Branch 9 not taken.
✓ Branch 10 taken 2 times.
✓ Branch 11 taken 1 times.
✓ Branch 12 taken 11 times.
✓ Branch 13 taken 1 times.
|
85 | for (std::size_t i = 0; i < min_size; ++i) |
| 141 | { | ||
| 142 |
9/14✗ Branch 0 not taken.
✓ Branch 1 taken 39 times.
✓ Branch 2 taken 2 times.
✓ Branch 3 taken 3 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 4 times.
✗ Branch 6 not taken.
✓ Branch 7 taken 4 times.
✓ Branch 8 taken 1 times.
✓ Branch 9 taken 2 times.
✗ Branch 10 not taken.
✓ Branch 11 taken 2 times.
✗ Branch 12 not taken.
✓ Branch 13 taken 11 times.
|
68 | if (data_[i] < other[i]) return -1; |
| 143 |
10/14✓ Branch 0 taken 3 times.
✓ Branch 1 taken 36 times.
✓ Branch 2 taken 1 times.
✓ Branch 3 taken 2 times.
✓ Branch 4 taken 1 times.
✓ Branch 5 taken 3 times.
✗ Branch 6 not taken.
✓ Branch 7 taken 4 times.
✗ Branch 8 not taken.
✓ Branch 9 taken 2 times.
✗ Branch 10 not taken.
✓ Branch 11 taken 2 times.
✗ Branch 12 not taken.
✓ Branch 13 taken 11 times.
|
65 | if (data_[i] > other[i]) return 1; |
| 144 | } | ||
| 145 | if (N < n) return -1; | ||
| 146 | 1 | if (N > n) return 1; | |
| 147 | 16 | return 0; | |
| 148 | } | ||
| 149 | |||
| 150 | template<std::size_t M> KUMI_ABI constexpr std::size_t rfind(char const (&s)[M], std::size_t pos = kumi::npos) const | ||
| 151 | { | ||
| 152 | 2 | constexpr std::size_t n = M - 1; | |
| 153 | if (n == 0) return (pos > N ? N : pos); | ||
| 154 | if (n > N) return kumi::npos; | ||
| 155 | 2 | std::size_t start = (pos > N - n) ? (N - n) : pos; | |
| 156 |
2/4✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 1 times.
|
2 | for (std::size_t i = start; i > 0; --i) |
| 157 | { | ||
| 158 | 1 | bool match = true; | |
| 159 |
2/4✓ Branch 0 taken 3 times.
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
|
4 | for (std::size_t j = 0; j < n; ++j) |
| 160 |
1/4✗ Branch 0 not taken.
✓ Branch 1 taken 3 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
|
3 | if (data_[i + j] != s[j]) |
| 161 | { | ||
| 162 | ✗ | match = false; | |
| 163 | ✗ | break; | |
| 164 | } | ||
| 165 |
1/4✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
|
1 | if (match) return i; |
| 166 | } | ||
| 167 | 1 | return kumi::npos; | |
| 168 | } | ||
| 169 | |||
| 170 | template<std::size_t M> KUMI_ABI constexpr std::size_t find_first_of(char const (&s)[M], std::size_t pos = 0) const | ||
| 171 | { | ||
| 172 | 2 | constexpr std::size_t n = M - 1; | |
| 173 |
3/4✓ Branch 0 taken 5 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 11 times.
✓ Branch 3 taken 1 times.
|
17 | for (std::size_t i = pos; i < N; ++i) |
| 174 |
4/4✓ Branch 0 taken 5 times.
✓ Branch 1 taken 4 times.
✓ Branch 2 taken 11 times.
✓ Branch 3 taken 11 times.
|
31 | for (std::size_t j = 0; j < n; ++j) |
| 175 |
3/4✓ Branch 0 taken 1 times.
✓ Branch 1 taken 4 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 11 times.
|
16 | if (data_[i] == s[j]) return i; |
| 176 | 1 | return kumi::npos; | |
| 177 | } | ||
| 178 | |||
| 179 | template<std::size_t M> | ||
| 180 | KUMI_ABI constexpr std::size_t find_last_of(char const (&s)[M], std::size_t pos = kumi::npos) const | ||
| 181 | { | ||
| 182 | 2 | constexpr std::size_t n = M - 1; | |
| 183 | if (N == 0) return kumi::npos; | ||
| 184 | 12 | for (std::size_t i = (pos >= N ? N - 1 : pos);; --i) | |
| 185 | { | ||
| 186 |
3/4✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 11 times.
✓ Branch 3 taken 11 times.
|
23 | for (std::size_t j = 0; j < n; ++j) |
| 187 |
2/4✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 11 times.
|
12 | if (data_[i] == s[j]) return i; |
| 188 |
2/4✗ Branch 0 not taken.
✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
✓ Branch 3 taken 10 times.
|
11 | if (i == 0) break; |
| 189 | } | ||
| 190 | 1 | return kumi::npos; | |
| 191 | } | ||
| 192 | |||
| 193 | template<std::size_t M> | ||
| 194 | KUMI_ABI constexpr std::size_t find_first_not_of(char const (&s)[M], std::size_t pos = 0) const | ||
| 195 | { | ||
| 196 | 2 | constexpr std::size_t n = M - 1; | |
| 197 |
3/4✓ Branch 0 taken 3 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 11 times.
✓ Branch 3 taken 1 times.
|
15 | for (std::size_t i = pos; i < N; ++i) |
| 198 | { | ||
| 199 | 14 | bool found = false; | |
| 200 |
3/4✓ Branch 0 taken 5 times.
✓ Branch 1 taken 1 times.
✓ Branch 2 taken 24 times.
✗ Branch 3 not taken.
|
30 | for (std::size_t j = 0; j < n; ++j) |
| 201 |
4/4✓ Branch 0 taken 2 times.
✓ Branch 1 taken 3 times.
✓ Branch 2 taken 11 times.
✓ Branch 3 taken 13 times.
|
29 | if (data_[i] == s[j]) |
| 202 | { | ||
| 203 | 13 | found = true; | |
| 204 | 13 | break; | |
| 205 | } | ||
| 206 |
3/4✓ Branch 0 taken 1 times.
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 11 times.
|
14 | if (!found) return i; |
| 207 | } | ||
| 208 | 1 | return kumi::npos; | |
| 209 | } | ||
| 210 | |||
| 211 | template<std::size_t M> | ||
| 212 | KUMI_ABI constexpr std::size_t find_last_not_of(char const (&s)[M], std::size_t pos = kumi::npos) const | ||
| 213 | { | ||
| 214 | 2 | constexpr std::size_t n = M - 1; | |
| 215 | if (N == 0) return kumi::npos; | ||
| 216 | 14 | for (std::size_t i = (pos >= N ? N - 1 : pos);; --i) | |
| 217 | { | ||
| 218 | 14 | bool found = false; | |
| 219 |
3/4✓ Branch 0 taken 5 times.
✓ Branch 1 taken 1 times.
✓ Branch 2 taken 24 times.
✗ Branch 3 not taken.
|
30 | for (std::size_t j = 0; j < n; ++j) |
| 220 |
4/4✓ Branch 0 taken 2 times.
✓ Branch 1 taken 3 times.
✓ Branch 2 taken 11 times.
✓ Branch 3 taken 13 times.
|
29 | if (data_[i] == s[j]) |
| 221 | { | ||
| 222 | 13 | found = true; | |
| 223 | 13 | break; | |
| 224 | } | ||
| 225 |
3/4✓ Branch 0 taken 1 times.
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 11 times.
|
14 | if (!found) return i; |
| 226 |
3/4✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
✓ Branch 2 taken 1 times.
✓ Branch 3 taken 10 times.
|
13 | if (i == 0) break; |
| 227 | } | ||
| 228 | 1 | return kumi::npos; | |
| 229 | } | ||
| 230 | |||
| 231 | // N chars + separator + (M - 1) chars | ||
| 232 | template<std::size_t M> KUMI_ABI constexpr str<N + M> operator+(char const (&other)[M]) const | ||
| 233 | { | ||
| 234 | 1 | str<N + M> res{}; | |
| 235 | |||
| 236 |
2/2✓ Branch 0 taken 3 times.
✓ Branch 1 taken 1 times.
|
4 | for (std::size_t i = 0; i < N; ++i) res.data_[i] = data_[i]; |
| 237 | |||
| 238 | 1 | res.data_[N] = separator; | |
| 239 | |||
| 240 |
2/2✓ Branch 0 taken 3 times.
✓ Branch 1 taken 1 times.
|
4 | for (std::size_t i = 0; i < M - 1; ++i) res.data_[N + 1 + i] = other[i]; |
| 241 | |||
| 242 | 1 | return res; | |
| 243 | } | ||
| 244 | |||
| 245 | // str + str: a str is not deduced through its conversion to char array, so it needs its own overload | ||
| 246 | template<std::size_t M> KUMI_ABI constexpr str<N + 1 + M> operator+(str<M> const& other) const | ||
| 247 | { | ||
| 248 | 2 | return *this + other.data_; | |
| 249 | } | ||
| 250 | }; | ||
| 251 | |||
| 252 | template<std::size_t M> str(char const (&)[M]) -> str<M - 1>; | ||
| 253 | |||
| 254 | template<std::size_t N, std::size_t M> | ||
| 255 | KUMI_ABI constexpr bool operator==(str<N> const& lhs, str<M> const& rhs) noexcept | ||
| 256 | { | ||
| 257 | 28 | return lhs.compare(rhs.data_) == 0; | |
| 258 | } | ||
| 259 | |||
| 260 | template<std::size_t N, std::size_t M> | ||
| 261 | KUMI_ABI constexpr auto operator<=>(str<N> const& lhs, str<M> const& rhs) noexcept | ||
| 262 | { | ||
| 263 |
4/8✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 1 times.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✓ Branch 7 taken 1 times.
|
4 | return lhs.compare(rhs.data_) <=> 0; |
| 264 | } | ||
| 265 | |||
| 266 | template<std::size_t N, std::size_t M> | ||
| 267 | KUMI_ABI constexpr bool operator==(str<N> const& lhs, char const (&rhs)[M]) noexcept | ||
| 268 | { | ||
| 269 | 9 | return lhs.compare(rhs) == 0; | |
| 270 | } | ||
| 271 | |||
| 272 | template<std::size_t N, std::size_t M> | ||
| 273 | KUMI_ABI constexpr auto operator<=>(str<N> const& lhs, char const (&rhs)[M]) noexcept | ||
| 274 | { | ||
| 275 | return lhs.compare(rhs) <=> 0; | ||
| 276 | } | ||
| 277 | |||
| 278 | inline namespace literals | ||
| 279 | { | ||
| 280 | template<kumi::str S> KUMI_ABI constexpr auto operator""_str() | ||
| 281 | { | ||
| 282 | 3 | return S; | |
| 283 | } | ||
| 284 | } | ||
| 285 | |||
| 286 | //==================================================================================================================== | ||
| 287 | /** | ||
| 288 | @ingroup kumi_types | ||
| 289 | @class unknown | ||
| 290 | @brief Type indicating an identifier was not found in a given kumi::product_type | ||
| 291 | **/ | ||
| 292 | //==================================================================================================================== | ||
| 293 | struct unknown | ||
| 294 | { | ||
| 295 | static constexpr auto value = kumi::str{"kumi::unknown"}; | ||
| 296 | using type = decltype(value); | ||
| 297 | |||
| 298 | constexpr inline operator type() const noexcept { return value; } | ||
| 299 | |||
| 300 | KUMI_ABI friend constexpr auto operator<=>(unknown const&, unknown const&) noexcept = default; | ||
| 301 | |||
| 302 | template<typename CharT, typename Traits> | ||
| 303 | 5 | friend std::basic_ostream<CharT, Traits>& operator<<(std::basic_ostream<CharT, Traits>& os, unknown const&) noexcept | |
| 304 | { | ||
| 305 | 5 | return os << "kumi::unknown"; | |
| 306 | } | ||
| 307 | }; | ||
| 308 | } | ||
| 309 |