xchar-test.cc
1 // Formatting library for C++ - formatting library tests 2 // 3 // Copyright (c) 2012 - present, Victor Zverovich 4 // All rights reserved. 5 // 6 // For the license information refer to format.h. 7 8 #include "fmt/xchar.h" 9 10 #include <algorithm> 11 #include <complex> 12 #include <cwchar> 13 #include <vector> 14 15 #include "fmt/chrono.h" 16 #include "fmt/color.h" 17 #include "fmt/ostream.h" 18 #include "fmt/ranges.h" 19 #include "fmt/std.h" 20 #include "gtest-extra.h" // Contains 21 #include "util.h" // get_locale 22 23 using fmt::detail::max_value; 24 using testing::Contains; 25 26 #if defined(__MINGW32__) && !defined(_UCRT) 27 // Only C89 conversion specifiers when using MSVCRT instead of UCRT 28 # define FMT_HAS_C99_STRFTIME 0 29 #else 30 # define FMT_HAS_C99_STRFTIME 1 31 #endif 32 33 struct non_string {}; 34 35 template <typename T> class is_string_test : public testing::Test {}; 36 37 using string_char_types = testing::Types<char, wchar_t, char16_t, char32_t>; 38 TYPED_TEST_SUITE(is_string_test, string_char_types); 39 40 template <typename Char> 41 struct derived_from_string_view : fmt::basic_string_view<Char> {}; 42 43 TYPED_TEST(is_string_test, is_string) { 44 EXPECT_TRUE(fmt::detail::is_string<TypeParam*>::value); 45 EXPECT_TRUE(fmt::detail::is_string<const TypeParam*>::value); 46 EXPECT_TRUE(fmt::detail::is_string<TypeParam[2]>::value); 47 EXPECT_TRUE(fmt::detail::is_string<const TypeParam[2]>::value); 48 EXPECT_TRUE(fmt::detail::is_string<std::basic_string<TypeParam>>::value); 49 EXPECT_TRUE(fmt::detail::is_string<fmt::basic_string_view<TypeParam>>::value); 50 EXPECT_TRUE( 51 fmt::detail::is_string<derived_from_string_view<TypeParam>>::value); 52 using fmt_string_view = fmt::detail::std_string_view<TypeParam>; 53 EXPECT_TRUE(std::is_empty<fmt_string_view>::value != 54 fmt::detail::is_string<fmt_string_view>::value); 55 EXPECT_FALSE(fmt::detail::is_string<non_string>::value); 56 } 57 58 // std::is_constructible is broken in MSVC until version 2015. 59 #if !FMT_MSC_VERSION || FMT_MSC_VERSION >= 1900 60 struct explicitly_convertible_to_wstring_view { 61 explicit operator fmt::wstring_view() const { return L"foo"; } 62 }; 63 64 TEST(xchar_test, format_explicitly_convertible_to_wstring_view) { 65 // Types explicitly convertible to wstring_view are not formattable by 66 // default because it may introduce ODR violations. 67 static_assert( 68 !fmt::is_formattable<explicitly_convertible_to_wstring_view>::value, ""); 69 } 70 #endif 71 72 TEST(xchar_test, format) { 73 EXPECT_EQ(L"42", fmt::format(L"{}", 42)); 74 EXPECT_EQ(L"4.2", fmt::format(L"{}", 4.2)); 75 EXPECT_EQ(L"abc", fmt::format(L"{}", L"abc")); 76 EXPECT_EQ(L"z", fmt::format(L"{}", L'z')); 77 EXPECT_THROW(fmt::format(fmt::runtime(L"{:*\x343E}"), 42), fmt::format_error); 78 EXPECT_EQ(L"true", fmt::format(L"{}", true)); 79 EXPECT_EQ(L"a", fmt::format(L"{0}", 'a')); 80 EXPECT_EQ(L"a", fmt::format(L"{0}", L'a')); 81 EXPECT_EQ(L"Cyrillic letter \x42e", 82 fmt::format(L"Cyrillic letter {}", L'\x42e')); 83 EXPECT_EQ(L"abc1", fmt::format(L"{}c{}", L"ab", 1)); 84 } 85 86 TEST(xchar_test, is_formattable) { 87 static_assert(!fmt::is_formattable<const wchar_t*>::value, ""); 88 } 89 90 TEST(xchar_test, compile_time_string) { 91 EXPECT_EQ(fmt::format(fmt::wformat_string<int>(L"{}"), 42), L"42"); 92 #if defined(FMT_USE_STRING_VIEW) && FMT_CPLUSPLUS >= 201703L 93 EXPECT_EQ(fmt::format(FMT_STRING(std::wstring_view(L"{}")), 42), L"42"); 94 #endif 95 } 96 97 #if FMT_CPLUSPLUS > 201103L 98 struct custom_char { 99 int value; 100 custom_char() = default; 101 102 template <typename T> 103 constexpr custom_char(T val) : value(static_cast<int>(val)) {} 104 105 operator char() const { 106 return value <= 0xff ? static_cast<char>(value) : '\0'; 107 } 108 }; 109 110 auto to_ascii(custom_char c) -> char { return c; } 111 112 FMT_BEGIN_NAMESPACE 113 template <> struct is_char<custom_char> : std::true_type {}; 114 FMT_END_NAMESPACE 115 116 TEST(xchar_test, format_custom_char) { 117 const custom_char format[] = {'{', '}', 0}; 118 auto result = fmt::format(format, custom_char('x')); 119 EXPECT_EQ(result.size(), 1); 120 EXPECT_EQ(result[0], custom_char('x')); 121 } 122 #endif 123 124 // Convert a char8_t string to std::string. Otherwise GTest will insist on 125 // inserting `char8_t` NTBS into a `char` stream which is disabled by P1423. 126 template <typename S> std::string from_u8str(const S& str) { 127 return std::string(str.begin(), str.end()); 128 } 129 130 TEST(xchar_test, format_utf8_precision) { 131 using str_type = std::basic_string<fmt::detail::char8_type>; 132 auto format = 133 str_type(reinterpret_cast<const fmt::detail::char8_type*>(u8"{:.4}")); 134 auto str = str_type(reinterpret_cast<const fmt::detail::char8_type*>( 135 u8"caf\u00e9s")); // cafés 136 auto result = fmt::format(format, str); 137 EXPECT_EQ(fmt::detail::compute_width(result), 4); 138 EXPECT_EQ(result.size(), 5); 139 EXPECT_EQ(from_u8str(result), from_u8str(str.substr(0, 5))); 140 } 141 142 TEST(xchar_test, format_to) { 143 auto buf = std::vector<wchar_t>(); 144 fmt::format_to(std::back_inserter(buf), L"{}{}", 42, L'\0'); 145 EXPECT_STREQ(buf.data(), L"42"); 146 } 147 148 TEST(xchar_test, vformat_to) { 149 auto args = fmt::make_wformat_args(42); 150 auto w = std::wstring(); 151 fmt::vformat_to(std::back_inserter(w), L"{}", args); 152 EXPECT_EQ(L"42", w); 153 } 154 155 namespace test { 156 struct struct_as_wstring_view {}; 157 auto format_as(struct_as_wstring_view) -> fmt::wstring_view { return L"foo"; } 158 } // namespace test 159 160 TEST(xchar_test, format_as) { 161 EXPECT_EQ(fmt::format(L"{}", test::struct_as_wstring_view()), L"foo"); 162 } 163 164 TEST(format_test, wide_format_to_n) { 165 wchar_t buffer[4]; 166 buffer[3] = L'x'; 167 auto result = fmt::format_to_n(buffer, 3, L"{}", 12345); 168 EXPECT_EQ(5u, result.size); 169 EXPECT_EQ(buffer + 3, result.out); 170 EXPECT_EQ(L"123x", fmt::wstring_view(buffer, 4)); 171 buffer[0] = L'x'; 172 buffer[1] = L'x'; 173 buffer[2] = L'x'; 174 result = fmt::format_to_n(buffer, 3, L"{}", L'A'); 175 EXPECT_EQ(1u, result.size); 176 EXPECT_EQ(buffer + 1, result.out); 177 EXPECT_EQ(L"Axxx", fmt::wstring_view(buffer, 4)); 178 result = fmt::format_to_n(buffer, 3, L"{}{} ", L'B', L'C'); 179 EXPECT_EQ(3u, result.size); 180 EXPECT_EQ(buffer + 3, result.out); 181 EXPECT_EQ(L"BC x", fmt::wstring_view(buffer, 4)); 182 } 183 184 #if FMT_USE_USER_DEFINED_LITERALS 185 TEST(xchar_test, named_arg_udl) { 186 using namespace fmt::literals; 187 auto udl_a = 188 fmt::format(L"{first}{second}{first}{third}", L"first"_a = L"abra", 189 L"second"_a = L"cad", L"third"_a = 99); 190 EXPECT_EQ( 191 fmt::format(L"{first}{second}{first}{third}", fmt::arg(L"first", L"abra"), 192 fmt::arg(L"second", L"cad"), fmt::arg(L"third", 99)), 193 udl_a); 194 } 195 #endif // FMT_USE_USER_DEFINED_LITERALS 196 197 TEST(xchar_test, print) { 198 // Check that the wide print overload compiles. 199 if (fmt::detail::const_check(false)) { 200 fmt::print(L"test"); 201 fmt::println(L"test"); 202 } 203 } 204 205 TEST(xchar_test, join) { 206 int v[3] = {1, 2, 3}; 207 EXPECT_EQ(fmt::format(L"({})", fmt::join(v, v + 3, L", ")), L"(1, 2, 3)"); 208 auto t = std::tuple<wchar_t, int, float>('a', 1, 2.0f); 209 EXPECT_EQ(fmt::format(L"({})", fmt::join(t, L", ")), L"(a, 1, 2)"); 210 } 211 212 enum streamable_enum {}; 213 214 std::wostream& operator<<(std::wostream& os, streamable_enum) { 215 return os << L"streamable_enum"; 216 } 217 218 namespace fmt { 219 template <> 220 struct formatter<streamable_enum, wchar_t> : basic_ostream_formatter<wchar_t> { 221 }; 222 } // namespace fmt 223 224 enum unstreamable_enum {}; 225 auto format_as(unstreamable_enum e) -> int { return e; } 226 227 TEST(xchar_test, enum) { 228 EXPECT_EQ(L"streamable_enum", fmt::format(L"{}", streamable_enum())); 229 EXPECT_EQ(L"0", fmt::format(L"{}", unstreamable_enum())); 230 } 231 232 struct streamable_and_unformattable {}; 233 234 auto operator<<(std::wostream& os, streamable_and_unformattable) 235 -> std::wostream& { 236 return os << L"foo"; 237 } 238 239 TEST(xchar_test, streamed) { 240 EXPECT_FALSE(fmt::is_formattable<streamable_and_unformattable>()); 241 EXPECT_EQ(fmt::format(L"{}", fmt::streamed(streamable_and_unformattable())), 242 L"foo"); 243 } 244 245 TEST(xchar_test, sign_not_truncated) { 246 wchar_t format_str[] = { 247 L'{', L':', 248 '+' | static_cast<wchar_t>(1 << fmt::detail::num_bits<char>()), L'}', 0}; 249 EXPECT_THROW(fmt::format(fmt::runtime(format_str), 42), fmt::format_error); 250 } 251 252 TEST(xchar_test, chrono) { 253 auto tm = std::tm(); 254 tm.tm_year = 116; 255 tm.tm_mon = 3; 256 tm.tm_mday = 25; 257 tm.tm_hour = 11; 258 tm.tm_min = 22; 259 tm.tm_sec = 33; 260 EXPECT_EQ(fmt::format("The date is {:%Y-%m-%d %H:%M:%S}.", tm), 261 "The date is 2016-04-25 11:22:33."); 262 EXPECT_EQ(L"42s", fmt::format(L"{}", std::chrono::seconds(42))); 263 EXPECT_EQ(fmt::format(L"{:%F}", tm), L"2016-04-25"); 264 EXPECT_EQ(fmt::format(L"{:%T}", tm), L"11:22:33"); 265 } 266 267 std::wstring system_wcsftime(const std::wstring& format, const std::tm* timeptr, 268 std::locale* locptr = nullptr) { 269 auto loc = locptr ? *locptr : std::locale::classic(); 270 auto& facet = std::use_facet<std::time_put<wchar_t>>(loc); 271 std::wostringstream os; 272 os.imbue(loc); 273 facet.put(os, os, L' ', timeptr, format.c_str(), 274 format.c_str() + format.size()); 275 #ifdef _WIN32 276 // Workaround a bug in older versions of Universal CRT. 277 auto str = os.str(); 278 if (str == L"-0000") str = L"+0000"; 279 return str; 280 #else 281 return os.str(); 282 #endif 283 } 284 285 TEST(chrono_test_wchar, time_point) { 286 auto t1 = std::chrono::time_point_cast<std::chrono::seconds>( 287 std::chrono::system_clock::now()); 288 289 std::vector<std::wstring> spec_list = { 290 L"%%", L"%n", L"%t", L"%Y", L"%EY", L"%y", L"%Oy", L"%Ey", L"%C", 291 L"%EC", L"%G", L"%g", L"%b", L"%h", L"%B", L"%m", L"%Om", L"%U", 292 L"%OU", L"%W", L"%OW", L"%V", L"%OV", L"%j", L"%d", L"%Od", L"%e", 293 L"%Oe", L"%a", L"%A", L"%w", L"%Ow", L"%u", L"%Ou", L"%H", L"%OH", 294 L"%I", L"%OI", L"%M", L"%OM", L"%S", L"%OS", L"%x", L"%Ex", L"%X", 295 L"%EX", L"%D", L"%F", L"%R", L"%T", L"%p"}; 296 #ifndef _WIN32 297 // Disabled on Windows, because these formats is not consistent among 298 // platforms. 299 spec_list.insert(spec_list.end(), {L"%c", L"%Ec", L"%r"}); 300 #elif !FMT_HAS_C99_STRFTIME 301 // Only C89 conversion specifiers when using MSVCRT instead of UCRT 302 spec_list = {L"%%", L"%Y", L"%y", L"%b", L"%B", L"%m", L"%U", 303 L"%W", L"%j", L"%d", L"%a", L"%A", L"%w", L"%H", 304 L"%I", L"%M", L"%S", L"%x", L"%X", L"%p"}; 305 #endif 306 spec_list.push_back(L"%Y-%m-%d %H:%M:%S"); 307 308 for (const auto& spec : spec_list) { 309 auto t = std::chrono::system_clock::to_time_t(t1); 310 auto tm = *std::gmtime(&t); 311 312 auto sys_output = system_wcsftime(spec, &tm); 313 314 auto fmt_spec = fmt::format(L"{{:{}}}", spec); 315 EXPECT_EQ(sys_output, fmt::format(fmt::runtime(fmt_spec), t1)); 316 EXPECT_EQ(sys_output, fmt::format(fmt::runtime(fmt_spec), tm)); 317 } 318 319 // Timezone formatters tests makes sense for localtime. 320 #if FMT_HAS_C99_STRFTIME 321 spec_list = {L"%z", L"%Z"}; 322 #else 323 spec_list = {L"%Z"}; 324 #endif 325 for (const auto& spec : spec_list) { 326 auto t = std::chrono::system_clock::to_time_t(t1); 327 auto tm = *std::localtime(&t); 328 329 auto sys_output = system_wcsftime(spec, &tm); 330 331 auto fmt_spec = fmt::format(L"{{:{}}}", spec); 332 EXPECT_EQ(sys_output, fmt::format(fmt::runtime(fmt_spec), tm)); 333 334 if (spec == L"%z") { 335 sys_output.insert(sys_output.end() - 2, 1, L':'); 336 EXPECT_EQ(sys_output, fmt::format(L"{:%Ez}", tm)); 337 EXPECT_EQ(sys_output, fmt::format(L"{:%Oz}", tm)); 338 } 339 } 340 341 // Separate tests for UTC, since std::time_put can use local time and ignoring 342 // the timezone in std::tm (if it presents on platform). 343 if (fmt::detail::has_member_data_tm_zone<std::tm>::value) { 344 auto t = std::chrono::system_clock::to_time_t(t1); 345 auto tm = *std::gmtime(&t); 346 347 std::vector<std::wstring> tz_names = {L"GMT", L"UTC"}; 348 EXPECT_THAT(tz_names, Contains(fmt::format(L"{:%Z}", t1))); 349 EXPECT_THAT(tz_names, Contains(fmt::format(L"{:%Z}", tm))); 350 } 351 352 if (fmt::detail::has_member_data_tm_gmtoff<std::tm>::value) { 353 auto t = std::chrono::system_clock::to_time_t(t1); 354 auto tm = *std::gmtime(&t); 355 356 EXPECT_EQ(L"+0000", fmt::format(L"{:%z}", t1)); 357 EXPECT_EQ(L"+0000", fmt::format(L"{:%z}", tm)); 358 359 EXPECT_EQ(L"+00:00", fmt::format(L"{:%Ez}", t1)); 360 EXPECT_EQ(L"+00:00", fmt::format(L"{:%Ez}", tm)); 361 362 EXPECT_EQ(L"+00:00", fmt::format(L"{:%Oz}", t1)); 363 EXPECT_EQ(L"+00:00", fmt::format(L"{:%Oz}", tm)); 364 } 365 } 366 367 TEST(xchar_test, color) { 368 EXPECT_EQ(fmt::format(fg(fmt::rgb(255, 20, 30)), L"rgb(255,20,30) wide"), 369 L"\x1b[38;2;255;020;030mrgb(255,20,30) wide\x1b[0m"); 370 } 371 372 TEST(xchar_test, ostream) { 373 #if !FMT_GCC_VERSION || FMT_GCC_VERSION >= 409 374 { 375 std::wostringstream wos; 376 fmt::print(wos, L"Don't {}!", L"panic"); 377 EXPECT_EQ(wos.str(), L"Don't panic!"); 378 } 379 380 { 381 std::wostringstream wos; 382 fmt::println(wos, L"Don't {}!", L"panic"); 383 EXPECT_EQ(wos.str(), L"Don't panic!\n"); 384 } 385 #endif 386 } 387 388 TEST(xchar_test, format_map) { 389 auto m = std::map<std::wstring, int>{{L"one", 1}, {L"t\"wo", 2}}; 390 EXPECT_EQ(fmt::format(L"{}", m), L"{\"one\": 1, \"t\\\"wo\": 2}"); 391 } 392 393 TEST(xchar_test, escape_string) { 394 using vec = std::vector<std::wstring>; 395 EXPECT_EQ(fmt::format(L"{}", vec{L"\n\r\t\"\\"}), L"[\"\\n\\r\\t\\\"\\\\\"]"); 396 EXPECT_EQ(fmt::format(L"{}", vec{L"понедельник"}), L"[\"понедельник\"]"); 397 } 398 399 TEST(xchar_test, to_wstring) { EXPECT_EQ(L"42", fmt::to_wstring(42)); } 400 401 #ifndef FMT_STATIC_THOUSANDS_SEPARATOR 402 403 template <typename Char> struct numpunct : std::numpunct<Char> { 404 protected: 405 Char do_decimal_point() const override { return '?'; } 406 std::string do_grouping() const override { return "\03"; } 407 Char do_thousands_sep() const override { return '~'; } 408 }; 409 410 template <typename Char> struct no_grouping : std::numpunct<Char> { 411 protected: 412 Char do_decimal_point() const override { return '.'; } 413 std::string do_grouping() const override { return ""; } 414 Char do_thousands_sep() const override { return ','; } 415 }; 416 417 template <typename Char> struct special_grouping : std::numpunct<Char> { 418 protected: 419 Char do_decimal_point() const override { return '.'; } 420 std::string do_grouping() const override { return "\03\02"; } 421 Char do_thousands_sep() const override { return ','; } 422 }; 423 424 template <typename Char> struct small_grouping : std::numpunct<Char> { 425 protected: 426 Char do_decimal_point() const override { return '.'; } 427 std::string do_grouping() const override { return "\01"; } 428 Char do_thousands_sep() const override { return ','; } 429 }; 430 431 TEST(locale_test, localized_double) { 432 auto loc = std::locale(std::locale(), new numpunct<char>()); 433 EXPECT_EQ(fmt::format(loc, "{:L}", 1.23), "1?23"); 434 EXPECT_EQ(fmt::format(loc, "{:Lf}", 1.23), "1?230000"); 435 EXPECT_EQ(fmt::format(loc, "{:L}", 1234.5), "1~234?5"); 436 EXPECT_EQ(fmt::format(loc, "{:L}", 12000.0), "12~000"); 437 EXPECT_EQ(fmt::format(loc, "{:8L}", 1230.0), " 1~230"); 438 EXPECT_EQ(fmt::format(loc, "{:15.6Lf}", 0.1), " 0?100000"); 439 EXPECT_EQ(fmt::format(loc, "{:15.6Lf}", 1.0), " 1?000000"); 440 EXPECT_EQ(fmt::format(loc, "{:15.6Lf}", 1e3), " 1~000?000000"); 441 } 442 443 TEST(locale_test, format) { 444 auto loc = std::locale(std::locale(), new numpunct<char>()); 445 EXPECT_EQ("1234567", fmt::format(std::locale(), "{:L}", 1234567)); 446 EXPECT_EQ("1~234~567", fmt::format(loc, "{:L}", 1234567)); 447 EXPECT_EQ("-1~234~567", fmt::format(loc, "{:L}", -1234567)); 448 EXPECT_EQ("-256", fmt::format(loc, "{:L}", -256)); 449 auto n = 1234567; 450 EXPECT_EQ("1~234~567", fmt::vformat(loc, "{:L}", fmt::make_format_args(n))); 451 auto s = std::string(); 452 fmt::format_to(std::back_inserter(s), loc, "{:L}", 1234567); 453 EXPECT_EQ("1~234~567", s); 454 455 auto no_grouping_loc = std::locale(std::locale(), new no_grouping<char>()); 456 EXPECT_EQ("1234567", fmt::format(no_grouping_loc, "{:L}", 1234567)); 457 458 auto special_grouping_loc = 459 std::locale(std::locale(), new special_grouping<char>()); 460 EXPECT_EQ("1,23,45,678", fmt::format(special_grouping_loc, "{:L}", 12345678)); 461 EXPECT_EQ("12,345", fmt::format(special_grouping_loc, "{:L}", 12345)); 462 463 auto small_grouping_loc = 464 std::locale(std::locale(), new small_grouping<char>()); 465 EXPECT_EQ("4,2,9,4,9,6,7,2,9,5", 466 fmt::format(small_grouping_loc, "{:L}", max_value<uint32_t>())); 467 } 468 469 TEST(locale_test, format_detault_align) { 470 auto loc = std::locale({}, new special_grouping<char>()); 471 EXPECT_EQ(" 12,345", fmt::format(loc, "{:8L}", 12345)); 472 } 473 474 TEST(locale_test, format_plus) { 475 auto loc = std::locale({}, new special_grouping<char>()); 476 EXPECT_EQ("+100", fmt::format(loc, "{:+L}", 100)); 477 } 478 479 TEST(locale_test, wformat) { 480 auto loc = std::locale(std::locale(), new numpunct<wchar_t>()); 481 EXPECT_EQ(L"1234567", fmt::format(std::locale(), L"{:L}", 1234567)); 482 EXPECT_EQ(L"1~234~567", fmt::format(loc, L"{:L}", 1234567)); 483 int n = 1234567; 484 EXPECT_EQ(L"1~234~567", 485 fmt::vformat(loc, L"{:L}", fmt::make_wformat_args(n))); 486 EXPECT_EQ(L"1234567", fmt::format(std::locale("C"), L"{:L}", 1234567)); 487 488 auto no_grouping_loc = std::locale(std::locale(), new no_grouping<wchar_t>()); 489 EXPECT_EQ(L"1234567", fmt::format(no_grouping_loc, L"{:L}", 1234567)); 490 491 auto special_grouping_loc = 492 std::locale(std::locale(), new special_grouping<wchar_t>()); 493 EXPECT_EQ(L"1,23,45,678", 494 fmt::format(special_grouping_loc, L"{:L}", 12345678)); 495 496 auto small_grouping_loc = 497 std::locale(std::locale(), new small_grouping<wchar_t>()); 498 EXPECT_EQ(L"4,2,9,4,9,6,7,2,9,5", 499 fmt::format(small_grouping_loc, L"{:L}", max_value<uint32_t>())); 500 } 501 502 TEST(locale_test, int_formatter) { 503 auto loc = std::locale(std::locale(), new special_grouping<char>()); 504 auto f = fmt::formatter<int>(); 505 auto parse_ctx = fmt::format_parse_context("L"); 506 f.parse(parse_ctx); 507 auto buf = fmt::memory_buffer(); 508 fmt::basic_format_context<fmt::appender, char> format_ctx( 509 fmt::appender(buf), {}, fmt::detail::locale_ref(loc)); 510 f.format(12345, format_ctx); 511 EXPECT_EQ(fmt::to_string(buf), "12,345"); 512 } 513 514 FMT_BEGIN_NAMESPACE 515 template <class charT> struct formatter<std::complex<double>, charT> { 516 private: 517 detail::dynamic_format_specs<char> specs_; 518 519 public: 520 FMT_CONSTEXPR typename basic_format_parse_context<charT>::iterator parse( 521 basic_format_parse_context<charT>& ctx) { 522 auto end = parse_format_specs(ctx.begin(), ctx.end(), specs_, ctx, 523 detail::type::float_type); 524 detail::parse_float_type_spec(specs_, detail::error_handler()); 525 return end; 526 } 527 528 template <class FormatContext> 529 typename FormatContext::iterator format(const std::complex<double>& c, 530 FormatContext& ctx) { 531 detail::handle_dynamic_spec<detail::precision_checker>( 532 specs_.precision, specs_.precision_ref, ctx); 533 auto specs = std::string(); 534 if (specs_.precision > 0) specs = fmt::format(".{}", specs_.precision); 535 if (specs_.type == presentation_type::fixed_lower) specs += 'f'; 536 auto real = fmt::format(ctx.locale().template get<std::locale>(), 537 fmt::runtime("{:" + specs + "}"), c.real()); 538 auto imag = fmt::format(ctx.locale().template get<std::locale>(), 539 fmt::runtime("{:" + specs + "}"), c.imag()); 540 auto fill_align_width = std::string(); 541 if (specs_.width > 0) fill_align_width = fmt::format(">{}", specs_.width); 542 return format_to(ctx.out(), runtime("{:" + fill_align_width + "}"), 543 c.real() != 0 ? fmt::format("({}+{}i)", real, imag) 544 : fmt::format("{}i", imag)); 545 } 546 }; 547 FMT_END_NAMESPACE 548 549 TEST(locale_test, complex) { 550 std::string s = fmt::format("{}", std::complex<double>(1, 2)); 551 EXPECT_EQ(s, "(1+2i)"); 552 EXPECT_EQ(fmt::format("{:.2f}", std::complex<double>(1, 2)), "(1.00+2.00i)"); 553 EXPECT_EQ(fmt::format("{:8}", std::complex<double>(1, 2)), " (1+2i)"); 554 } 555 556 TEST(locale_test, chrono_weekday) { 557 auto loc = get_locale("ru_RU.UTF-8", "Russian_Russia.1251"); 558 auto loc_old = std::locale::global(loc); 559 auto mon = fmt::weekday(1); 560 EXPECT_EQ(fmt::format(L"{}", mon), L"Mon"); 561 if (loc != std::locale::classic()) { 562 // {L"\x43F\x43D", L"\x41F\x43D", L"\x43F\x43D\x434", L"\x41F\x43D\x434"} 563 // {L"пн", L"Пн", L"пнд", L"Пнд"} 564 EXPECT_THAT( 565 (std::vector<std::wstring>{L"\x43F\x43D", L"\x41F\x43D", 566 L"\x43F\x43D\x434", L"\x41F\x43D\x434"}), 567 Contains(fmt::format(loc, L"{:L}", mon))); 568 } 569 std::locale::global(loc_old); 570 } 571 572 TEST(locale_test, sign) { 573 EXPECT_EQ(fmt::format(std::locale(), L"{:L}", -50), L"-50"); 574 } 575 576 TEST(std_test_xchar, optional) { 577 # ifdef __cpp_lib_optional 578 EXPECT_EQ(fmt::format(L"{}", std::optional{L'C'}), L"optional(\'C\')"); 579 EXPECT_EQ(fmt::format(L"{}", std::optional{std::wstring{L"wide string"}}), 580 L"optional(\"wide string\")"); 581 # endif 582 } 583 584 #endif // FMT_STATIC_THOUSANDS_SEPARATOR