1 // Formatting library for C++ - the core API
3 // Copyright (c) 2012 - present, Victor Zverovich
4 // All rights reserved.
6 // For the license information refer to format.h.
8 // Copyright (c) 2018 - present, Remotion (Igor Schulz)
10 // {fmt} support for ranges, containers and types tuple interface.
12 #include "fmt/ranges.h"
18 #include "gtest/gtest.h"
20 #if !FMT_GCC_VERSION || FMT_GCC_VERSION >= 601
21 # define FMT_RANGES_TEST_ENABLE_C_STYLE_ARRAY
24 #if !FMT_MSC_VERSION || FMT_MSC_VERSION > 1910
25 # define FMT_RANGES_TEST_ENABLE_JOIN
26 # define FMT_RANGES_TEST_ENABLE_FORMAT_STRUCT
29 #ifdef FMT_RANGES_TEST_ENABLE_C_STYLE_ARRAY
30 TEST(ranges_test
, format_array
) {
31 int arr
[] = {1, 2, 3, 5, 7, 11};
32 EXPECT_EQ(fmt::format("{}", arr
), "[1, 2, 3, 5, 7, 11]");
35 TEST(ranges_test
, format_2d_array
) {
36 int arr
[][2] = {{1, 2}, {3, 5}, {7, 11}};
37 EXPECT_EQ(fmt::format("{}", arr
), "[[1, 2], [3, 5], [7, 11]]");
40 TEST(ranges_test
, format_array_of_literals
) {
41 const char* arr
[] = {"1234", "abcd"};
42 EXPECT_EQ(fmt::format("{}", arr
), "[\"1234\", \"abcd\"]");
43 EXPECT_EQ(fmt::format("{:n}", arr
), "\"1234\", \"abcd\"");
44 EXPECT_EQ(fmt::format("{:n:}", arr
), "1234, abcd");
46 #endif // FMT_RANGES_TEST_ENABLE_C_STYLE_ARRAY
48 TEST(ranges_test
, format_vector
) {
49 auto v
= std::vector
<int>{1, 2, 3, 5, 7, 11};
50 EXPECT_EQ(fmt::format("{}", v
), "[1, 2, 3, 5, 7, 11]");
51 EXPECT_EQ(fmt::format("{::#x}", v
), "[0x1, 0x2, 0x3, 0x5, 0x7, 0xb]");
52 EXPECT_EQ(fmt::format("{:n:#x}", v
), "0x1, 0x2, 0x3, 0x5, 0x7, 0xb");
55 TEST(ranges_test
, format_vector2
) {
56 auto v
= std::vector
<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:n:#x}", v
), "0x1, 0x2, 0x3, 0x5, 0x7, 0xb");
62 TEST(ranges_test
, format_map
) {
63 auto m
= std::map
<std::string
, int>{{"one", 1}, {"two", 2}};
64 EXPECT_EQ(fmt::format("{}", m
), "{\"one\": 1, \"two\": 2}");
65 EXPECT_EQ(fmt::format("{:n}", m
), "\"one\": 1, \"two\": 2");
68 TEST(ranges_test
, format_set
) {
69 EXPECT_EQ(fmt::format("{}", std::set
<std::string
>{"one", "two"}),
70 "{\"one\", \"two\"}");
78 auto begin(const box
& b
) -> const int* { return &b
.value
; }
80 auto end(const box
& b
) -> const int* { return &b
.value
+ 1; }
83 TEST(ranges_test
, format_adl_begin_end
) {
84 auto b
= adl::box
{42};
85 EXPECT_EQ(fmt::format("{}", b
), "[42]");
88 TEST(ranges_test
, format_pair
) {
89 auto p
= std::pair
<int, float>(42, 1.5f
);
90 EXPECT_EQ(fmt::format("{}", p
), "(42, 1.5)");
93 struct unformattable
{};
95 TEST(ranges_test
, format_tuple
) {
97 std::tuple
<int, float, std::string
, char>(42, 1.5f
, "this is tuple", 'i');
98 EXPECT_EQ(fmt::format("{}", t
), "(42, 1.5, \"this is tuple\", 'i')");
99 EXPECT_EQ(fmt::format("{}", std::tuple
<>()), "()");
101 EXPECT_TRUE((fmt::is_formattable
<std::tuple
<>>::value
));
102 EXPECT_FALSE((fmt::is_formattable
<unformattable
>::value
));
103 EXPECT_FALSE((fmt::is_formattable
<std::tuple
<unformattable
>>::value
));
104 EXPECT_FALSE((fmt::is_formattable
<std::tuple
<unformattable
, int>>::value
));
105 EXPECT_FALSE((fmt::is_formattable
<std::tuple
<int, unformattable
>>::value
));
107 (fmt::is_formattable
<std::tuple
<unformattable
, unformattable
>>::value
));
108 EXPECT_TRUE((fmt::is_formattable
<std::tuple
<int, float>>::value
));
111 #ifdef FMT_RANGES_TEST_ENABLE_FORMAT_STRUCT
116 template <size_t N
> fmt::enable_if_t
<N
== 0, int> get() const noexcept
{
120 fmt::enable_if_t
<N
== 1, fmt::string_view
> get() const noexcept
{
126 auto get(const tuple_like
& t
) noexcept
-> decltype(t
.get
<N
>()) {
132 struct tuple_size
<tuple_like
> : std::integral_constant
<size_t, 2> {};
134 template <size_t N
> struct tuple_element
<N
, tuple_like
> {
135 using type
= decltype(std::declval
<tuple_like
>().get
<N
>());
139 TEST(ranges_test
, format_struct
) {
140 auto t
= tuple_like
{42, "foo"};
141 EXPECT_EQ(fmt::format("{}", t
), "(42, \"foo\")");
143 #endif // FMT_RANGES_TEST_ENABLE_FORMAT_STRUCT
145 TEST(ranges_test
, format_to
) {
147 auto end
= fmt::format_to(buf
, "{}", std::vector
<int>{1, 2, 3});
149 EXPECT_STREQ(buf
, "[1, 2, 3]");
153 const path_like
* begin() const;
154 const path_like
* end() const;
156 operator std::string() const;
159 TEST(ranges_test
, path_like
) {
160 EXPECT_FALSE((fmt::is_range
<path_like
, char>::value
));
163 #ifdef FMT_USE_STRING_VIEW
167 operator fmt::string_view() const { return "foo"; }
168 operator std::string_view() const { return "foo"; }
171 TEST(ranges_test
, format_string_like
) {
172 EXPECT_EQ(fmt::format("{}", string_like()), "foo");
174 #endif // FMT_USE_STRING_VIEW
176 // A range that provides non-const only begin()/end() to test fmt::join handles
179 // Some ranges (e.g. those produced by range-v3's views::filter()) can cache
180 // information during iteration so they only provide non-const begin()/end().
181 template <typename T
> class non_const_only_range
{
186 using const_iterator
= typename ::std::vector
<T
>::const_iterator
;
188 template <typename
... Args
>
189 explicit non_const_only_range(Args
&&... args
)
190 : vec(std::forward
<Args
>(args
)...) {}
192 const_iterator
begin() { return vec
.begin(); }
193 const_iterator
end() { return vec
.end(); }
196 template <typename T
> class noncopyable_range
{
201 using const_iterator
= typename ::std::vector
<T
>::const_iterator
;
203 template <typename
... Args
>
204 explicit noncopyable_range(Args
&&... args
)
205 : vec(std::forward
<Args
>(args
)...) {}
207 noncopyable_range(noncopyable_range
const&) = delete;
208 noncopyable_range(noncopyable_range
&) = delete;
210 const_iterator
begin() const { return vec
.begin(); }
211 const_iterator
end() const { return vec
.end(); }
214 TEST(ranges_test
, range
) {
215 noncopyable_range
<int> w(3u, 0);
216 EXPECT_EQ(fmt::format("{}", w
), "[0, 0, 0]");
217 EXPECT_EQ(fmt::format("{}", noncopyable_range
<int>(3u, 0)), "[0, 0, 0]");
219 non_const_only_range
<int> x(3u, 0);
220 EXPECT_EQ(fmt::format("{}", x
), "[0, 0, 0]");
221 EXPECT_EQ(fmt::format("{}", non_const_only_range
<int>(3u, 0)), "[0, 0, 0]");
223 auto y
= std::vector
<int>(3u, 0);
224 EXPECT_EQ(fmt::format("{}", y
), "[0, 0, 0]");
225 EXPECT_EQ(fmt::format("{}", std::vector
<int>(3u, 0)), "[0, 0, 0]");
227 const auto z
= std::vector
<int>(3u, 0);
228 EXPECT_EQ(fmt::format("{}", z
), "[0, 0, 0]");
231 enum test_enum
{ foo
};
232 auto format_as(test_enum e
) -> int { return e
; }
234 TEST(ranges_test
, enum_range
) {
235 auto v
= std::vector
<test_enum
>{test_enum::foo
};
236 EXPECT_EQ(fmt::format("{}", v
), "[0]");
241 TEST(ranges_test
, unformattable_range
) {
242 EXPECT_FALSE((fmt::has_formatter
<std::vector
<unformattable
>,
243 fmt::format_context
>::value
));
247 #ifdef FMT_RANGES_TEST_ENABLE_JOIN
248 TEST(ranges_test
, join_tuple
) {
250 auto t1
= std::tuple
<char, int, float>('a', 1, 2.0f
);
251 EXPECT_EQ(fmt::format("({})", fmt::join(t1
, ", ")), "(a, 1, 2)");
253 // Testing lvalue tuple args.
255 auto t2
= std::tuple
<char, int&>('b', x
);
256 EXPECT_EQ(fmt::format("{}", fmt::join(t2
, " + ")), "b + 4");
259 auto t3
= std::tuple
<>();
260 EXPECT_EQ(fmt::format("{}", fmt::join(t3
, "|")), "");
262 // Single element tuple.
263 auto t4
= std::tuple
<float>(4.0f
);
264 EXPECT_EQ(fmt::format("{}", fmt::join(t4
, "/")), "4");
266 # if FMT_TUPLE_JOIN_SPECIFIERS
267 // Specs applied to each element.
268 auto t5
= std::tuple
<int, int, long>(-3, 100, 1);
269 EXPECT_EQ(fmt::format("{:+03}", fmt::join(t5
, ", ")), "-03, +100, +01");
271 auto t6
= std::tuple
<float, double, long double>(3, 3.14, 3.1415);
272 EXPECT_EQ(fmt::format("{:5.5f}", fmt::join(t6
, ", ")),
273 "3.00000, 3.14000, 3.14150");
275 // Testing lvalue tuple args.
277 auto t7
= std::tuple
<int, int&, const int&>(3, y
, y
);
278 EXPECT_EQ(fmt::format("{:03}", fmt::join(t7
, ", ")), "003, -01, -01");
282 TEST(ranges_test
, join_initializer_list
) {
283 EXPECT_EQ(fmt::format("{}", fmt::join({1, 2, 3}, ", ")), "1, 2, 3");
284 EXPECT_EQ(fmt::format("{}", fmt::join({"fmt", "rocks", "!"}, " ")),
288 struct zstring_sentinel
{};
290 bool operator==(const char* p
, zstring_sentinel
) { return *p
== '\0'; }
291 bool operator!=(const char* p
, zstring_sentinel
) { return *p
!= '\0'; }
295 const char* begin() const { return p
; }
296 zstring_sentinel
end() const { return {}; }
299 # ifdef __cpp_lib_ranges
300 struct cpp20_only_range
{
304 using value_type
= int;
305 using difference_type
= std::ptrdiff_t;
306 using iterator_concept
= std::input_iterator_tag
;
308 iterator() = default;
309 iterator(int i
) : val(i
) {}
310 int operator*() const { return val
; }
311 iterator
& operator++() {
315 void operator++(int) { ++*this; }
316 bool operator==(const iterator
& rhs
) const { return val
== rhs
.val
; }
322 iterator
begin() const { return iterator(lo
); }
323 iterator
end() const { return iterator(hi
); }
326 static_assert(std::input_iterator
<cpp20_only_range::iterator
>);
329 TEST(ranges_test
, join_sentinel
) {
330 auto hello
= zstring
{"hello"};
331 EXPECT_EQ(fmt::format("{}", hello
), "['h', 'e', 'l', 'l', 'o']");
332 EXPECT_EQ(fmt::format("{::}", hello
), "[h, e, l, l, o]");
333 EXPECT_EQ(fmt::format("{}", fmt::join(hello
, "_")), "h_e_l_l_o");
336 TEST(ranges_test
, join_range
) {
337 noncopyable_range
<int> w(3u, 0);
338 EXPECT_EQ(fmt::format("{}", fmt::join(w
, ",")), "0,0,0");
339 EXPECT_EQ(fmt::format("{}", fmt::join(noncopyable_range
<int>(3u, 0), ",")),
342 non_const_only_range
<int> x(3u, 0);
343 EXPECT_EQ(fmt::format("{}", fmt::join(x
, ",")), "0,0,0");
344 EXPECT_EQ(fmt::format("{}", fmt::join(non_const_only_range
<int>(3u, 0), ",")),
347 auto y
= std::vector
<int>(3u, 0);
348 EXPECT_EQ(fmt::format("{}", fmt::join(y
, ",")), "0,0,0");
349 EXPECT_EQ(fmt::format("{}", fmt::join(std::vector
<int>(3u, 0), ",")),
352 const auto z
= std::vector
<int>(3u, 0);
353 EXPECT_EQ(fmt::format("{}", fmt::join(z
, ",")), "0,0,0");
355 # ifdef __cpp_lib_ranges
356 EXPECT_EQ(fmt::format("{}", cpp20_only_range
{.lo
= 0, .hi
= 5}),
359 fmt::format("{}", fmt::join(cpp20_only_range
{.lo
= 0, .hi
= 5}, ",")),
363 #endif // FMT_RANGES_TEST_ENABLE_JOIN
365 TEST(ranges_test
, is_printable
) {
366 using fmt::detail::is_printable
;
367 EXPECT_TRUE(is_printable(0x0323));
368 EXPECT_FALSE(is_printable(0x0378));
369 EXPECT_FALSE(is_printable(0x110000));
372 TEST(ranges_test
, escape_string
) {
373 using vec
= std::vector
<std::string
>;
374 EXPECT_EQ(fmt::format("{}", vec
{"\n\r\t\"\\"}), "[\"\\n\\r\\t\\\"\\\\\"]");
375 EXPECT_EQ(fmt::format("{}", vec
{"\x07"}), "[\"\\x07\"]");
376 EXPECT_EQ(fmt::format("{}", vec
{"\x7f"}), "[\"\\x7f\"]");
377 EXPECT_EQ(fmt::format("{}", vec
{"n\xcc\x83"}), "[\"n\xcc\x83\"]");
379 if (fmt::detail::is_utf8()) {
380 EXPECT_EQ(fmt::format("{}", vec
{"\xcd\xb8"}), "[\"\\u0378\"]");
381 // Unassigned Unicode code points.
382 EXPECT_EQ(fmt::format("{}", vec
{"\xf0\xaa\x9b\x9e"}), "[\"\\U0002a6de\"]");
384 EXPECT_EQ(fmt::format("{}", vec
{"\xf4\x8f\xbf\xc0"}),
385 "[\"\\xf4\\x8f\\xbf\\xc0\"]");
386 EXPECT_EQ(fmt::format("{}", vec
{"\xf0\x28"}), "[\"\\xf0(\"]");
387 EXPECT_EQ(fmt::format("{}", vec
{"\xe1\x28"}), "[\"\\xe1(\"]");
388 EXPECT_EQ(fmt::format("{}", vec
{std::string("\xf0\x28\0\0anything", 12)}),
389 "[\"\\xf0(\\x00\\x00anything\"]");
392 EXPECT_EQ(fmt::format("{}", vec
{"понедельник"}), "[\"понедельник\"]");
396 #ifdef FMT_USE_STRING_VIEW
397 struct convertible_to_string_view
{
398 operator std::string_view() const { return "foo"; }
401 TEST(ranges_test
, escape_convertible_to_string_view
) {
402 EXPECT_EQ(fmt::format("{}", std::vector
<convertible_to_string_view
>(1)),
405 #endif // FMT_USE_STRING_VIEW
407 template <typename R
> struct fmt_ref_view
{
410 auto begin() const -> decltype(r
->begin()) { return r
->begin(); }
411 auto end() const -> decltype(r
->end()) { return r
->end(); }
414 TEST(ranges_test
, range_of_range_of_mixed_const
) {
415 std::vector
<std::vector
<int>> v
= {{1, 2, 3}, {4, 5}};
416 EXPECT_EQ(fmt::format("{}", v
), "[[1, 2, 3], [4, 5]]");
418 fmt_ref_view
<decltype(v
)> r
{&v
};
419 EXPECT_EQ(fmt::format("{}", r
), "[[1, 2, 3], [4, 5]]");
422 TEST(ranges_test
, vector_char
) {
423 EXPECT_EQ(fmt::format("{}", std::vector
<char>{'a', 'b'}), "['a', 'b']");