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 <string>
16 #include <vector>
17 
18 #include "gtest/gtest.h"
19 
20 #if !FMT_GCC_VERSION || FMT_GCC_VERSION >= 601
21 #  define FMT_RANGES_TEST_ENABLE_C_STYLE_ARRAY
22 #endif
23 
24 #if !FMT_MSC_VER || FMT_MSC_VER > 1910
25 #  define FMT_RANGES_TEST_ENABLE_JOIN
26 #  define FMT_RANGES_TEST_ENABLE_FORMAT_STRUCT
27 #endif
28 
29 #ifdef FMT_RANGES_TEST_ENABLE_C_STYLE_ARRAY
TEST(ranges_test,format_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]");
33 }
34 
TEST(ranges_test,format_2d_array)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]]");
38 }
39 
TEST(ranges_test,format_array_of_literals)40 TEST(ranges_test, format_array_of_literals) {
41   const char* arr[] = {"1234", "abcd"};
42   EXPECT_EQ(fmt::format("{}", arr), "[\"1234\", \"abcd\"]");
43 }
44 #endif  // FMT_RANGES_TEST_ENABLE_C_STYLE_ARRAY
45 
TEST(ranges_test,format_vector)46 TEST(ranges_test, format_vector) {
47   auto v = std::vector<int>{1, 2, 3, 5, 7, 11};
48   EXPECT_EQ(fmt::format("{}", v), "[1, 2, 3, 5, 7, 11]");
49 }
50 
TEST(ranges_test,format_vector2)51 TEST(ranges_test, format_vector2) {
52   auto v = std::vector<std::vector<int>>{{1, 2}, {3, 5}, {7, 11}};
53   EXPECT_EQ(fmt::format("{}", v), "[[1, 2], [3, 5], [7, 11]]");
54 }
55 
TEST(ranges_test,format_map)56 TEST(ranges_test, format_map) {
57   auto m = std::map<std::string, int>{{"one", 1}, {"two", 2}};
58   EXPECT_EQ(fmt::format("{}", m), "[(\"one\", 1), (\"two\", 2)]");
59 }
60 
TEST(ranges_test,format_pair)61 TEST(ranges_test, format_pair) {
62   auto p = std::pair<int, float>(42, 1.5f);
63   EXPECT_EQ(fmt::format("{}", p), "(42, 1.5)");
64 }
65 
TEST(ranges_test,format_tuple)66 TEST(ranges_test, format_tuple) {
67   auto t =
68       std::tuple<int, float, std::string, char>(42, 1.5f, "this is tuple", 'i');
69   EXPECT_EQ(fmt::format("{}", t), "(42, 1.5, \"this is tuple\", 'i')");
70   EXPECT_EQ(fmt::format("{}", std::tuple<>()), "()");
71 }
72 
73 #ifdef FMT_RANGES_TEST_ENABLE_FORMAT_STRUCT
74 struct tuple_like {
75   int i;
76   std::string str;
77 
gettuple_like78   template <size_t N> fmt::enable_if_t<N == 0, int> get() const noexcept {
79     return i;
80   }
81   template <size_t N>
gettuple_like82   fmt::enable_if_t<N == 1, fmt::string_view> get() const noexcept {
83     return str;
84   }
85 };
86 
87 template <size_t N>
get(const tuple_like & t)88 auto get(const tuple_like& t) noexcept -> decltype(t.get<N>()) {
89   return t.get<N>();
90 }
91 
92 namespace std {
93 template <>
94 struct tuple_size<tuple_like> : std::integral_constant<size_t, 2> {};
95 
96 template <size_t N> struct tuple_element<N, tuple_like> {
97   using type = decltype(std::declval<tuple_like>().get<N>());
98 };
99 }  // namespace std
100 
TEST(ranges_test,format_struct)101 TEST(ranges_test, format_struct) {
102   auto t = tuple_like{42, "foo"};
103   EXPECT_EQ(fmt::format("{}", t), "(42, \"foo\")");
104 }
105 #endif  // FMT_RANGES_TEST_ENABLE_FORMAT_STRUCT
106 
TEST(ranges_test,format_to)107 TEST(ranges_test, format_to) {
108   char buf[10];
109   auto end = fmt::format_to(buf, "{}", std::vector<int>{1, 2, 3});
110   *end = '\0';
111   EXPECT_STREQ(buf, "[1, 2, 3]");
112 }
113 
114 struct path_like {
115   const path_like* begin() const;
116   const path_like* end() const;
117 
118   operator std::string() const;
119 };
120 
TEST(ranges_test,path_like)121 TEST(ranges_test, path_like) {
122   EXPECT_FALSE((fmt::is_range<path_like, char>::value));
123 }
124 
125 #ifdef FMT_USE_STRING_VIEW
126 struct string_like {
127   const char* begin();
128   const char* end();
operator fmt::string_viewstring_like129   explicit operator fmt::string_view() const { return "foo"; }
operator std::string_viewstring_like130   explicit operator std::string_view() const { return "foo"; }
131 };
132 
TEST(ranges_test,format_string_like)133 TEST(ranges_test, format_string_like) {
134   EXPECT_EQ(fmt::format("{}", string_like()), "foo");
135 }
136 #endif  // FMT_USE_STRING_VIEW
137 
138 // A range that provides non-const only begin()/end() to test fmt::join handles
139 // that.
140 //
141 // Some ranges (e.g. those produced by range-v3's views::filter()) can cache
142 // information during iteration so they only provide non-const begin()/end().
143 template <typename T> class non_const_only_range {
144  private:
145   std::vector<T> vec;
146 
147  public:
148   using const_iterator = typename ::std::vector<T>::const_iterator;
149 
150   template <typename... Args>
non_const_only_range(Args &&...args)151   explicit non_const_only_range(Args&&... args)
152       : vec(std::forward<Args>(args)...) {}
153 
begin()154   const_iterator begin() { return vec.begin(); }
end()155   const_iterator end() { return vec.end(); }
156 };
157 
158 template <typename T> class noncopyable_range {
159  private:
160   std::vector<T> vec;
161 
162  public:
163   using const_iterator = typename ::std::vector<T>::const_iterator;
164 
165   template <typename... Args>
noncopyable_range(Args &&...args)166   explicit noncopyable_range(Args&&... args)
167       : vec(std::forward<Args>(args)...) {}
168 
169   noncopyable_range(noncopyable_range const&) = delete;
170   noncopyable_range(noncopyable_range&) = delete;
171 
begin() const172   const_iterator begin() const { return vec.begin(); }
end() const173   const_iterator end() const { return vec.end(); }
174 };
175 
TEST(ranges_test,range)176 TEST(ranges_test, range) {
177   noncopyable_range<int> w(3u, 0);
178   EXPECT_EQ(fmt::format("{}", w), "[0, 0, 0]");
179   EXPECT_EQ(fmt::format("{}", noncopyable_range<int>(3u, 0)), "[0, 0, 0]");
180 
181   non_const_only_range<int> x(3u, 0);
182   EXPECT_EQ(fmt::format("{}", x), "[0, 0, 0]");
183   EXPECT_EQ(fmt::format("{}", non_const_only_range<int>(3u, 0)), "[0, 0, 0]");
184 
185   auto y = std::vector<int>(3u, 0);
186   EXPECT_EQ(fmt::format("{}", y), "[0, 0, 0]");
187   EXPECT_EQ(fmt::format("{}", std::vector<int>(3u, 0)), "[0, 0, 0]");
188 
189   const auto z = std::vector<int>(3u, 0);
190   EXPECT_EQ(fmt::format("{}", z), "[0, 0, 0]");
191 }
192 
193 #if !FMT_MSC_VER || FMT_MSC_VER >= 1927
194 struct unformattable {};
195 
TEST(ranges_test,unformattable_range)196 TEST(ranges_test, unformattable_range) {
197   EXPECT_FALSE((fmt::has_formatter<std::vector<unformattable>,
198                                    fmt::format_context>::value));
199 }
200 #endif
201 
202 #ifdef FMT_RANGES_TEST_ENABLE_JOIN
TEST(ranges_test,join_tuple)203 TEST(ranges_test, join_tuple) {
204   // Value tuple args.
205   auto t1 = std::tuple<char, int, float>('a', 1, 2.0f);
206   EXPECT_EQ(fmt::format("({})", fmt::join(t1, ", ")), "(a, 1, 2)");
207 
208   // Testing lvalue tuple args.
209   int x = 4;
210   auto t2 = std::tuple<char, int&>('b', x);
211   EXPECT_EQ(fmt::format("{}", fmt::join(t2, " + ")), "b + 4");
212 
213   // Empty tuple.
214   auto t3 = std::tuple<>();
215   EXPECT_EQ(fmt::format("{}", fmt::join(t3, "|")), "");
216 
217   // Single element tuple.
218   auto t4 = std::tuple<float>(4.0f);
219   EXPECT_EQ(fmt::format("{}", fmt::join(t4, "/")), "4");
220 }
221 
TEST(ranges_test,join_initializer_list)222 TEST(ranges_test, join_initializer_list) {
223   EXPECT_EQ(fmt::format("{}", fmt::join({1, 2, 3}, ", ")), "1, 2, 3");
224   EXPECT_EQ(fmt::format("{}", fmt::join({"fmt", "rocks", "!"}, " ")),
225             "fmt rocks !");
226 }
227 
228 struct zstring_sentinel {};
229 
operator ==(const char * p,zstring_sentinel)230 bool operator==(const char* p, zstring_sentinel) { return *p == '\0'; }
operator !=(const char * p,zstring_sentinel)231 bool operator!=(const char* p, zstring_sentinel) { return *p != '\0'; }
232 
233 struct zstring {
234   const char* p;
beginzstring235   const char* begin() const { return p; }
endzstring236   zstring_sentinel end() const { return {}; }
237 };
238 
TEST(ranges_test,join_sentinel)239 TEST(ranges_test, join_sentinel) {
240   auto hello = zstring{"hello"};
241   EXPECT_EQ(fmt::format("{}", hello), "['h', 'e', 'l', 'l', 'o']");
242   EXPECT_EQ(fmt::format("{}", fmt::join(hello, "_")), "h_e_l_l_o");
243 }
244 
TEST(ranges_test,join_range)245 TEST(ranges_test, join_range) {
246   noncopyable_range<int> w(3u, 0);
247   EXPECT_EQ(fmt::format("{}", fmt::join(w, ",")), "0,0,0");
248   EXPECT_EQ(fmt::format("{}", fmt::join(noncopyable_range<int>(3u, 0), ",")),
249             "0,0,0");
250 
251   non_const_only_range<int> x(3u, 0);
252   EXPECT_EQ(fmt::format("{}", fmt::join(x, ",")), "0,0,0");
253   EXPECT_EQ(fmt::format("{}", fmt::join(non_const_only_range<int>(3u, 0), ",")),
254             "0,0,0");
255 
256   auto y = std::vector<int>(3u, 0);
257   EXPECT_EQ(fmt::format("{}", fmt::join(y, ",")), "0,0,0");
258   EXPECT_EQ(fmt::format("{}", fmt::join(std::vector<int>(3u, 0), ",")),
259             "0,0,0");
260 
261   const auto z = std::vector<int>(3u, 0);
262   EXPECT_EQ(fmt::format("{}", fmt::join(z, ",")), "0,0,0");
263 }
264 #endif  // FMT_RANGES_TEST_ENABLE_JOIN
265