]>
Commit | Line | Data |
---|---|---|
1d9454ab JW |
1 | // { dg-options "-std=gnu++20" } |
2 | // { dg-do run { target c++20 } } | |
3 | ||
4 | #include <format> | |
02e86035 JW |
5 | |
6 | #ifndef __cpp_lib_format | |
7 | # error "Feature test macro for std::format is missing in <format>" | |
8 | #elif __cpp_lib_format < 202106L | |
9 | # error "Feature test macro for std::format has wrong value in <format>" | |
10 | #endif | |
11 | ||
12 | #undef __cpp_lib_format | |
13 | #include <version> | |
14 | #ifndef __cpp_lib_format | |
15 | # error "Feature test macro for std::format is missing in <version>" | |
16 | #elif __cpp_lib_format < 202106L | |
17 | # error "Feature test macro for std::format has wrong value in <version>" | |
18 | #endif | |
19 | ||
1d9454ab JW |
20 | #include <string> |
21 | #include <limits> | |
22 | #include <cstdint> | |
ce86d967 | 23 | #include <cstdio> |
1d9454ab JW |
24 | #include <testsuite_hooks.h> |
25 | ||
26 | void | |
27 | test_no_args() | |
28 | { | |
29 | std::string s; | |
30 | s = std::format("disco"); | |
31 | VERIFY( s == "disco" ); | |
32 | ||
33 | s = std::format("}} machine {{ funk }} specialists {{"); | |
34 | VERIFY( s == "} machine { funk } specialists {" ); | |
35 | ||
36 | s = std::format("128bpm }}"); | |
37 | VERIFY( s == "128bpm }" ); | |
38 | } | |
39 | ||
40 | void | |
41 | test_unescaped() | |
42 | { | |
43 | #ifdef __cpp_exceptions | |
44 | for (auto f : { "{", "}", "{{{", "{{}", "}{", "{{{{{" }) | |
45 | try { | |
46 | (void) std::vformat(f, std::make_format_args()); | |
47 | VERIFY( false ); | |
48 | } catch (const std::format_error& e) { | |
49 | std::string what = e.what(); | |
50 | VERIFY( what.find("unmatched") != what.npos ); | |
51 | } | |
52 | #endif | |
53 | } | |
54 | ||
55 | struct brit_punc : std::numpunct<char> | |
56 | { | |
57 | std::string do_grouping() const override { return "\3\3"; } | |
58 | char do_thousands_sep() const override { return ','; } | |
59 | std::string do_truename() const override { return "yes mate"; } | |
60 | std::string do_falsename() const override { return "nah bruv"; } | |
61 | }; | |
62 | ||
63 | void | |
64 | test_std_examples() | |
65 | { | |
66 | using namespace std; | |
67 | ||
68 | string s = format("{0}-{{", 8); // value of s is "8-{" | |
69 | VERIFY( s == "8-{" ); | |
70 | ||
71 | // align | |
72 | { | |
73 | char c = 120; | |
74 | string s0 = format("{:6}", 42); | |
75 | VERIFY(s0 == " 42"); | |
76 | string s1 = format("{:6}", 'x'); | |
77 | VERIFY(s1 == "x "); | |
78 | string s2 = format("{:*<6}", 'x'); | |
79 | VERIFY(s2 == "x*****"); | |
80 | string s3 = format("{:*>6}", 'x'); | |
81 | VERIFY(s3 == "*****x"); | |
82 | string s4 = format("{:*^6}", 'x'); | |
83 | VERIFY(s4 == "**x***"); | |
84 | string s5 = format("{:6d}", c); | |
85 | VERIFY(s5 == " 120"); | |
86 | string s6 = format("{:6}", true); | |
87 | VERIFY(s6 == "true "); | |
88 | } | |
89 | ||
90 | // sign | |
91 | { | |
92 | double inf = numeric_limits<double>::infinity(); | |
93 | double nan = numeric_limits<double>::quiet_NaN(); | |
94 | string s0 = format("{0:},{0:+},{0:-},{0: }", 1); | |
95 | VERIFY(s0 == "1,+1,1, 1"); | |
96 | string s1 = format("{0:},{0:+},{0:-},{0: }", -1); | |
97 | VERIFY(s1 == "-1,-1,-1,-1"); | |
98 | string s2 = format("{0:},{0:+},{0:-},{0: }", inf); | |
99 | VERIFY(s2 == "inf,+inf,inf, inf"); | |
100 | string s3 = format("{0:},{0:+},{0:-},{0: }", nan); | |
101 | VERIFY(s3 == "nan,+nan,nan, nan"); | |
102 | } | |
103 | ||
104 | // alternate form and zero fill | |
105 | { | |
106 | char c = 120; | |
107 | string s1 = format("{:+06d}", c); | |
108 | VERIFY(s1 == "+00120"); | |
109 | string s2 = format("{:#06x}", 0xa); | |
110 | VERIFY(s2 == "0x000a"); | |
111 | string s3 = format("{:<06}", -42); | |
112 | VERIFY(s3 == "-42 "); // 0 is ignored because of < alignment | |
113 | } | |
114 | ||
115 | // integer presentation types | |
116 | { | |
117 | // Change global locale so "{:L}" adds digit separators. | |
118 | std::locale::global(std::locale({}, new brit_punc)); | |
119 | ||
120 | string s0 = format("{}", 42); | |
121 | VERIFY(s0 == "42"); | |
122 | string s1 = format("{0:b} {0:d} {0:o} {0:x}", 42); | |
123 | VERIFY(s1 == "101010 42 52 2a"); | |
124 | string s2 = format("{0:#x} {0:#X}", 42); | |
125 | VERIFY(s2 == "0x2a 0X2A"); | |
126 | string s3 = format("{:L}", 1234); | |
127 | VERIFY(s3 == "1,234"); | |
128 | ||
48e21e87 JW |
129 | // Test locale's "byte-and-a-half" grouping (Imperial word? tribble?). |
130 | string s4 = format("{:#Lx}", 0xfffff); | |
131 | VERIFY(s4 == "0xff,fff"); | |
132 | ||
1d9454ab JW |
133 | // Restore |
134 | std::locale::global(std::locale::classic()); | |
135 | } | |
136 | } | |
137 | ||
138 | void | |
139 | test_alternate_forms() | |
140 | { | |
141 | std::string s; | |
142 | ||
143 | s = std::format("{0:#b} {0:+#B} {0:#o} {0:#x} {0:+#X} {0: #d}", 42); | |
144 | VERIFY( s == "0b101010 +0B101010 052 0x2a +0X2A 42" ); | |
145 | s = std::format("{0:#b} {0:+#B} {0:#o} {0:#x} {0:+#X} {0: #d}", 0); | |
146 | VERIFY( s == "0b0 +0B0 0 0x0 +0X0 0" ); | |
147 | ||
148 | s = std::format("{0:+#012g} {0:+#014g} {0:+#014g}", 1234.0); | |
149 | VERIFY( s == "+00001234.00 +0000001234.00 +0000001234.00" ); | |
150 | s = std::format("{0:+#0{1}g} {0:+#0{2}g} {0:+#0{2}g}", 1234.5, 12, 14); | |
151 | VERIFY( s == "+00001234.50 +0000001234.50 +0000001234.50" ); | |
152 | ||
153 | s = std::format("{:#.2g}", -0.0); | |
154 | VERIFY( s == "-0.0" ); | |
50bc490c JW |
155 | |
156 | // PR libstdc++/108046 | |
157 | s = std::format("{0:#.0} {0:#.1} {0:#.0g}", 10.0); | |
158 | VERIFY( s == "1.e+01 1.e+01 1.e+01" ); | |
1d9454ab JW |
159 | } |
160 | ||
161 | struct euro_punc : std::numpunct<char> | |
162 | { | |
163 | std::string do_grouping() const override { return "\3\3"; } | |
164 | char do_thousands_sep() const override { return '.'; } | |
165 | char do_decimal_point() const override { return ','; } | |
166 | }; | |
167 | ||
168 | void | |
169 | test_locale() | |
170 | { | |
171 | // The default C locale. | |
172 | std::locale cloc = std::locale::classic(); | |
173 | // A custom locale using comma digit separators. | |
174 | std::locale bloc(cloc, new brit_punc); | |
175 | // A custom locale using period digit separators. | |
176 | std::locale eloc(cloc, new euro_punc); | |
177 | ||
178 | std::string s; | |
179 | ||
180 | // Change the global locale: | |
181 | std::locale::global(bloc); | |
182 | // Format using the global locale: | |
183 | s = std::format("{0:L} {0:Lx} {0:Lb}", 12345); | |
184 | VERIFY( s == "12,345 3,039 11,000,000,111,001" ); | |
185 | s = std::format("{0:L} {0:.7Lg} {0:La}", 12345.6789); | |
186 | VERIFY( s == "12,345.6789 12,345.68 1.81cd6e631f8a1p+13" ); | |
187 | ||
188 | s = std::format("{0:s} {0:L} {1:Ls} {0:Ld}", true, false); | |
189 | VERIFY( s == "true yes mate nah bruv 1" ); | |
190 | ||
191 | // Format using a specific locale: | |
192 | s = std::format(eloc, "{0:L} {0:Lx} {0:Lb}", 12345); | |
193 | VERIFY( s == "12.345 3.039 11.000.000.111.001" ); | |
194 | s = std::format(eloc, "{0:L} {0:.7LG} {0:La}", 12345.6789); | |
195 | VERIFY( s == "12.345,6789 12.345,68 1,81cd6e631f8a1p+13" ); | |
196 | ||
197 | s = std::format(eloc, "{0:#Lg} {0:+#.3Lg} {0:#08.4Lg}", -1234.); | |
198 | VERIFY( s == "-1.234,00 -1,23e+03 -01.234," ); | |
199 | ||
f48a5423 JW |
200 | s = std::format(cloc, "{:05L}", -1.0); // PR libstdc++/110968 |
201 | VERIFY( s == "-0001" ); | |
202 | ||
1d9454ab JW |
203 | // Restore |
204 | std::locale::global(cloc); | |
205 | } | |
206 | ||
207 | void | |
208 | test_width() | |
209 | { | |
210 | std::string s; | |
211 | ||
212 | s = std::format("{:4}", ""); | |
213 | VERIFY( s == " " ); | |
214 | s = std::format("{:{}}", "", 3); | |
215 | VERIFY( s == " " ); | |
628ba410 JW |
216 | s = std::format("{:{}}|{:{}}", 1, 2, 3, 4); |
217 | VERIFY( s == " 1| 3" ); | |
1d9454ab JW |
218 | s = std::format("{1:{0}}", 2, ""); |
219 | VERIFY( s == " " ); | |
220 | s = std::format("{:03}", 9); | |
221 | VERIFY( s == "009" ); | |
222 | ||
223 | s = std::format("DR {0:{1}}: allow width {1} from arg-id", 3721, 0); | |
224 | VERIFY( s == "DR 3721: allow width 0 from arg-id" ); | |
225 | ||
226 | try { | |
227 | s = std::format("Negative width is an error: {0:{1}}", 123, -1); | |
228 | VERIFY(false); | |
229 | } catch (const std::format_error&) { | |
230 | } | |
231 | ||
232 | try { | |
233 | auto args = std::make_format_args(false, true); | |
234 | s = std::vformat("DR 3720: restrict type of width arg-id {0:{1}}", args); | |
235 | VERIFY(false); | |
236 | } catch (const std::format_error&) { | |
237 | } | |
238 | ||
239 | try { | |
240 | auto args = std::make_format_args('?', '!'); | |
241 | s = std::vformat("DR 3720: restrict type of width arg-id {0:{1}}", args); | |
242 | VERIFY(false); | |
243 | } catch (const std::format_error&) { | |
244 | } | |
245 | } | |
246 | ||
247 | void | |
248 | test_wchar() | |
249 | { | |
250 | using namespace std::literals; | |
251 | std::wstring s; | |
252 | ||
253 | s = std::format(L"{} {} {} {} {} {}", L'0', 1, 2LL, 3.4, L"five", L"six"s); | |
254 | VERIFY( s == L"0 1 2 3.4 five six" ); | |
255 | ||
256 | std::locale loc; | |
257 | s = std::format(loc, L"{:L} {:.3s}{:Lc}", true, L"data"sv, '.'); | |
258 | VERIFY( s == L"true dat." ); | |
4a2b2625 JW |
259 | |
260 | s = std::format(L"{}", 0.0625); | |
261 | VERIFY( s == L"0.0625" ); | |
262 | s = std::format(L"{}", 0.25); | |
263 | VERIFY( s == L"0.25" ); | |
1d9454ab JW |
264 | } |
265 | ||
266 | void | |
267 | test_minmax() | |
268 | { | |
c68c468e | 269 | auto check = []<typename T, typename U = std::make_unsigned_t<T>>(T, U = 0) { |
1d9454ab JW |
270 | const int digits = std::numeric_limits<T>::digits; |
271 | const std::string zeros(digits, '0'); | |
272 | const std::string ones(digits, '1'); | |
273 | auto s = std::format("{:b}" , std::numeric_limits<T>::min()); | |
274 | VERIFY( s == "-1" + zeros ); | |
275 | s = std::format("{:b}" , std::numeric_limits<T>::max()); | |
276 | VERIFY( s == ones ); | |
1d9454ab JW |
277 | s = std::format("{:0{}b}" , std::numeric_limits<U>::min(), digits + 1); |
278 | VERIFY( s == '0' + zeros ); | |
279 | s = std::format("{:b}" , std::numeric_limits<U>::max()); | |
280 | VERIFY( s == '1' + ones ); | |
281 | }; | |
282 | check(std::int8_t(0)); | |
283 | check(std::int16_t(0)); | |
284 | check(std::int32_t(0)); | |
285 | check(std::int64_t(0)); | |
286 | #ifdef __SIZEOF_INT128__ | |
c68c468e JW |
287 | // std::make_unsigned_t<__int128> is invalid for strict -std=c++20 mode, |
288 | // so pass a second argument of the unsigned type. | |
289 | check(__int128(0), (unsigned __int128)(0)); | |
1d9454ab JW |
290 | #endif |
291 | } | |
292 | ||
293 | void | |
294 | test_p1652r1() // printf corner cases in std::format | |
295 | { | |
296 | std::string s; | |
297 | ||
298 | // Problem 1: "#o" specification should not print 0 as "00" | |
299 | s = std::format("{:#o}", 0); | |
300 | VERIFY( s == "0" ); | |
301 | ||
302 | // Problem 2: 'c' should be able to print 65 as "A" (ASCII) | |
303 | int c = 'A'; | |
304 | s = std::format("{:c}", c); | |
305 | VERIFY( s == "A" ); | |
306 | ||
307 | // Problem 3: "-000nan" is not a floating point value | |
308 | double nan = std::numeric_limits<double>::quiet_NaN(); | |
309 | try { | |
310 | s = std::vformat("{:0=6}", std::make_format_args(nan)); | |
311 | VERIFY( false ); | |
312 | } catch (const std::format_error&) { | |
313 | } | |
314 | ||
315 | s = std::format("{:06}", nan); | |
316 | VERIFY( s == " nan" ); | |
317 | ||
318 | // Problem 4: bool needs a type format specifier | |
319 | s = std::format("{:s}", true); | |
320 | VERIFY( s == "true" ); | |
321 | ||
322 | // Problem 5: double does not roundtrip float | |
323 | s = std::format("{}", 3.31f); | |
324 | VERIFY( s == "3.31" ); | |
325 | } | |
326 | ||
ce86d967 JW |
327 | template<typename T> |
328 | bool format_float() | |
329 | { | |
330 | auto s = std::format("{:#} != {:<+7.3f}", (T)-0.0, (T)0.5); | |
331 | return s == "-0. != +0.500 "; | |
332 | } | |
333 | ||
a5d4f38f JW |
334 | #if __cplusplus > 202002L |
335 | template<typename T> | |
336 | concept formattable = std::formattable<T, char>; | |
337 | #else | |
338 | template<typename T> | |
339 | concept formattable = requires (T t, char* p) { std::to_chars(p, p, t); }; | |
340 | #endif | |
341 | ||
1d9454ab JW |
342 | void |
343 | test_float128() | |
344 | { | |
345 | #ifdef __SIZEOF_FLOAT128__ | |
a5d4f38f | 346 | if constexpr (formattable<__float128>) |
ce86d967 JW |
347 | VERIFY( format_float<__float128>() ); |
348 | else | |
349 | std::puts("Cannot format __float128 on this target"); | |
350 | #endif | |
351 | #if __FLT128_DIG__ | |
a5d4f38f | 352 | if constexpr (formattable<_Float128>) |
ce86d967 JW |
353 | VERIFY( format_float<_Float128>() ); |
354 | else | |
355 | std::puts("Cannot format _Float128 on this target"); | |
1d9454ab JW |
356 | #endif |
357 | } | |
358 | ||
628ba410 JW |
359 | void |
360 | test_pointer() | |
361 | { | |
362 | void* p = nullptr; | |
363 | const void* pc = p; | |
364 | std::string s, str_int; | |
365 | ||
366 | s = std::format("{} {} {}", p, pc, nullptr); | |
367 | VERIFY( s == "0x0 0x0 0x0" ); | |
368 | s = std::format("{:p} {:p} {:p}", p, pc, nullptr); | |
369 | VERIFY( s == "0x0 0x0 0x0" ); | |
370 | s = std::format("{:4},{:5},{:6}", p, pc, nullptr); // width | |
371 | VERIFY( s == " 0x0, 0x0, 0x0" ); | |
372 | s = std::format("{:<4},{:>5},{:^7}", p, pc, nullptr); // align+width | |
373 | VERIFY( s == "0x0 , 0x0, 0x0 " ); | |
374 | s = std::format("{:o<4},{:o>5},{:o^7}", p, pc, nullptr); // fill+align+width | |
375 | VERIFY( s == "0x0o,oo0x0,oo0x0oo" ); | |
376 | ||
377 | pc = p = &s; | |
378 | str_int = std::format("{:#x}", reinterpret_cast<std::uintptr_t>(p)); | |
379 | s = std::format("{} {} {}", p, pc, nullptr); | |
380 | VERIFY( s == (str_int + ' ' + str_int + " 0x0") ); | |
381 | str_int = std::format("{:#20x}", reinterpret_cast<std::uintptr_t>(p)); | |
382 | s = std::format("{:20} {:20p}", p, pc); | |
383 | VERIFY( s == (str_int + ' ' + str_int) ); | |
384 | ||
385 | #if __cplusplus > 202302L || ! defined __STRICT_ANSI__ | |
386 | // P2510R3 Formatting pointers | |
387 | s = std::format("{:06} {:07P} {:08p}", (void*)0, (const void*)0, nullptr); | |
388 | VERIFY( s == "0x0000 0X00000 0x000000" ); | |
389 | str_int = std::format("{:#016x}", reinterpret_cast<std::uintptr_t>(p)); | |
390 | s = std::format("{:016} {:016}", p, pc); | |
391 | VERIFY( s == (str_int + ' ' + str_int) ); | |
392 | str_int = std::format("{:#016X}", reinterpret_cast<std::uintptr_t>(p)); | |
393 | s = std::format("{:016P} {:016P}", p, pc); | |
394 | VERIFY( s == (str_int + ' ' + str_int) ); | |
395 | #endif | |
396 | } | |
397 | ||
1d9454ab JW |
398 | int main() |
399 | { | |
400 | test_no_args(); | |
401 | test_unescaped(); | |
402 | test_std_examples(); | |
403 | test_alternate_forms(); | |
404 | test_locale(); | |
405 | test_width(); | |
406 | test_wchar(); | |
407 | test_minmax(); | |
408 | test_p1652r1(); | |
409 | test_float128(); | |
628ba410 | 410 | test_pointer(); |
1d9454ab | 411 | } |