// { dg-options "-std=gnu++20" } // { dg-do run { target c++20 } } #include #ifndef __cpp_lib_format # error "Feature test macro for std::format is missing in " #elif __cpp_lib_format < 202106L # error "Feature test macro for std::format has wrong value in " #endif #undef __cpp_lib_format #include #ifndef __cpp_lib_format # error "Feature test macro for std::format is missing in " #elif __cpp_lib_format < 202106L # error "Feature test macro for std::format has wrong value in " #endif #include #include #include #include #include void test_no_args() { std::string s; s = std::format("disco"); VERIFY( s == "disco" ); s = std::format("}} machine {{ funk }} specialists {{"); VERIFY( s == "} machine { funk } specialists {" ); s = std::format("128bpm }}"); VERIFY( s == "128bpm }" ); } void test_unescaped() { #ifdef __cpp_exceptions for (auto f : { "{", "}", "{{{", "{{}", "}{", "{{{{{" }) try { (void) std::vformat(f, std::make_format_args()); VERIFY( false ); } catch (const std::format_error& e) { std::string what = e.what(); VERIFY( what.find("unmatched") != what.npos ); } #endif } struct brit_punc : std::numpunct { std::string do_grouping() const override { return "\3\3"; } char do_thousands_sep() const override { return ','; } std::string do_truename() const override { return "yes mate"; } std::string do_falsename() const override { return "nah bruv"; } }; void test_std_examples() { using namespace std; string s = format("{0}-{{", 8); // value of s is "8-{" VERIFY( s == "8-{" ); // align { char c = 120; string s0 = format("{:6}", 42); VERIFY(s0 == " 42"); string s1 = format("{:6}", 'x'); VERIFY(s1 == "x "); string s2 = format("{:*<6}", 'x'); VERIFY(s2 == "x*****"); string s3 = format("{:*>6}", 'x'); VERIFY(s3 == "*****x"); string s4 = format("{:*^6}", 'x'); VERIFY(s4 == "**x***"); string s5 = format("{:6d}", c); VERIFY(s5 == " 120"); string s6 = format("{:6}", true); VERIFY(s6 == "true "); } // sign { double inf = numeric_limits::infinity(); double nan = numeric_limits::quiet_NaN(); string s0 = format("{0:},{0:+},{0:-},{0: }", 1); VERIFY(s0 == "1,+1,1, 1"); string s1 = format("{0:},{0:+},{0:-},{0: }", -1); VERIFY(s1 == "-1,-1,-1,-1"); string s2 = format("{0:},{0:+},{0:-},{0: }", inf); VERIFY(s2 == "inf,+inf,inf, inf"); string s3 = format("{0:},{0:+},{0:-},{0: }", nan); VERIFY(s3 == "nan,+nan,nan, nan"); } // alternate form and zero fill { char c = 120; string s1 = format("{:+06d}", c); VERIFY(s1 == "+00120"); string s2 = format("{:#06x}", 0xa); VERIFY(s2 == "0x000a"); string s3 = format("{:<06}", -42); VERIFY(s3 == "-42 "); // 0 is ignored because of < alignment } // integer presentation types { // Change global locale so "{:L}" adds digit separators. std::locale::global(std::locale({}, new brit_punc)); string s0 = format("{}", 42); VERIFY(s0 == "42"); string s1 = format("{0:b} {0:d} {0:o} {0:x}", 42); VERIFY(s1 == "101010 42 52 2a"); string s2 = format("{0:#x} {0:#X}", 42); VERIFY(s2 == "0x2a 0X2A"); string s3 = format("{:L}", 1234); VERIFY(s3 == "1,234"); // Test locale's "byte-and-a-half" grouping (Imperial word? tribble?). string s4 = format("{:#Lx}", 0xfffff); VERIFY(s4 == "0xff,fff"); // Restore std::locale::global(std::locale::classic()); } } void test_alternate_forms() { std::string s; s = std::format("{0:#b} {0:+#B} {0:#o} {0:#x} {0:+#X} {0: #d}", 42); VERIFY( s == "0b101010 +0B101010 052 0x2a +0X2A 42" ); s = std::format("{0:#b} {0:+#B} {0:#o} {0:#x} {0:+#X} {0: #d}", 0); VERIFY( s == "0b0 +0B0 0 0x0 +0X0 0" ); s = std::format("{0:+#012g} {0:+#014g} {0:+#014g}", 1234.0); VERIFY( s == "+00001234.00 +0000001234.00 +0000001234.00" ); s = std::format("{0:+#0{1}g} {0:+#0{2}g} {0:+#0{2}g}", 1234.5, 12, 14); VERIFY( s == "+00001234.50 +0000001234.50 +0000001234.50" ); s = std::format("{:#.2g}", -0.0); VERIFY( s == "-0.0" ); } struct euro_punc : std::numpunct { std::string do_grouping() const override { return "\3\3"; } char do_thousands_sep() const override { return '.'; } char do_decimal_point() const override { return ','; } }; void test_locale() { // The default C locale. std::locale cloc = std::locale::classic(); // A custom locale using comma digit separators. std::locale bloc(cloc, new brit_punc); // A custom locale using period digit separators. std::locale eloc(cloc, new euro_punc); std::string s; // Change the global locale: std::locale::global(bloc); // Format using the global locale: s = std::format("{0:L} {0:Lx} {0:Lb}", 12345); VERIFY( s == "12,345 3,039 11,000,000,111,001" ); s = std::format("{0:L} {0:.7Lg} {0:La}", 12345.6789); VERIFY( s == "12,345.6789 12,345.68 1.81cd6e631f8a1p+13" ); s = std::format("{0:s} {0:L} {1:Ls} {0:Ld}", true, false); VERIFY( s == "true yes mate nah bruv 1" ); // Format using a specific locale: s = std::format(eloc, "{0:L} {0:Lx} {0:Lb}", 12345); VERIFY( s == "12.345 3.039 11.000.000.111.001" ); s = std::format(eloc, "{0:L} {0:.7LG} {0:La}", 12345.6789); VERIFY( s == "12.345,6789 12.345,68 1,81cd6e631f8a1p+13" ); s = std::format(eloc, "{0:#Lg} {0:+#.3Lg} {0:#08.4Lg}", -1234.); VERIFY( s == "-1.234,00 -1,23e+03 -01.234," ); // Restore std::locale::global(cloc); } void test_width() { std::string s; s = std::format("{:4}", ""); VERIFY( s == " " ); s = std::format("{:{}}", "", 3); VERIFY( s == " " ); s = std::format("{1:{0}}", 2, ""); VERIFY( s == " " ); s = std::format("{:03}", 9); VERIFY( s == "009" ); s = std::format("DR {0:{1}}: allow width {1} from arg-id", 3721, 0); VERIFY( s == "DR 3721: allow width 0 from arg-id" ); try { s = std::format("Negative width is an error: {0:{1}}", 123, -1); VERIFY(false); } catch (const std::format_error&) { } try { auto args = std::make_format_args(false, true); s = std::vformat("DR 3720: restrict type of width arg-id {0:{1}}", args); VERIFY(false); } catch (const std::format_error&) { } try { auto args = std::make_format_args('?', '!'); s = std::vformat("DR 3720: restrict type of width arg-id {0:{1}}", args); VERIFY(false); } catch (const std::format_error&) { } } void test_wchar() { using namespace std::literals; std::wstring s; s = std::format(L"{} {} {} {} {} {}", L'0', 1, 2LL, 3.4, L"five", L"six"s); VERIFY( s == L"0 1 2 3.4 five six" ); std::locale loc; s = std::format(loc, L"{:L} {:.3s}{:Lc}", true, L"data"sv, '.'); VERIFY( s == L"true dat." ); } void test_minmax() { auto check = []>(T, U = 0) { const int digits = std::numeric_limits::digits; const std::string zeros(digits, '0'); const std::string ones(digits, '1'); auto s = std::format("{:b}" , std::numeric_limits::min()); VERIFY( s == "-1" + zeros ); s = std::format("{:b}" , std::numeric_limits::max()); VERIFY( s == ones ); s = std::format("{:0{}b}" , std::numeric_limits::min(), digits + 1); VERIFY( s == '0' + zeros ); s = std::format("{:b}" , std::numeric_limits::max()); VERIFY( s == '1' + ones ); }; check(std::int8_t(0)); check(std::int16_t(0)); check(std::int32_t(0)); check(std::int64_t(0)); #ifdef __SIZEOF_INT128__ // std::make_unsigned_t<__int128> is invalid for strict -std=c++20 mode, // so pass a second argument of the unsigned type. check(__int128(0), (unsigned __int128)(0)); #endif } void test_p1652r1() // printf corner cases in std::format { std::string s; // Problem 1: "#o" specification should not print 0 as "00" s = std::format("{:#o}", 0); VERIFY( s == "0" ); // Problem 2: 'c' should be able to print 65 as "A" (ASCII) int c = 'A'; s = std::format("{:c}", c); VERIFY( s == "A" ); // Problem 3: "-000nan" is not a floating point value double nan = std::numeric_limits::quiet_NaN(); try { s = std::vformat("{:0=6}", std::make_format_args(nan)); VERIFY( false ); } catch (const std::format_error&) { } s = std::format("{:06}", nan); VERIFY( s == " nan" ); // Problem 4: bool needs a type format specifier s = std::format("{:s}", true); VERIFY( s == "true" ); // Problem 5: double does not roundtrip float s = std::format("{}", 3.31f); VERIFY( s == "3.31" ); } template bool format_float() { auto s = std::format("{:#} != {:<+7.3f}", (T)-0.0, (T)0.5); return s == "-0. != +0.500 "; } #if __cplusplus > 202002L template concept formattable = std::formattable; #else template concept formattable = requires (T t, char* p) { std::to_chars(p, p, t); }; #endif void test_float128() { #ifdef __SIZEOF_FLOAT128__ if constexpr (formattable<__float128>) VERIFY( format_float<__float128>() ); else std::puts("Cannot format __float128 on this target"); #endif #if __FLT128_DIG__ if constexpr (formattable<_Float128>) VERIFY( format_float<_Float128>() ); else std::puts("Cannot format _Float128 on this target"); #endif } int main() { test_no_args(); test_unescaped(); test_std_examples(); test_alternate_forms(); test_locale(); test_width(); test_wchar(); test_minmax(); test_p1652r1(); test_float128(); }