• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 "gtest.h"
15 
16 // Check if  'if constexpr' is supported.
17 #if (__cplusplus > 201402L) || \
18     (defined(_MSVC_LANG) && _MSVC_LANG > 201402L && _MSC_VER >= 1910)
19 
20 #  include <array>
21 #  include <map>
22 #  include <string>
23 #  include <vector>
24 
TEST(RangesTest,FormatVector)25 TEST(RangesTest, FormatVector) {
26   std::vector<int32_t> iv{1, 2, 3, 5, 7, 11};
27   auto ivf = fmt::format("{}", iv);
28   EXPECT_EQ("{1, 2, 3, 5, 7, 11}", ivf);
29 }
30 
TEST(RangesTest,FormatVector2)31 TEST(RangesTest, FormatVector2) {
32   std::vector<std::vector<int32_t>> ivv{{1, 2}, {3, 5}, {7, 11}};
33   auto ivf = fmt::format("{}", ivv);
34   EXPECT_EQ("{{1, 2}, {3, 5}, {7, 11}}", ivf);
35 }
36 
TEST(RangesTest,FormatMap)37 TEST(RangesTest, FormatMap) {
38   std::map<std::string, int32_t> simap{{"one", 1}, {"two", 2}};
39   EXPECT_EQ("{(\"one\", 1), (\"two\", 2)}", fmt::format("{}", simap));
40 }
41 
TEST(RangesTest,FormatPair)42 TEST(RangesTest, FormatPair) {
43   std::pair<int64_t, float> pa1{42, 1.5f};
44   EXPECT_EQ("(42, 1.5)", fmt::format("{}", pa1));
45 }
46 
TEST(RangesTest,FormatTuple)47 TEST(RangesTest, FormatTuple) {
48   std::tuple<int64_t, float, std::string, char> t{42, 1.5f, "this is tuple",
49                                                   'i'};
50   EXPECT_EQ("(42, 1.5, \"this is tuple\", 'i')", fmt::format("{}", t));
51   EXPECT_EQ("()", fmt::format("{}", std::tuple<>()));
52 }
53 
TEST(RangesTest,JoinTuple)54 TEST(RangesTest, JoinTuple) {
55   // Value tuple args
56   std::tuple<char, int, float> t1 = std::make_tuple('a', 1, 2.0f);
57   EXPECT_EQ("(a, 1, 2)", fmt::format("({})", fmt::join(t1, ", ")));
58 
59   // Testing lvalue tuple args
60   int x = 4;
61   std::tuple<char, int&> t2{'b', x};
62   EXPECT_EQ("b + 4", fmt::format("{}", fmt::join(t2, " + ")));
63 
64   // Empty tuple
65   std::tuple<> t3;
66   EXPECT_EQ("", fmt::format("{}", fmt::join(t3, "|")));
67 
68   // Single element tuple
69   std::tuple<float> t4{4.0f};
70   EXPECT_EQ("4", fmt::format("{}", fmt::join(t4, "/")));
71 }
72 
TEST(RangesTest,JoinInitializerList)73 TEST(RangesTest, JoinInitializerList) {
74   EXPECT_EQ("1, 2, 3", fmt::format("{}", fmt::join({1, 2, 3}, ", ")));
75   EXPECT_EQ("fmt rocks !",
76             fmt::format("{}", fmt::join({"fmt", "rocks", "!"}, " ")));
77 }
78 
79 struct my_struct {
80   int32_t i;
81   std::string str;  // can throw
getmy_struct82   template <size_t N> decltype(auto) get() const noexcept {
83     if constexpr (N == 0)
84       return i;
85     else if constexpr (N == 1)
86       return fmt::string_view{str};
87   }
88 };
89 
get(const my_struct & s)90 template <size_t N> decltype(auto) get(const my_struct& s) noexcept {
91   return s.get<N>();
92 }
93 
94 namespace std {
95 
96 template <> struct tuple_size<my_struct> : std::integral_constant<size_t, 2> {};
97 
98 template <size_t N> struct tuple_element<N, my_struct> {
99   using type = decltype(std::declval<my_struct>().get<N>());
100 };
101 
102 }  // namespace std
103 
TEST(RangesTest,FormatStruct)104 TEST(RangesTest, FormatStruct) {
105   my_struct mst{13, "my struct"};
106   EXPECT_EQ("(13, \"my struct\")", fmt::format("{}", mst));
107 }
108 
TEST(RangesTest,FormatTo)109 TEST(RangesTest, FormatTo) {
110   char buf[10];
111   auto end = fmt::format_to(buf, "{}", std::vector{1, 2, 3});
112   *end = '\0';
113   EXPECT_STREQ(buf, "{1, 2, 3}");
114 }
115 
116 struct path_like {
117   const path_like* begin() const;
118   const path_like* end() const;
119 
120   operator std::string() const;
121 };
122 
TEST(RangesTest,PathLike)123 TEST(RangesTest, PathLike) {
124   EXPECT_FALSE((fmt::is_range<path_like, char>::value));
125 }
126 
127 #endif  // (__cplusplus > 201402L) || (defined(_MSVC_LANG) && _MSVC_LANG >
128         // 201402L && _MSC_VER >= 1910)
129 
130 #ifdef FMT_USE_STRING_VIEW
131 struct string_like {
132   const char* begin();
133   const char* end();
operator fmt::string_viewstring_like134   explicit operator fmt::string_view() const { return "foo"; }
operator std::string_viewstring_like135   explicit operator std::string_view() const { return "foo"; }
136 };
137 
TEST(RangesTest,FormatStringLike)138 TEST(RangesTest, FormatStringLike) {
139   EXPECT_EQ("foo", fmt::format("{}", string_like()));
140 }
141 #endif  // FMT_USE_STRING_VIEW
142 
143 struct zstring_sentinel {};
144 
operator ==(const char * p,zstring_sentinel)145 bool operator==(const char* p, zstring_sentinel) { return *p == '\0'; }
operator !=(const char * p,zstring_sentinel)146 bool operator!=(const char* p, zstring_sentinel) { return *p != '\0'; }
147 
148 struct zstring {
149   const char* p;
beginzstring150   const char* begin() const { return p; }
endzstring151   zstring_sentinel end() const { return {}; }
152 };
153 
TEST(RangesTest,JoinSentinel)154 TEST(RangesTest, JoinSentinel) {
155   zstring hello{"hello"};
156   EXPECT_EQ("{'h', 'e', 'l', 'l', 'o'}", fmt::format("{}", hello));
157   EXPECT_EQ("h_e_l_l_o", fmt::format("{}", fmt::join(hello, "_")));
158 }
159 
160 // A range that provides non-const only begin()/end() to test fmt::join handles
161 // that
162 //
163 // Some ranges (eg those produced by range-v3's views::filter()) can cache
164 // information during iteration so they only provide non-const begin()/end().
165 template <typename T> class non_const_only_range {
166  private:
167   std::vector<T> vec;
168 
169  public:
170   using const_iterator = typename ::std::vector<T>::const_iterator;
171 
172   template <typename... Args>
non_const_only_range(Args &&...args)173   explicit non_const_only_range(Args&&... args)
174       : vec(::std::forward<Args>(args)...) {}
175 
begin()176   const_iterator begin() { return vec.begin(); }
end()177   const_iterator end() { return vec.end(); }
178 };
179 
TEST(RangesTest,JoinRange)180 TEST(RangesTest, JoinRange) {
181   non_const_only_range<int> x(3u, 0);
182   EXPECT_EQ("0,0,0", fmt::format("{}", fmt::join(x, ",")));
183   EXPECT_EQ(
184       "0,0,0",
185       fmt::format("{}", fmt::join(non_const_only_range<int>(3u, 0), ",")));
186 
187   std::vector<int> y(3u, 0);
188   EXPECT_EQ("0,0,0", fmt::format("{}", fmt::join(y, ",")));
189   EXPECT_EQ("0,0,0",
190             fmt::format("{}", fmt::join(std::vector<int>(3u, 0), ",")));
191 
192   const std::vector<int> z(3u, 0);
193   EXPECT_EQ("0,0,0", fmt::format("{}", fmt::join(z, ",")));
194 }
195 
196 #if !FMT_MSC_VER || FMT_MSC_VER >= 1927
197 struct unformattable {};
198 
TEST(RangesTest,UnformattableRange)199 TEST(RangesTest, UnformattableRange) {
200   EXPECT_FALSE((fmt::has_formatter<std::vector<unformattable>,
201                                    fmt::format_context>::value));
202 }
203 #endif
204