1 | //===----------------------------------------------------------------------===// |
2 | // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
3 | // See https://llvm.org/LICENSE.txt for license information. |
4 | // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
5 | // |
6 | //===----------------------------------------------------------------------===// |
7 | |
8 | // UNSUPPORTED: c++03, c++11, c++14, c++17, c++20, c++23 |
9 | // UNSUPPORTED: GCC-ALWAYS_INLINE-FIXME |
10 | // The tested functionality needs deducing this. |
11 | // UNSUPPORTED: clang-17 |
12 | // XFAIL: apple-clang |
13 | |
14 | // <format> |
15 | |
16 | // class basic_format_arg; |
17 | |
18 | // template<class Visitor> |
19 | // decltype(auto) visit(this basic_format_arg arg, Visitor&& vis); // since C++26 |
20 | |
21 | #include <algorithm> |
22 | #include <cassert> |
23 | #include <format> |
24 | #include <type_traits> |
25 | |
26 | #include "constexpr_char_traits.h" |
27 | #include "make_string.h" |
28 | #include "min_allocator.h" |
29 | #include "test_macros.h" |
30 | |
31 | template <class Context, class To, class From> |
32 | void test(From value) { |
33 | auto store = std::make_format_args<Context>(value); |
34 | std::basic_format_args<Context> format_args{store}; |
35 | |
36 | LIBCPP_ASSERT(format_args.__size() == 1); |
37 | assert(format_args.get(0)); |
38 | |
39 | auto result = format_args.get(0).visit([v = To(value)](auto a) -> To { |
40 | if constexpr (std::is_same_v<To, decltype(a)>) { |
41 | assert(v == a); |
42 | return a; |
43 | } else { |
44 | assert(false); |
45 | return {}; |
46 | } |
47 | }); |
48 | |
49 | using ct = std::common_type_t<From, To>; |
50 | assert(static_cast<ct>(result) == static_cast<ct>(value)); |
51 | } |
52 | |
53 | // Some types, as an extension, are stored in the variant. The Standard |
54 | // requires them to be observed as a handle. |
55 | template <class Context, class T> |
56 | void test_handle(T value) { |
57 | auto store = std::make_format_args<Context>(value); |
58 | std::basic_format_args<Context> format_args{store}; |
59 | |
60 | LIBCPP_ASSERT(format_args.__size() == 1); |
61 | assert(format_args.get(0)); |
62 | |
63 | format_args.get(0).visit([](auto a) { |
64 | assert((std::is_same_v<decltype(a), typename std::basic_format_arg<Context>::handle>)); |
65 | }); |
66 | } |
67 | |
68 | // Test specific for string and string_view. |
69 | // |
70 | // Since both result in a string_view there's no need to pass this as a |
71 | // template argument. |
72 | template <class Context, class From> |
73 | void test_string_view(From value) { |
74 | auto store = std::make_format_args<Context>(value); |
75 | std::basic_format_args<Context> format_args{store}; |
76 | |
77 | LIBCPP_ASSERT(format_args.__size() == 1); |
78 | assert(format_args.get(0)); |
79 | |
80 | using CharT = typename Context::char_type; |
81 | using To = std::basic_string_view<CharT>; |
82 | using V = std::basic_string<CharT>; |
83 | |
84 | auto result = format_args.get(0).visit([v = V(value.begin(), value.end())](auto a) -> To { |
85 | if constexpr (std::is_same_v<To, decltype(a)>) { |
86 | assert(v == a); |
87 | return a; |
88 | } else { |
89 | assert(false); |
90 | return {}; |
91 | } |
92 | }); |
93 | |
94 | assert(std::equal(value.begin(), value.end(), result.begin(), result.end())); |
95 | } |
96 | |
97 | template <class CharT> |
98 | void test() { |
99 | using Context = std::basic_format_context<CharT*, CharT>; |
100 | std::basic_string<CharT> empty; |
101 | std::basic_string<CharT> str = MAKE_STRING(CharT, "abc" ); |
102 | |
103 | // Test boolean types. |
104 | |
105 | test<Context, bool>(true); |
106 | test<Context, bool>(false); |
107 | |
108 | // Test CharT types. |
109 | |
110 | test<Context, CharT, CharT>('a'); |
111 | test<Context, CharT, CharT>('z'); |
112 | test<Context, CharT, CharT>('0'); |
113 | test<Context, CharT, CharT>('9'); |
114 | |
115 | // Test char types. |
116 | |
117 | if (std::is_same_v<CharT, char>) { |
118 | // char to char -> char |
119 | test<Context, CharT, char>('a'); |
120 | test<Context, CharT, char>('z'); |
121 | test<Context, CharT, char>('0'); |
122 | test<Context, CharT, char>('9'); |
123 | } else { |
124 | if (std::is_same_v<CharT, wchar_t>) { |
125 | // char to wchar_t -> wchar_t |
126 | test<Context, wchar_t, char>('a'); |
127 | test<Context, wchar_t, char>('z'); |
128 | test<Context, wchar_t, char>('0'); |
129 | test<Context, wchar_t, char>('9'); |
130 | } else if (std::is_signed_v<char>) { |
131 | // char to CharT -> int |
132 | // This happens when CharT is a char8_t, char16_t, or char32_t and char |
133 | // is a signed type. |
134 | // Note if sizeof(CharT) > sizeof(int) this test fails. If there are |
135 | // platforms where that occurs extra tests need to be added for char32_t |
136 | // testing it against a long long. |
137 | test<Context, int, char>('a'); |
138 | test<Context, int, char>('z'); |
139 | test<Context, int, char>('0'); |
140 | test<Context, int, char>('9'); |
141 | } else { |
142 | // char to CharT -> unsigned |
143 | // This happens when CharT is a char8_t, char16_t, or char32_t and char |
144 | // is an unsigned type. |
145 | // Note if sizeof(CharT) > sizeof(unsigned) this test fails. If there are |
146 | // platforms where that occurs extra tests need to be added for char32_t |
147 | // testing it against an unsigned long long. |
148 | test<Context, unsigned, char>('a'); |
149 | test<Context, unsigned, char>('z'); |
150 | test<Context, unsigned, char>('0'); |
151 | test<Context, unsigned, char>('9'); |
152 | } |
153 | } |
154 | |
155 | // Test signed integer types. |
156 | |
157 | test<Context, int, signed char>(std::numeric_limits<signed char>::min()); |
158 | test<Context, int, signed char>(0); |
159 | test<Context, int, signed char>(std::numeric_limits<signed char>::max()); |
160 | |
161 | test<Context, int, short>(std::numeric_limits<short>::min()); |
162 | test<Context, int, short>(std::numeric_limits<signed char>::min()); |
163 | test<Context, int, short>(0); |
164 | test<Context, int, short>(std::numeric_limits<signed char>::max()); |
165 | test<Context, int, short>(std::numeric_limits<short>::max()); |
166 | |
167 | test<Context, int, int>(std::numeric_limits<int>::min()); |
168 | test<Context, int, int>(std::numeric_limits<short>::min()); |
169 | test<Context, int, int>(std::numeric_limits<signed char>::min()); |
170 | test<Context, int, int>(0); |
171 | test<Context, int, int>(std::numeric_limits<signed char>::max()); |
172 | test<Context, int, int>(std::numeric_limits<short>::max()); |
173 | test<Context, int, int>(std::numeric_limits<int>::max()); |
174 | |
175 | using LongToType = std::conditional_t<sizeof(long) == sizeof(int), int, long long>; |
176 | |
177 | test<Context, LongToType, long>(std::numeric_limits<long>::min()); |
178 | test<Context, LongToType, long>(std::numeric_limits<int>::min()); |
179 | test<Context, LongToType, long>(std::numeric_limits<short>::min()); |
180 | test<Context, LongToType, long>(std::numeric_limits<signed char>::min()); |
181 | test<Context, LongToType, long>(0); |
182 | test<Context, LongToType, long>(std::numeric_limits<signed char>::max()); |
183 | test<Context, LongToType, long>(std::numeric_limits<short>::max()); |
184 | test<Context, LongToType, long>(std::numeric_limits<int>::max()); |
185 | test<Context, LongToType, long>(std::numeric_limits<long>::max()); |
186 | |
187 | test<Context, long long, long long>(std::numeric_limits<long long>::min()); |
188 | test<Context, long long, long long>(std::numeric_limits<long>::min()); |
189 | test<Context, long long, long long>(std::numeric_limits<int>::min()); |
190 | test<Context, long long, long long>(std::numeric_limits<short>::min()); |
191 | test<Context, long long, long long>(std::numeric_limits<signed char>::min()); |
192 | test<Context, long long, long long>(0); |
193 | test<Context, long long, long long>(std::numeric_limits<signed char>::max()); |
194 | test<Context, long long, long long>(std::numeric_limits<short>::max()); |
195 | test<Context, long long, long long>(std::numeric_limits<int>::max()); |
196 | test<Context, long long, long long>(std::numeric_limits<long>::max()); |
197 | test<Context, long long, long long>(std::numeric_limits<long long>::max()); |
198 | |
199 | #ifndef TEST_HAS_NO_INT128 |
200 | test_handle<Context, __int128_t>(0); |
201 | #endif // TEST_HAS_NO_INT128 |
202 | |
203 | // Test unsigned integer types. |
204 | |
205 | test<Context, unsigned, unsigned char>(0); |
206 | test<Context, unsigned, unsigned char>(std::numeric_limits<unsigned char>::max()); |
207 | |
208 | test<Context, unsigned, unsigned short>(0); |
209 | test<Context, unsigned, unsigned short>(std::numeric_limits<unsigned char>::max()); |
210 | test<Context, unsigned, unsigned short>(std::numeric_limits<unsigned short>::max()); |
211 | |
212 | test<Context, unsigned, unsigned>(0); |
213 | test<Context, unsigned, unsigned>(std::numeric_limits<unsigned char>::max()); |
214 | test<Context, unsigned, unsigned>(std::numeric_limits<unsigned short>::max()); |
215 | test<Context, unsigned, unsigned>(std::numeric_limits<unsigned>::max()); |
216 | |
217 | using UnsignedLongToType = |
218 | std::conditional_t<sizeof(unsigned long) == sizeof(unsigned), unsigned, unsigned long long>; |
219 | |
220 | test<Context, UnsignedLongToType, unsigned long>(0); |
221 | test<Context, UnsignedLongToType, unsigned long>(std::numeric_limits<unsigned char>::max()); |
222 | test<Context, UnsignedLongToType, unsigned long>(std::numeric_limits<unsigned short>::max()); |
223 | test<Context, UnsignedLongToType, unsigned long>(std::numeric_limits<unsigned>::max()); |
224 | test<Context, UnsignedLongToType, unsigned long>(std::numeric_limits<unsigned long>::max()); |
225 | |
226 | test<Context, unsigned long long, unsigned long long>(0); |
227 | test<Context, unsigned long long, unsigned long long>(std::numeric_limits<unsigned char>::max()); |
228 | test<Context, unsigned long long, unsigned long long>(std::numeric_limits<unsigned short>::max()); |
229 | test<Context, unsigned long long, unsigned long long>(std::numeric_limits<unsigned>::max()); |
230 | test<Context, unsigned long long, unsigned long long>(std::numeric_limits<unsigned long>::max()); |
231 | test<Context, unsigned long long, unsigned long long>(std::numeric_limits<unsigned long long>::max()); |
232 | |
233 | #ifndef TEST_HAS_NO_INT128 |
234 | test_handle<Context, __uint128_t>(0); |
235 | #endif // TEST_HAS_NO_INT128 |
236 | |
237 | // Test floating point types. |
238 | |
239 | test<Context, float, float>(-std::numeric_limits<float>::max()); |
240 | test<Context, float, float>(-std::numeric_limits<float>::min()); |
241 | test<Context, float, float>(-0.0); |
242 | test<Context, float, float>(0.0); |
243 | test<Context, float, float>(std::numeric_limits<float>::min()); |
244 | test<Context, float, float>(std::numeric_limits<float>::max()); |
245 | |
246 | test<Context, double, double>(-std::numeric_limits<double>::max()); |
247 | test<Context, double, double>(-std::numeric_limits<double>::min()); |
248 | test<Context, double, double>(-0.0); |
249 | test<Context, double, double>(0.0); |
250 | test<Context, double, double>(std::numeric_limits<double>::min()); |
251 | test<Context, double, double>(std::numeric_limits<double>::max()); |
252 | |
253 | test<Context, long double, long double>(-std::numeric_limits<long double>::max()); |
254 | test<Context, long double, long double>(-std::numeric_limits<long double>::min()); |
255 | test<Context, long double, long double>(-0.0); |
256 | test<Context, long double, long double>(0.0); |
257 | test<Context, long double, long double>(std::numeric_limits<long double>::min()); |
258 | test<Context, long double, long double>(std::numeric_limits<long double>::max()); |
259 | |
260 | // Test const CharT pointer types. |
261 | |
262 | test<Context, const CharT*, const CharT*>(empty.c_str()); |
263 | test<Context, const CharT*, const CharT*>(str.c_str()); |
264 | |
265 | // Test string_view types. |
266 | |
267 | { |
268 | using From = std::basic_string_view<CharT>; |
269 | |
270 | test_string_view<Context>(From()); |
271 | test_string_view<Context>(From(empty.c_str())); |
272 | test_string_view<Context>(From(str.c_str())); |
273 | } |
274 | |
275 | { |
276 | using From = std::basic_string_view<CharT, constexpr_char_traits<CharT>>; |
277 | |
278 | test_string_view<Context>(From()); |
279 | test_string_view<Context>(From(empty.c_str())); |
280 | test_string_view<Context>(From(str.c_str())); |
281 | } |
282 | |
283 | // Test string types. |
284 | |
285 | { |
286 | using From = std::basic_string<CharT>; |
287 | |
288 | test_string_view<Context>(From()); |
289 | test_string_view<Context>(From(empty.c_str())); |
290 | test_string_view<Context>(From(str.c_str())); |
291 | } |
292 | |
293 | { |
294 | using From = std::basic_string<CharT, constexpr_char_traits<CharT>, std::allocator<CharT>>; |
295 | |
296 | test_string_view<Context>(From()); |
297 | test_string_view<Context>(From(empty.c_str())); |
298 | test_string_view<Context>(From(str.c_str())); |
299 | } |
300 | |
301 | { |
302 | using From = std::basic_string<CharT, std::char_traits<CharT>, min_allocator<CharT>>; |
303 | |
304 | test_string_view<Context>(From()); |
305 | test_string_view<Context>(From(empty.c_str())); |
306 | test_string_view<Context>(From(str.c_str())); |
307 | } |
308 | |
309 | { |
310 | using From = std::basic_string<CharT, constexpr_char_traits<CharT>, min_allocator<CharT>>; |
311 | |
312 | test_string_view<Context>(From()); |
313 | test_string_view<Context>(From(empty.c_str())); |
314 | test_string_view<Context>(From(str.c_str())); |
315 | } |
316 | |
317 | // Test pointer types. |
318 | |
319 | test<Context, const void*>(nullptr); |
320 | int i = 0; |
321 | test<Context, const void*>(static_cast<void*>(&i)); |
322 | const int ci = 0; |
323 | test<Context, const void*>(static_cast<const void*>(&ci)); |
324 | } |
325 | |
326 | int main(int, char**) { |
327 | test<char>(); |
328 | #ifndef TEST_HAS_NO_WIDE_CHARACTERS |
329 | test<wchar_t>(); |
330 | #endif |
331 | |
332 | return 0; |
333 | } |
334 | |