ranges-test.cc
1 // Formatting library for C++ - the core API 2 // 3 // Copyright (c) 2012 - present, Victor Zverovich 4 // All rights reserved. 5 // 6 // For the license information refer to format.h. 7 // 8 // Copyright (c) 2018 - present, Remotion (Igor Schulz) 9 // All Rights Reserved 10 // {fmt} support for ranges, containers and types tuple interface. 11 12 #include "fmt/ranges.h" 13 14 #include <map> 15 #include <queue> 16 #include <stack> 17 #include <string> 18 #include <utility> 19 #include <vector> 20 21 #include "gtest/gtest.h" 22 23 #if !FMT_GCC_VERSION || FMT_GCC_VERSION >= 601 24 # define FMT_RANGES_TEST_ENABLE_C_STYLE_ARRAY 25 #endif 26 27 #if !FMT_MSC_VERSION || FMT_MSC_VERSION > 1910 28 # define FMT_RANGES_TEST_ENABLE_JOIN 29 # define FMT_RANGES_TEST_ENABLE_FORMAT_STRUCT 30 #endif 31 32 #ifdef FMT_RANGES_TEST_ENABLE_C_STYLE_ARRAY 33 TEST(ranges_test, format_array) { 34 int arr[] = {1, 2, 3, 5, 7, 11}; 35 EXPECT_EQ(fmt::format("{}", arr), "[1, 2, 3, 5, 7, 11]"); 36 } 37 38 TEST(ranges_test, format_2d_array) { 39 int arr[][2] = {{1, 2}, {3, 5}, {7, 11}}; 40 EXPECT_EQ(fmt::format("{}", arr), "[[1, 2], [3, 5], [7, 11]]"); 41 } 42 43 TEST(ranges_test, format_array_of_literals) { 44 const char* arr[] = {"1234", "abcd"}; 45 EXPECT_EQ(fmt::format("{}", arr), "[\"1234\", \"abcd\"]"); 46 EXPECT_EQ(fmt::format("{:n}", arr), "\"1234\", \"abcd\""); 47 EXPECT_EQ(fmt::format("{:n:}", arr), "1234, abcd"); 48 } 49 #endif // FMT_RANGES_TEST_ENABLE_C_STYLE_ARRAY 50 51 TEST(ranges_test, format_vector) { 52 auto v = std::vector<int>{1, 2, 3, 5, 7, 11}; 53 EXPECT_EQ(fmt::format("{}", v), "[1, 2, 3, 5, 7, 11]"); 54 EXPECT_EQ(fmt::format("{::#x}", v), "[0x1, 0x2, 0x3, 0x5, 0x7, 0xb]"); 55 EXPECT_EQ(fmt::format("{:n:#x}", v), "0x1, 0x2, 0x3, 0x5, 0x7, 0xb"); 56 57 auto vc = std::vector<char>{'a', 'b', 'c'}; 58 auto vvc = std::vector<std::vector<char>>{vc, vc}; 59 EXPECT_EQ(fmt::format("{}", vc), "['a', 'b', 'c']"); 60 EXPECT_EQ(fmt::format("{}", vvc), "[['a', 'b', 'c'], ['a', 'b', 'c']]"); 61 EXPECT_EQ(fmt::format("{:n}", vvc), "['a', 'b', 'c'], ['a', 'b', 'c']"); 62 EXPECT_EQ(fmt::format("{:n:n}", vvc), "'a', 'b', 'c', 'a', 'b', 'c'"); 63 EXPECT_EQ(fmt::format("{:n:n:}", vvc), "a, b, c, a, b, c"); 64 } 65 66 TEST(ranges_test, format_vector2) { 67 auto v = std::vector<std::vector<int>>{{1, 2}, {3, 5}, {7, 11}}; 68 EXPECT_EQ(fmt::format("{}", v), "[[1, 2], [3, 5], [7, 11]]"); 69 EXPECT_EQ(fmt::format("{:::#x}", v), "[[0x1, 0x2], [0x3, 0x5], [0x7, 0xb]]"); 70 EXPECT_EQ(fmt::format("{:n:n:#x}", v), "0x1, 0x2, 0x3, 0x5, 0x7, 0xb"); 71 } 72 73 TEST(ranges_test, format_map) { 74 auto m = std::map<std::string, int>{{"one", 1}, {"two", 2}}; 75 EXPECT_EQ(fmt::format("{}", m), "{\"one\": 1, \"two\": 2}"); 76 EXPECT_EQ(fmt::format("{:n}", m), "\"one\": 1, \"two\": 2"); 77 } 78 79 TEST(ranges_test, format_set) { 80 EXPECT_EQ(fmt::format("{}", std::set<std::string>{"one", "two"}), 81 "{\"one\", \"two\"}"); 82 } 83 84 // Models std::flat_set close enough to test if no ambiguous lookup of a 85 // formatter happens due to the flat_set type matching is_set and 86 // is_container_adaptor_like 87 template <typename T> class flat_set { 88 public: 89 using key_type = T; 90 using container_type = std::vector<T>; 91 92 using iterator = typename std::vector<T>::iterator; 93 using const_iterator = typename std::vector<T>::const_iterator; 94 95 template <typename... Ts> 96 explicit flat_set(Ts&&... args) : c{std::forward<Ts>(args)...} {} 97 98 iterator begin() { return c.begin(); } 99 const_iterator begin() const { return c.begin(); } 100 101 iterator end() { return c.end(); } 102 const_iterator end() const { return c.end(); } 103 104 private: 105 std::vector<T> c; 106 }; 107 108 TEST(ranges_test, format_flat_set) { 109 EXPECT_EQ(fmt::format("{}", flat_set<std::string>{"one", "two"}), 110 "{\"one\", \"two\"}"); 111 } 112 113 namespace adl { 114 struct box { 115 int value; 116 }; 117 118 auto begin(const box& b) -> const int* { return &b.value; } 119 120 auto end(const box& b) -> const int* { return &b.value + 1; } 121 } // namespace adl 122 123 TEST(ranges_test, format_adl_begin_end) { 124 auto b = adl::box{42}; 125 EXPECT_EQ(fmt::format("{}", b), "[42]"); 126 } 127 128 TEST(ranges_test, format_pair) { 129 auto p = std::pair<int, float>(42, 1.5f); 130 EXPECT_EQ(fmt::format("{}", p), "(42, 1.5)"); 131 } 132 133 struct unformattable {}; 134 135 TEST(ranges_test, format_tuple) { 136 auto t = 137 std::tuple<int, float, std::string, char>(42, 1.5f, "this is tuple", 'i'); 138 EXPECT_EQ(fmt::format("{}", t), "(42, 1.5, \"this is tuple\", 'i')"); 139 140 EXPECT_EQ(fmt::format("{}", std::tuple<>()), "()"); 141 142 EXPECT_TRUE((fmt::is_formattable<std::tuple<>>::value)); 143 EXPECT_FALSE((fmt::is_formattable<unformattable>::value)); 144 EXPECT_FALSE((fmt::is_formattable<std::tuple<unformattable>>::value)); 145 EXPECT_FALSE((fmt::is_formattable<std::tuple<unformattable, int>>::value)); 146 EXPECT_FALSE((fmt::is_formattable<std::tuple<int, unformattable>>::value)); 147 EXPECT_FALSE( 148 (fmt::is_formattable<std::tuple<unformattable, unformattable>>::value)); 149 EXPECT_TRUE((fmt::is_formattable<std::tuple<int, float>>::value)); 150 } 151 152 struct not_default_formattable {}; 153 struct bad_format {}; 154 155 FMT_BEGIN_NAMESPACE 156 template <> struct formatter<not_default_formattable> { 157 auto parse(format_parse_context&) -> const char* { throw bad_format(); } 158 auto format(not_default_formattable, format_context& ctx) 159 -> format_context::iterator { 160 return ctx.out(); 161 } 162 }; 163 FMT_END_NAMESPACE 164 165 TEST(ranges_test, tuple_parse_calls_element_parse) { 166 auto f = fmt::formatter<std::tuple<not_default_formattable>>(); 167 auto ctx = fmt::format_parse_context(""); 168 EXPECT_THROW(f.parse(ctx), bad_format); 169 } 170 171 #ifdef FMT_RANGES_TEST_ENABLE_FORMAT_STRUCT 172 struct tuple_like { 173 int i; 174 std::string str; 175 176 template <size_t N> fmt::enable_if_t<N == 0, int> get() const noexcept { 177 return i; 178 } 179 template <size_t N> 180 fmt::enable_if_t<N == 1, fmt::string_view> get() const noexcept { 181 return str; 182 } 183 }; 184 185 template <size_t N> 186 auto get(const tuple_like& t) noexcept -> decltype(t.get<N>()) { 187 return t.get<N>(); 188 } 189 190 namespace std { 191 template <> 192 struct tuple_size<tuple_like> : std::integral_constant<size_t, 2> {}; 193 194 template <size_t N> struct tuple_element<N, tuple_like> { 195 using type = decltype(std::declval<tuple_like>().get<N>()); 196 }; 197 } // namespace std 198 199 TEST(ranges_test, format_struct) { 200 auto t = tuple_like{42, "foo"}; 201 EXPECT_EQ(fmt::format("{}", t), "(42, \"foo\")"); 202 } 203 #endif // FMT_RANGES_TEST_ENABLE_FORMAT_STRUCT 204 205 TEST(ranges_test, format_to) { 206 char buf[10]; 207 auto end = fmt::format_to(buf, "{}", std::vector<int>{1, 2, 3}); 208 *end = '\0'; 209 EXPECT_STREQ(buf, "[1, 2, 3]"); 210 } 211 212 template <typename Char> struct path_like { 213 const path_like* begin() const; 214 const path_like* end() const; 215 216 operator std::basic_string<Char>() const; 217 }; 218 219 TEST(ranges_test, disabled_range_formatting_of_path) { 220 // Range formatting of path is disabled because of infinite recursion 221 // (path element is itself a path). 222 EXPECT_EQ((fmt::range_format_kind<path_like<char>, char>::value), 223 fmt::range_format::disabled); 224 EXPECT_EQ((fmt::range_format_kind<path_like<wchar_t>, char>::value), 225 fmt::range_format::disabled); 226 } 227 228 // A range that provides non-const only begin()/end() to test fmt::join 229 // handles that. 230 // 231 // Some ranges (e.g. those produced by range-v3's views::filter()) can cache 232 // information during iteration so they only provide non-const begin()/end(). 233 template <typename T> class non_const_only_range { 234 private: 235 std::vector<T> vec; 236 237 public: 238 using const_iterator = typename ::std::vector<T>::const_iterator; 239 240 template <typename... Args> 241 explicit non_const_only_range(Args&&... args) 242 : vec(std::forward<Args>(args)...) {} 243 244 const_iterator begin() { return vec.begin(); } 245 const_iterator end() { return vec.end(); } 246 }; 247 248 template <typename T> class noncopyable_range { 249 private: 250 std::vector<T> vec; 251 252 public: 253 using iterator = typename ::std::vector<T>::iterator; 254 255 template <typename... Args> 256 explicit noncopyable_range(Args&&... args) 257 : vec(std::forward<Args>(args)...) {} 258 259 noncopyable_range(noncopyable_range const&) = delete; 260 noncopyable_range(noncopyable_range&) = delete; 261 262 iterator begin() { return vec.begin(); } 263 iterator end() { return vec.end(); } 264 }; 265 266 TEST(ranges_test, range) { 267 noncopyable_range<int> w(3u, 0); 268 EXPECT_EQ(fmt::format("{}", w), "[0, 0, 0]"); 269 EXPECT_EQ(fmt::format("{}", noncopyable_range<int>(3u, 0)), "[0, 0, 0]"); 270 271 non_const_only_range<int> x(3u, 0); 272 EXPECT_EQ(fmt::format("{}", x), "[0, 0, 0]"); 273 EXPECT_EQ(fmt::format("{}", non_const_only_range<int>(3u, 0)), "[0, 0, 0]"); 274 275 auto y = std::vector<int>(3u, 0); 276 EXPECT_EQ(fmt::format("{}", y), "[0, 0, 0]"); 277 EXPECT_EQ(fmt::format("{}", std::vector<int>(3u, 0)), "[0, 0, 0]"); 278 279 const auto z = std::vector<int>(3u, 0); 280 EXPECT_EQ(fmt::format("{}", z), "[0, 0, 0]"); 281 } 282 283 enum test_enum { foo }; 284 auto format_as(test_enum e) -> int { return e; } 285 286 TEST(ranges_test, enum_range) { 287 auto v = std::vector<test_enum>{test_enum::foo}; 288 EXPECT_EQ(fmt::format("{}", v), "[0]"); 289 } 290 291 #if !FMT_MSC_VERSION 292 TEST(ranges_test, unformattable_range) { 293 EXPECT_FALSE((fmt::has_formatter<std::vector<unformattable>, 294 fmt::format_context>::value)); 295 } 296 #endif 297 298 #ifdef FMT_RANGES_TEST_ENABLE_JOIN 299 TEST(ranges_test, join_tuple) { 300 // Value tuple args. 301 auto t1 = std::tuple<char, int, float>('a', 1, 2.0f); 302 EXPECT_EQ(fmt::format("({})", fmt::join(t1, ", ")), "(a, 1, 2)"); 303 304 // Testing lvalue tuple args. 305 int x = 4; 306 auto t2 = std::tuple<char, int&>('b', x); 307 EXPECT_EQ(fmt::format("{}", fmt::join(t2, " + ")), "b + 4"); 308 309 // Empty tuple. 310 auto t3 = std::tuple<>(); 311 EXPECT_EQ(fmt::format("{}", fmt::join(t3, "|")), ""); 312 313 // Single element tuple. 314 auto t4 = std::tuple<float>(4.0f); 315 EXPECT_EQ(fmt::format("{}", fmt::join(t4, "/")), "4"); 316 317 # if FMT_TUPLE_JOIN_SPECIFIERS 318 // Specs applied to each element. 319 auto t5 = std::tuple<int, int, long>(-3, 100, 1); 320 EXPECT_EQ(fmt::format("{:+03}", fmt::join(t5, ", ")), "-03, +100, +01"); 321 322 auto t6 = std::tuple<float, double, long double>(3, 3.14, 3.1415); 323 EXPECT_EQ(fmt::format("{:5.5f}", fmt::join(t6, ", ")), 324 "3.00000, 3.14000, 3.14150"); 325 326 // Testing lvalue tuple args. 327 int y = -1; 328 auto t7 = std::tuple<int, int&, const int&>(3, y, y); 329 EXPECT_EQ(fmt::format("{:03}", fmt::join(t7, ", ")), "003, -01, -01"); 330 # endif 331 } 332 333 TEST(ranges_test, join_initializer_list) { 334 EXPECT_EQ(fmt::format("{}", fmt::join({1, 2, 3}, ", ")), "1, 2, 3"); 335 EXPECT_EQ(fmt::format("{}", fmt::join({"fmt", "rocks", "!"}, " ")), 336 "fmt rocks !"); 337 } 338 339 struct zstring_sentinel {}; 340 341 bool operator==(const char* p, zstring_sentinel) { return *p == '\0'; } 342 bool operator!=(const char* p, zstring_sentinel) { return *p != '\0'; } 343 344 struct zstring { 345 const char* p; 346 const char* begin() const { return p; } 347 zstring_sentinel end() const { return {}; } 348 }; 349 350 # ifdef __cpp_lib_ranges 351 struct cpp20_only_range { 352 struct iterator { 353 int val = 0; 354 355 using value_type = int; 356 using difference_type = std::ptrdiff_t; 357 using iterator_concept = std::input_iterator_tag; 358 359 iterator() = default; 360 iterator(int i) : val(i) {} 361 int operator*() const { return val; } 362 iterator& operator++() { 363 ++val; 364 return *this; 365 } 366 void operator++(int) { ++*this; } 367 bool operator==(const iterator& rhs) const { return val == rhs.val; } 368 }; 369 370 int lo; 371 int hi; 372 373 iterator begin() const { return iterator(lo); } 374 iterator end() const { return iterator(hi); } 375 }; 376 377 static_assert(std::input_iterator<cpp20_only_range::iterator>); 378 # endif 379 380 TEST(ranges_test, join_sentinel) { 381 auto hello = zstring{"hello"}; 382 EXPECT_EQ(fmt::format("{}", hello), "['h', 'e', 'l', 'l', 'o']"); 383 EXPECT_EQ(fmt::format("{::}", hello), "[h, e, l, l, o]"); 384 EXPECT_EQ(fmt::format("{}", fmt::join(hello, "_")), "h_e_l_l_o"); 385 } 386 387 TEST(ranges_test, join_range) { 388 noncopyable_range<int> w(3u, 0); 389 EXPECT_EQ(fmt::format("{}", fmt::join(w, ",")), "0,0,0"); 390 EXPECT_EQ(fmt::format("{}", fmt::join(noncopyable_range<int>(3u, 0), ",")), 391 "0,0,0"); 392 393 non_const_only_range<int> x(3u, 0); 394 EXPECT_EQ(fmt::format("{}", fmt::join(x, ",")), "0,0,0"); 395 EXPECT_EQ(fmt::format("{}", fmt::join(non_const_only_range<int>(3u, 0), ",")), 396 "0,0,0"); 397 398 auto y = std::vector<int>(3u, 0); 399 EXPECT_EQ(fmt::format("{}", fmt::join(y, ",")), "0,0,0"); 400 EXPECT_EQ(fmt::format("{}", fmt::join(std::vector<int>(3u, 0), ",")), 401 "0,0,0"); 402 403 const auto z = std::vector<int>(3u, 0); 404 EXPECT_EQ(fmt::format("{}", fmt::join(z, ",")), "0,0,0"); 405 406 # ifdef __cpp_lib_ranges 407 EXPECT_EQ(fmt::format("{}", cpp20_only_range{.lo = 0, .hi = 5}), 408 "[0, 1, 2, 3, 4]"); 409 EXPECT_EQ( 410 fmt::format("{}", fmt::join(cpp20_only_range{.lo = 0, .hi = 5}, ",")), 411 "0,1,2,3,4"); 412 # endif 413 } 414 #endif // FMT_RANGES_TEST_ENABLE_JOIN 415 416 TEST(ranges_test, is_printable) { 417 using fmt::detail::is_printable; 418 EXPECT_TRUE(is_printable(0x0323)); 419 EXPECT_FALSE(is_printable(0x0378)); 420 EXPECT_FALSE(is_printable(0x110000)); 421 } 422 423 TEST(ranges_test, escape) { 424 using vec = std::vector<std::string>; 425 EXPECT_EQ(fmt::format("{}", vec{"\n\r\t\"\\"}), "[\"\\n\\r\\t\\\"\\\\\"]"); 426 EXPECT_EQ(fmt::format("{}", vec{"\x07"}), "[\"\\x07\"]"); 427 EXPECT_EQ(fmt::format("{}", vec{"\x7f"}), "[\"\\x7f\"]"); 428 EXPECT_EQ(fmt::format("{}", vec{"n\xcc\x83"}), "[\"n\xcc\x83\"]"); 429 430 if (fmt::detail::is_utf8()) { 431 EXPECT_EQ(fmt::format("{}", vec{"\xcd\xb8"}), "[\"\\u0378\"]"); 432 // Unassigned Unicode code points. 433 EXPECT_EQ(fmt::format("{}", vec{"\xf0\xaa\x9b\x9e"}), "[\"\\U0002a6de\"]"); 434 // Broken utf-8. 435 EXPECT_EQ(fmt::format("{}", vec{"\xf4\x8f\xbf\xc0"}), 436 "[\"\\xf4\\x8f\\xbf\\xc0\"]"); 437 EXPECT_EQ(fmt::format("{}", vec{"\xf0\x28"}), "[\"\\xf0(\"]"); 438 EXPECT_EQ(fmt::format("{}", vec{"\xe1\x28"}), "[\"\\xe1(\"]"); 439 EXPECT_EQ(fmt::format("{}", vec{std::string("\xf0\x28\0\0anything", 12)}), 440 "[\"\\xf0(\\x00\\x00anything\"]"); 441 442 // Correct utf-8. 443 EXPECT_EQ(fmt::format("{}", vec{"🦄"}), "[\"🦄\"]"); 444 } 445 446 EXPECT_EQ(fmt::format("{}", std::vector<std::vector<char>>{{'x'}}), 447 "[['x']]"); 448 EXPECT_EQ(fmt::format("{}", std::tuple<std::vector<char>>{{'x'}}), "(['x'])"); 449 } 450 451 template <typename R> struct fmt_ref_view { 452 R* r; 453 454 auto begin() const -> decltype(r->begin()) { return r->begin(); } 455 auto end() const -> decltype(r->end()) { return r->end(); } 456 }; 457 458 TEST(ranges_test, range_of_range_of_mixed_const) { 459 std::vector<std::vector<int>> v = {{1, 2, 3}, {4, 5}}; 460 EXPECT_EQ(fmt::format("{}", v), "[[1, 2, 3], [4, 5]]"); 461 462 fmt_ref_view<decltype(v)> r{&v}; 463 EXPECT_EQ(fmt::format("{}", r), "[[1, 2, 3], [4, 5]]"); 464 } 465 466 TEST(ranges_test, vector_char) { 467 EXPECT_EQ(fmt::format("{}", std::vector<char>{'a', 'b'}), "['a', 'b']"); 468 } 469 470 TEST(ranges_test, container_adaptor) { 471 { 472 using fmt::detail::is_container_adaptor_like; 473 using T = std::nullptr_t; 474 static_assert(is_container_adaptor_like<std::stack<T>>::value, ""); 475 static_assert(is_container_adaptor_like<std::queue<T>>::value, ""); 476 static_assert(is_container_adaptor_like<std::priority_queue<T>>::value, ""); 477 static_assert(!is_container_adaptor_like<std::vector<T>>::value, ""); 478 } 479 480 { 481 auto s = std::stack<int>(); 482 s.push(1); 483 s.push(2); 484 EXPECT_EQ(fmt::format("{}", s), "[1, 2]"); 485 EXPECT_EQ(fmt::format("{}", const_cast<const decltype(s)&>(s)), "[1, 2]"); 486 } 487 488 { 489 auto q = std::queue<int>(); 490 q.push(1); 491 q.push(2); 492 EXPECT_EQ(fmt::format("{}", q), "[1, 2]"); 493 } 494 495 { 496 auto q = std::priority_queue<int>(); 497 q.push(3); 498 q.push(1); 499 q.push(2); 500 q.push(4); 501 EXPECT_EQ(fmt::format("{}", q), "[4, 3, 2, 1]"); 502 } 503 504 { 505 auto s = std::stack<char, std::string>(); 506 s.push('a'); 507 s.push('b'); 508 // See https://cplusplus.github.io/LWG/issue3881. 509 EXPECT_EQ(fmt::format("{}", s), "['a', 'b']"); 510 } 511 512 { 513 struct my_container_adaptor { 514 using value_type = int; 515 using container_type = std::vector<value_type>; 516 void push(const value_type& v) { c.push_back(v); } 517 518 protected: 519 container_type c; 520 }; 521 522 auto m = my_container_adaptor(); 523 m.push(1); 524 m.push(2); 525 EXPECT_EQ(fmt::format("{}", m), "[1, 2]"); 526 } 527 }