1 // Formatting library for C++ - ranges tests
3 // Copyright (c) 2012 - present, Victor Zverovich and {fmt} contributors
4 // All rights reserved.
6 // For the license information refer to format.h.
8 #include "fmt/ranges.h"
20 #if FMT_CPLUSPLUS > 201703L && FMT_HAS_INCLUDE(<ranges>)
24 #include "fmt/format.h"
25 #include "gtest/gtest.h"
27 #if !FMT_GCC_VERSION || FMT_GCC_VERSION >= 601
28 # define FMT_RANGES_TEST_ENABLE_C_STYLE_ARRAY
31 #if !FMT_MSC_VERSION || FMT_MSC_VERSION > 1910
32 # define FMT_RANGES_TEST_ENABLE_JOIN
33 # define FMT_RANGES_TEST_ENABLE_FORMAT_STRUCT
36 #ifdef FMT_RANGES_TEST_ENABLE_C_STYLE_ARRAY
37 TEST(ranges_test
, format_array
) {
38 int arr
[] = {1, 2, 3, 5, 7, 11};
39 EXPECT_EQ(fmt::format("{}", arr
), "[1, 2, 3, 5, 7, 11]");
42 TEST(ranges_test
, format_2d_array
) {
43 int arr
[][2] = {{1, 2}, {3, 5}, {7, 11}};
44 EXPECT_EQ(fmt::format("{}", arr
), "[[1, 2], [3, 5], [7, 11]]");
47 TEST(ranges_test
, format_array_of_literals
) {
48 const char* arr
[] = {"1234", "abcd"};
49 EXPECT_EQ(fmt::format("{}", arr
), "[\"1234\", \"abcd\"]");
50 EXPECT_EQ(fmt::format("{:n}", arr
), "\"1234\", \"abcd\"");
51 EXPECT_EQ(fmt::format("{:n:}", arr
), "1234, abcd");
53 #endif // FMT_RANGES_TEST_ENABLE_C_STYLE_ARRAY
55 TEST(ranges_test
, format_vector
) {
56 auto v
= std::vector
<int>{1, 2, 3, 5, 7, 11};
57 EXPECT_EQ(fmt::format("{}", v
), "[1, 2, 3, 5, 7, 11]");
58 EXPECT_EQ(fmt::format("{::#x}", v
), "[0x1, 0x2, 0x3, 0x5, 0x7, 0xb]");
59 EXPECT_EQ(fmt::format("{:n:#x}", v
), "0x1, 0x2, 0x3, 0x5, 0x7, 0xb");
61 auto vc
= std::vector
<char>{'a', 'b', 'c'};
62 auto vec
= std::vector
<char>{'a', '\n', '\t'};
63 auto vvc
= std::vector
<std::vector
<char>>{vc
, vc
};
64 EXPECT_EQ(fmt::format("{}", vc
), "['a', 'b', 'c']");
65 EXPECT_EQ(fmt::format("{:s}", vc
), "\"abc\"");
66 EXPECT_EQ(fmt::format("{:?s}", vec
), "\"a\\n\\t\"");
67 EXPECT_EQ(fmt::format("{:s}", vec
), "\"a\n\t\"");
68 EXPECT_EQ(fmt::format("{::s}", vvc
), "[\"abc\", \"abc\"]");
69 EXPECT_EQ(fmt::format("{}", vvc
), "[['a', 'b', 'c'], ['a', 'b', 'c']]");
70 EXPECT_EQ(fmt::format("{:n}", vvc
), "['a', 'b', 'c'], ['a', 'b', 'c']");
71 EXPECT_EQ(fmt::format("{:n:n}", vvc
), "'a', 'b', 'c', 'a', 'b', 'c'");
72 EXPECT_EQ(fmt::format("{:n:n:}", vvc
), "a, b, c, a, b, c");
75 TEST(ranges_test
, format_nested_vector
) {
76 auto v
= std::vector
<std::vector
<int>>{{1, 2}, {3, 5}, {7, 11}};
77 EXPECT_EQ(fmt::format("{}", v
), "[[1, 2], [3, 5], [7, 11]]");
78 EXPECT_EQ(fmt::format("{:::#x}", v
), "[[0x1, 0x2], [0x3, 0x5], [0x7, 0xb]]");
79 EXPECT_EQ(fmt::format("{:n:n:#x}", v
), "0x1, 0x2, 0x3, 0x5, 0x7, 0xb");
82 TEST(ranges_test
, to_string_vector
) {
83 auto v
= std::vector
<std::string
>{"a", "b", "c"};
84 EXPECT_EQ(fmt::to_string(v
), "[\"a\", \"b\", \"c\"]");
87 TEST(ranges_test
, format_map
) {
88 auto m
= std::map
<std::string
, int>{{"one", 1}, {"two", 2}};
89 EXPECT_EQ(fmt::format("{}", m
), "{\"one\": 1, \"two\": 2}");
90 EXPECT_EQ(fmt::format("{:n}", m
), "\"one\": 1, \"two\": 2");
93 struct test_map_value
{};
96 template <> struct formatter
<test_map_value
> : formatter
<string_view
> {
97 auto format(test_map_value
, format_context
& ctx
) const
98 -> format_context::iterator
{
99 return formatter
<string_view
>::format("foo", ctx
);
103 template <typename K
>
104 struct formatter
<std::pair
<K
, test_map_value
>> : formatter
<string_view
> {
105 auto format(std::pair
<K
, test_map_value
>, format_context
& ctx
) const
106 -> format_context::iterator
{
111 template <typename K
>
112 struct is_tuple_formattable
<std::pair
<K
, test_map_value
>, char>
113 : std::false_type
{};
117 TEST(ranges_test
, format_map_custom_pair
) {
118 EXPECT_EQ(fmt::format("{}", std::map
<int, test_map_value
>{{42, {}}}),
122 TEST(ranges_test
, format_set
) {
123 EXPECT_EQ(fmt::format("{}", std::set
<std::string
>{"one", "two"}),
124 "{\"one\", \"two\"}");
127 // Models std::flat_set close enough to test if no ambiguous lookup of a
128 // formatter happens due to the flat_set type matching is_set and
129 // is_container_adaptor_like.
130 template <typename T
> class flat_set
{
133 using container_type
= std::vector
<T
>;
135 using iterator
= typename
std::vector
<T
>::iterator
;
136 using const_iterator
= typename
std::vector
<T
>::const_iterator
;
138 template <typename
... Ts
>
139 explicit flat_set(Ts
&&... args
) : c
{std::forward
<Ts
>(args
)...} {}
141 auto begin() -> iterator
{ return c
.begin(); }
142 auto end() -> iterator
{ return c
.end(); }
144 auto begin() const -> const_iterator
{ return c
.begin(); }
145 auto end() const -> const_iterator
{ return c
.end(); }
151 TEST(ranges_test
, format_flat_set
) {
152 EXPECT_EQ(fmt::format("{}", flat_set
<std::string
>{"one", "two"}),
153 "{\"one\", \"two\"}");
161 auto begin(const box
& b
) -> const int* { return &b
.value
; }
162 auto end(const box
& b
) -> const int* { return &b
.value
+ 1; }
165 TEST(ranges_test
, format_adl_begin_end
) {
166 auto b
= adl::box
{42};
167 EXPECT_EQ(fmt::format("{}", b
), "[42]");
170 TEST(ranges_test
, format_pair
) {
171 auto p
= std::pair
<int, float>(42, 1.5f
);
172 EXPECT_EQ(fmt::format("{}", p
), "(42, 1.5)");
175 struct unformattable
{};
177 TEST(ranges_test
, format_tuple
) {
179 std::tuple
<int, float, std::string
, char>(42, 1.5f
, "this is tuple", 'i');
180 EXPECT_EQ(fmt::format("{}", t
), "(42, 1.5, \"this is tuple\", 'i')");
182 EXPECT_EQ(fmt::format("{}", std::tuple
<>()), "()");
184 EXPECT_TRUE((fmt::is_formattable
<std::tuple
<>>::value
));
185 EXPECT_FALSE((fmt::is_formattable
<unformattable
>::value
));
186 EXPECT_FALSE((fmt::is_formattable
<std::tuple
<unformattable
>>::value
));
187 EXPECT_FALSE((fmt::is_formattable
<std::tuple
<unformattable
, int>>::value
));
188 EXPECT_FALSE((fmt::is_formattable
<std::tuple
<int, unformattable
>>::value
));
190 (fmt::is_formattable
<std::tuple
<unformattable
, unformattable
>>::value
));
191 EXPECT_TRUE((fmt::is_formattable
<std::tuple
<int, float>>::value
));
194 struct not_default_formattable
{};
195 struct bad_format
{};
198 template <> struct formatter
<not_default_formattable
> {
199 auto parse(format_parse_context
&) -> const char* { throw bad_format(); }
200 auto format(not_default_formattable
, format_context
& ctx
)
201 -> format_context::iterator
{
207 TEST(ranges_test
, tuple_parse_calls_element_parse
) {
208 auto f
= fmt::formatter
<std::tuple
<not_default_formattable
>>();
209 auto ctx
= fmt::format_parse_context("");
210 EXPECT_THROW(f
.parse(ctx
), bad_format
);
213 #ifdef FMT_RANGES_TEST_ENABLE_FORMAT_STRUCT
219 auto get() const noexcept
-> fmt::enable_if_t
<N
== 0, int> {
223 auto get() const noexcept
-> fmt::enable_if_t
<N
== 1, fmt::string_view
> {
229 auto get(const tuple_like
& t
) noexcept
-> decltype(t
.get
<N
>()) {
235 struct tuple_size
<tuple_like
> : std::integral_constant
<size_t, 2> {};
237 template <size_t N
> struct tuple_element
<N
, tuple_like
> {
238 using type
= decltype(std::declval
<tuple_like
>().get
<N
>());
242 TEST(ranges_test
, format_struct
) {
243 auto t
= tuple_like
{42, "foo"};
244 EXPECT_EQ(fmt::format("{}", t
), "(42, \"foo\")");
246 #endif // FMT_RANGES_TEST_ENABLE_FORMAT_STRUCT
248 TEST(ranges_test
, format_to
) {
250 auto end
= fmt::format_to(buf
, "{}", std::vector
<int>{1, 2, 3});
252 EXPECT_STREQ(buf
, "[1, 2, 3]");
255 template <typename Char
> struct path_like
{
256 auto begin() const -> const path_like
*;
257 auto end() const -> const path_like
*;
259 operator std::basic_string
<Char
>() const;
262 TEST(ranges_test
, disabled_range_formatting_of_path
) {
263 // Range formatting of path is disabled because of infinite recursion
264 // (path element is itself a path).
265 EXPECT_EQ((fmt::range_format_kind
<path_like
<char>, char>::value
),
266 fmt::range_format::disabled
);
267 EXPECT_EQ((fmt::range_format_kind
<path_like
<wchar_t>, char>::value
),
268 fmt::range_format::disabled
);
271 struct vector_string
: std::vector
<char> {
272 using base
= std::vector
<char>;
275 struct vector_debug_string
: std::vector
<char> {
276 using base
= std::vector
<char>;
281 struct range_format_kind
<vector_string
, char>
282 : std::integral_constant
<range_format
, range_format::string
> {};
284 struct range_format_kind
<vector_debug_string
, char>
285 : std::integral_constant
<range_format
, range_format::debug_string
> {};
288 TEST(ranges_test
, range_format_string
) {
289 const vector_string v
{'f', 'o', 'o'};
290 EXPECT_EQ(fmt::format("{}", v
), "foo");
293 TEST(ranges_test
, range_format_debug_string
) {
294 const vector_debug_string v
{'f', 'o', 'o'};
295 EXPECT_EQ(fmt::format("{}", v
), "\"foo\"");
298 // A range that provides non-const only begin()/end() to test fmt::join
301 // Some ranges (e.g. those produced by range-v3's views::filter()) can cache
302 // information during iteration so they only provide non-const begin()/end().
303 template <typename T
> class non_const_only_range
{
308 using const_iterator
= typename ::std::vector
<T
>::const_iterator
;
310 template <typename
... Args
>
311 explicit non_const_only_range(Args
&&... args
)
312 : vec(std::forward
<Args
>(args
)...) {}
314 auto begin() -> const_iterator
{ return vec
.begin(); }
315 auto end() -> const_iterator
{ return vec
.end(); }
318 template <typename T
> class noncopyable_range
{
323 using iterator
= typename ::std::vector
<T
>::iterator
;
325 template <typename
... Args
>
326 explicit noncopyable_range(Args
&&... args
)
327 : vec(std::forward
<Args
>(args
)...) {}
329 noncopyable_range(noncopyable_range
const&) = delete;
330 noncopyable_range(noncopyable_range
&) = delete;
332 auto begin() -> iterator
{ return vec
.begin(); }
333 auto end() -> iterator
{ return vec
.end(); }
336 TEST(ranges_test
, range
) {
337 auto&& w
= noncopyable_range
<int>(3u, 0);
338 EXPECT_EQ(fmt::format("{}", w
), "[0, 0, 0]");
339 EXPECT_EQ(fmt::format("{}", noncopyable_range
<int>(3u, 0)), "[0, 0, 0]");
341 auto x
= non_const_only_range
<int>(3u, 0);
342 EXPECT_EQ(fmt::format("{}", x
), "[0, 0, 0]");
343 EXPECT_EQ(fmt::format("{}", non_const_only_range
<int>(3u, 0)), "[0, 0, 0]");
345 auto y
= std::vector
<int>(3u, 0);
346 EXPECT_EQ(fmt::format("{}", y
), "[0, 0, 0]");
347 EXPECT_EQ(fmt::format("{}", std::vector
<int>(3u, 0)), "[0, 0, 0]");
349 const auto z
= std::vector
<int>(3u, 0);
350 EXPECT_EQ(fmt::format("{}", z
), "[0, 0, 0]");
353 enum test_enum
{ foo
, bar
};
354 auto format_as(test_enum e
) -> int { return e
; }
356 TEST(ranges_test
, enum_range
) {
357 auto v
= std::vector
<test_enum
>{test_enum::foo
};
358 EXPECT_EQ(fmt::format("{}", v
), "[0]");
362 TEST(ranges_test
, unformattable_range
) {
363 EXPECT_FALSE((fmt::has_formatter
<std::vector
<unformattable
>,
364 fmt::format_context
>::value
));
368 TEST(ranges_test
, join
) {
370 int v1
[3] = {1, 2, 3};
371 auto v2
= std::vector
<float>();
374 void* v3
[2] = {&v1
[0], &v1
[1]};
376 EXPECT_EQ(fmt::format("({})", join(v1
, v1
+ 3, ", ")), "(1, 2, 3)");
377 EXPECT_EQ(fmt::format("({})", join(v1
, v1
+ 1, ", ")), "(1)");
378 EXPECT_EQ(fmt::format("({})", join(v1
, v1
, ", ")), "()");
379 EXPECT_EQ(fmt::format("({:03})", join(v1
, v1
+ 3, ", ")), "(001, 002, 003)");
380 EXPECT_EQ("(+01.20, +03.40)",
381 fmt::format("({:+06.2f})", join(v2
.begin(), v2
.end(), ", ")));
383 EXPECT_EQ(fmt::format("{0:{1}}", join(v1
, v1
+ 3, ", "), 1), "1, 2, 3");
385 EXPECT_EQ(fmt::format("{}, {}", v3
[0], v3
[1]),
386 fmt::format("{}", join(v3
, v3
+ 2, ", ")));
388 EXPECT_EQ(fmt::format("({})", join(v1
, ", ")), "(1, 2, 3)");
389 EXPECT_EQ(fmt::format("({:+06.2f})", join(v2
, ", ")), "(+01.20, +03.40)");
391 auto v4
= std::vector
<test_enum
>{foo
, bar
, foo
};
392 EXPECT_EQ(fmt::format("{}", join(v4
, " ")), "0 1 0");
395 #ifdef __cpp_lib_byte
396 TEST(ranges_test
, join_bytes
) {
397 auto v
= std::vector
<std::byte
>{std::byte(1), std::byte(2), std::byte(3)};
398 EXPECT_EQ(fmt::format("{}", fmt::join(v
, ", ")), "1, 2, 3");
402 #ifdef FMT_RANGES_TEST_ENABLE_JOIN
403 TEST(ranges_test
, join_tuple
) {
405 auto t1
= std::tuple
<char, int, float>('a', 1, 2.0f
);
406 EXPECT_EQ(fmt::format("({})", fmt::join(t1
, ", ")), "(a, 1, 2)");
408 // Testing lvalue tuple args.
410 auto t2
= std::tuple
<char, int&>('b', x
);
411 EXPECT_EQ(fmt::format("{}", fmt::join(t2
, " + ")), "b + 4");
414 auto t3
= std::tuple
<>();
415 EXPECT_EQ(fmt::format("{}", fmt::join(t3
, "|")), "");
417 // Single element tuple.
418 auto t4
= std::tuple
<float>(4.0f
);
419 EXPECT_EQ(fmt::format("{}", fmt::join(t4
, "/")), "4");
421 # if FMT_TUPLE_JOIN_SPECIFIERS
422 // Specs applied to each element.
423 auto t5
= std::tuple
<int, int, long>(-3, 100, 1);
424 EXPECT_EQ(fmt::format("{:+03}", fmt::join(t5
, ", ")), "-03, +100, +01");
426 auto t6
= std::tuple
<float, double, long double>(3, 3.14, 3.1415);
427 EXPECT_EQ(fmt::format("{:5.5f}", fmt::join(t6
, ", ")),
428 "3.00000, 3.14000, 3.14150");
430 // Testing lvalue tuple args.
432 auto t7
= std::tuple
<int, int&, const int&>(3, y
, y
);
433 EXPECT_EQ(fmt::format("{:03}", fmt::join(t7
, ", ")), "003, -01, -01");
437 TEST(ranges_test
, join_initializer_list
) {
438 EXPECT_EQ(fmt::format("{}", fmt::join({1, 2, 3}, ", ")), "1, 2, 3");
439 EXPECT_EQ(fmt::format("{}", fmt::join({"fmt", "rocks", "!"}, " ")),
443 struct zstring_sentinel
{};
445 bool operator==(const char* p
, zstring_sentinel
) { return *p
== '\0'; }
446 bool operator!=(const char* p
, zstring_sentinel
) { return *p
!= '\0'; }
450 auto begin() const -> const char* { return p
; }
451 auto end() const -> zstring_sentinel
{ return {}; }
454 # ifdef __cpp_lib_ranges
455 struct cpp20_only_range
{
459 using value_type
= int;
460 using difference_type
= std::ptrdiff_t;
461 using iterator_concept
= std::input_iterator_tag
;
463 iterator() = default;
464 iterator(int i
) : val(i
) {}
465 auto operator*() const -> int { return val
; }
466 auto operator++() -> iterator
& {
470 void operator++(int) { ++*this; }
471 auto operator==(const iterator
& rhs
) const -> bool {
472 return val
== rhs
.val
;
479 auto begin() const -> iterator
{ return iterator(lo
); }
480 auto end() const -> iterator
{ return iterator(hi
); }
483 static_assert(std::input_iterator
<cpp20_only_range::iterator
>);
486 TEST(ranges_test
, join_sentinel
) {
487 auto hello
= zstring
{"hello"};
488 EXPECT_EQ(fmt::format("{}", hello
), "['h', 'e', 'l', 'l', 'o']");
489 EXPECT_EQ(fmt::format("{::}", hello
), "[h, e, l, l, o]");
490 EXPECT_EQ(fmt::format("{}", fmt::join(hello
, "_")), "h_e_l_l_o");
493 TEST(ranges_test
, join_range
) {
494 auto&& w
= noncopyable_range
<int>(3u, 0);
495 EXPECT_EQ(fmt::format("{}", fmt::join(w
, ",")), "0,0,0");
496 EXPECT_EQ(fmt::format("{}", fmt::join(noncopyable_range
<int>(3u, 0), ",")),
499 auto x
= non_const_only_range
<int>(3u, 0);
500 EXPECT_EQ(fmt::format("{}", fmt::join(x
, ",")), "0,0,0");
501 EXPECT_EQ(fmt::format("{}", fmt::join(non_const_only_range
<int>(3u, 0), ",")),
504 auto y
= std::vector
<int>(3u, 0);
505 EXPECT_EQ(fmt::format("{}", fmt::join(y
, ",")), "0,0,0");
506 EXPECT_EQ(fmt::format("{}", fmt::join(std::vector
<int>(3u, 0), ",")),
509 const auto z
= std::vector
<int>(3u, 0);
510 EXPECT_EQ(fmt::format("{}", fmt::join(z
, ",")), "0,0,0");
512 # ifdef __cpp_lib_ranges
513 EXPECT_EQ(fmt::format("{}", cpp20_only_range
{.lo
= 0, .hi
= 5}),
516 fmt::format("{}", fmt::join(cpp20_only_range
{.lo
= 0, .hi
= 5}, ",")),
526 auto begin(const vec
& v
) -> const int* { return v
.n
; }
527 auto end(const vec
& v
) -> const int* { return v
.n
+ 2; }
530 TEST(ranges_test
, format_join_adl_begin_end
) {
531 EXPECT_EQ(fmt::format("{}", fmt::join(adl::vec(), "/")), "42/43");
534 #endif // FMT_RANGES_TEST_ENABLE_JOIN
536 #if defined(__cpp_lib_ranges) && __cpp_lib_ranges >= 202207L
537 TEST(ranges_test
, nested_ranges
) {
538 auto l
= std::list
{1, 2, 3};
539 auto r
= std::views::iota(0, 3) | std::views::transform([&l
](auto i
) {
540 return std::views::take(std::ranges::subrange(l
), i
);
542 std::views::transform(std::views::reverse
);
543 EXPECT_EQ(fmt::format("{}", r
), "[[], [1], [2, 1]]");
547 TEST(ranges_test
, is_printable
) {
548 using fmt::detail::is_printable
;
549 EXPECT_TRUE(is_printable(0x0323));
550 EXPECT_FALSE(is_printable(0x0378));
551 EXPECT_FALSE(is_printable(0x110000));
554 TEST(ranges_test
, escape
) {
555 using vec
= std::vector
<std::string
>;
556 EXPECT_EQ(fmt::format("{}", vec
{"\n\r\t\"\\"}), "[\"\\n\\r\\t\\\"\\\\\"]");
557 EXPECT_EQ(fmt::format("{}", vec
{"\x07"}), "[\"\\x07\"]");
558 EXPECT_EQ(fmt::format("{}", vec
{"\x7f"}), "[\"\\x7f\"]");
559 EXPECT_EQ(fmt::format("{}", vec
{"n\xcc\x83"}), "[\"n\xcc\x83\"]");
561 if (fmt::detail::use_utf8()) {
562 EXPECT_EQ(fmt::format("{}", vec
{"\xcd\xb8"}), "[\"\\u0378\"]");
563 // Unassigned Unicode code points.
564 EXPECT_EQ(fmt::format("{}", vec
{"\xf0\xaa\x9b\x9e"}), "[\"\\U0002a6de\"]");
566 EXPECT_EQ(fmt::format("{}", vec
{"\xf4\x8f\xbf\xc0"}),
567 "[\"\\xf4\\x8f\\xbf\\xc0\"]");
568 EXPECT_EQ(fmt::format("{}", vec
{"\xf0\x28"}), "[\"\\xf0(\"]");
569 EXPECT_EQ(fmt::format("{}", vec
{"\xe1\x28"}), "[\"\\xe1(\"]");
570 EXPECT_EQ(fmt::format("{}", vec
{std::string("\xf0\x28\0\0anything", 12)}),
571 "[\"\\xf0(\\x00\\x00anything\"]");
574 EXPECT_EQ(fmt::format("{}", vec
{"🦄"}), "[\"🦄\"]");
577 EXPECT_EQ(fmt::format("{}", std::vector
<std::vector
<char>>{{'x'}}),
579 EXPECT_EQ(fmt::format("{}", std::tuple
<std::vector
<char>>{{'x'}}), "(['x'])");
582 template <typename R
> struct fmt_ref_view
{
585 auto begin() const -> decltype(r
->begin()) { return r
->begin(); }
586 auto end() const -> decltype(r
->end()) { return r
->end(); }
589 TEST(ranges_test
, range_of_range_of_mixed_const
) {
590 auto v
= std::vector
<std::vector
<int>>{{1, 2, 3}, {4, 5}};
591 EXPECT_EQ(fmt::format("{}", v
), "[[1, 2, 3], [4, 5]]");
593 auto r
= fmt_ref_view
<decltype(v
)>{&v
};
594 EXPECT_EQ(fmt::format("{}", r
), "[[1, 2, 3], [4, 5]]");
597 TEST(ranges_test
, vector_char
) {
598 EXPECT_EQ(fmt::format("{}", std::vector
<char>{'a', 'b'}), "['a', 'b']");
601 TEST(ranges_test
, container_adaptor
) {
603 using fmt::detail::is_container_adaptor_like
;
604 using T
= std::nullptr_t
;
605 static_assert(is_container_adaptor_like
<std::stack
<T
>>::value
, "");
606 static_assert(is_container_adaptor_like
<std::queue
<T
>>::value
, "");
607 static_assert(is_container_adaptor_like
<std::priority_queue
<T
>>::value
, "");
608 static_assert(!is_container_adaptor_like
<std::vector
<T
>>::value
, "");
612 auto s
= std::stack
<int>();
615 EXPECT_EQ(fmt::format("{}", s
), "[1, 2]");
616 EXPECT_EQ(fmt::format("{}", const_cast<const decltype(s
)&>(s
)), "[1, 2]");
620 auto q
= std::queue
<int>();
623 EXPECT_EQ(fmt::format("{}", q
), "[1, 2]");
627 auto q
= std::priority_queue
<int>();
632 EXPECT_EQ(fmt::format("{}", q
), "[4, 3, 2, 1]");
636 auto s
= std::stack
<char, std::string
>();
639 // See https://cplusplus.github.io/LWG/issue3881.
640 EXPECT_EQ(fmt::format("{}", s
), "['a', 'b']");
644 struct my_container_adaptor
{
645 using value_type
= int;
646 using container_type
= std::vector
<value_type
>;
647 void push(const value_type
& v
) { c
.push_back(v
); }
653 auto m
= my_container_adaptor();
656 EXPECT_EQ(fmt::format("{}", m
), "[1, 2]");
665 auto format_as(const tieable
& t
) -> std::tuple
<int, double> {
666 return std::tie(t
.a
, t
.b
);
669 TEST(ranges_test
, format_as_tie
) {
670 EXPECT_EQ(fmt::format("{}", tieable()), "(3, 0.42)");
673 struct lvalue_qualified_begin_end
{
674 int arr
[5] = {1, 2, 3, 4, 5};
676 auto begin() & -> const int* { return arr
; }
677 auto end() & -> const int* { return arr
+ 5; }
680 TEST(ranges_test
, lvalue_qualified_begin_end
) {
681 EXPECT_EQ(fmt::format("{}", lvalue_qualified_begin_end
{}), "[1, 2, 3, 4, 5]");
684 #if !defined(__cpp_lib_ranges) || __cpp_lib_ranges <= 202106L
685 # define ENABLE_STD_VIEWS_TESTS 0
686 #elif FMT_CLANG_VERSION
687 # if FMT_CLANG_VERSION > 1500
688 # define ENABLE_STD_VIEWS_TESTS 1
690 # define ENABLE_STD_VIEWS_TESTS 0
693 # define ENABLE_STD_VIEWS_TESTS 1
696 #if ENABLE_STD_VIEWS_TESTS
697 TEST(ranges_test
, input_range_join
) {
698 auto iss
= std::istringstream("1 2 3 4 5");
699 auto view
= std::views::istream
<std::string
>(iss
);
700 EXPECT_EQ("1, 2, 3, 4, 5",
701 fmt::format("{}", fmt::join(view
.begin(), view
.end(), ", ")));
704 TEST(ranges_test
, input_range_join_overload
) {
705 auto iss
= std::istringstream("1 2 3 4 5");
708 fmt::format("{}", fmt::join(std::views::istream
<std::string
>(iss
), ".")));
711 namespace views_filter_view_test
{
713 static constexpr auto codecs
= std::array
{0, 1, 2, 3};
717 auto format_as(codec_mask mask
) {
718 // Careful not to capture param by reference here, it will dangle.
719 return codec_mask::codecs
|
720 std::views::filter([mask
](auto c
) { return c
!= mask
.except
; });
722 } // namespace views_filter_view_test
724 TEST(ranges_test
, format_as_with_ranges_mutable_begin_end
) {
725 using namespace views_filter_view_test
;
727 auto make_filter_view
= []() {
728 return codec_mask::codecs
|
729 std::views::filter([](auto c
) { return c
!= 2; });
731 auto r
= make_filter_view();
732 EXPECT_EQ("[0, 1, 3]", fmt::format("{}", r
));
733 EXPECT_EQ("[0, 1, 3]", fmt::format("{}", make_filter_view()));
737 auto mask
= codec_mask
{2};
738 const auto const_mask
= codec_mask
{2};
740 EXPECT_EQ("[0, 1, 3]", fmt::format("{}", mask
));
741 EXPECT_EQ("[0, 1, 3]", fmt::format("{}", const_mask
));
742 EXPECT_EQ("[0, 1, 3]", fmt::format("{}", codec_mask
{2}));
748 TEST(ranges_test
, std_istream_iterator_join
) {
749 auto&& iss
= std::istringstream("1 2 3 4 5");
750 auto first
= std::istream_iterator
<int>(iss
);
751 auto last
= std::istream_iterator
<int>();
752 EXPECT_EQ("1, 2, 3, 4, 5", fmt::format("{}", fmt::join(first
, last
, ", ")));
755 // Mirrors C++20 std::ranges::basic_istream_view::iterator.
756 struct noncopyable_istream_iterator
: std::istream_iterator
<int> {
757 using base
= std::istream_iterator
<int>;
758 explicit noncopyable_istream_iterator(std::istringstream
& iss
) : base
{iss
} {}
759 noncopyable_istream_iterator(const noncopyable_istream_iterator
&) = delete;
760 noncopyable_istream_iterator(noncopyable_istream_iterator
&&) = default;
762 static_assert(!std::is_copy_constructible
<noncopyable_istream_iterator
>::value
,
765 TEST(ranges_test
, movable_only_istream_iter_join
) {
766 auto&& iss
= std::istringstream("1 2 3 4 5");
767 auto first
= noncopyable_istream_iterator(iss
);
768 auto last
= std::istream_iterator
<int>();
769 EXPECT_EQ("1, 2, 3, 4, 5",
770 fmt::format("{}", fmt::join(std::move(first
), last
, ", ")));
773 struct movable_iter_range
{
774 std::istringstream iss
{"1 2 3 4 5"};
775 noncopyable_istream_iterator
begin() {
776 return noncopyable_istream_iterator
{iss
};
778 std::istream_iterator
<int> end() { return {}; }
781 TEST(ranges_test
, movable_only_istream_iter_join2
) {
782 EXPECT_EQ("[1, 2, 3, 4, 5]", fmt::format("{}", movable_iter_range
{}));
786 void begin() const {}
789 static_assert(!fmt::is_formattable
<not_range
>{}, "");