xref: /llvm-project/libcxx/test/std/utilities/format/format.arguments/format.arg/visit.pass.cpp (revision 7f845cba2ccc2ab637b8e40fbafb9f83a2d67c70)
1 //===----------------------------------------------------------------------===//
2 //
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 //
7 //===----------------------------------------------------------------------===//
8 
9 // UNSUPPORTED: c++03, c++11, c++14, c++17, c++20, c++23
10 // UNSUPPORTED: GCC-ALWAYS_INLINE-FIXME
11 // The tested functionality needs deducing this.
12 // XFAIL: apple-clang
13 
14 // <format>
15 
16 // class basic_format_arg;
17 
18 // template<class Visitor>
19 //   decltype(auto) visit(this basic_format_arg arg, Visitor&& vis);  // since C++26
20 
21 #include <algorithm>
22 #include <cassert>
23 #include <format>
24 #include <type_traits>
25 
26 #include "constexpr_char_traits.h"
27 #include "make_string.h"
28 #include "min_allocator.h"
29 #include "test_macros.h"
30 
31 template <class Context, class To, class From>
32 void test(From value) {
33   auto store = std::make_format_args<Context>(value);
34   std::basic_format_args<Context> format_args{store};
35 
36   LIBCPP_ASSERT(format_args.__size() == 1);
37   assert(format_args.get(0));
38 
39   auto result = format_args.get(0).visit([v = To(value)](auto a) -> To {
40     if constexpr (std::is_same_v<To, decltype(a)>) {
41       assert(v == a);
42       return a;
43     } else {
44       assert(false);
45       return {};
46     }
47   });
48 
49   using ct = std::common_type_t<From, To>;
50   assert(static_cast<ct>(result) == static_cast<ct>(value));
51 }
52 
53 // Some types, as an extension, are stored in the variant. The Standard
54 // requires them to be observed as a handle.
55 template <class Context, class T>
56 void test_handle(T value) {
57   auto store = std::make_format_args<Context>(value);
58   std::basic_format_args<Context> format_args{store};
59 
60   LIBCPP_ASSERT(format_args.__size() == 1);
61   assert(format_args.get(0));
62 
63   format_args.get(0).visit([](auto a) {
64     assert((std::is_same_v<decltype(a), typename std::basic_format_arg<Context>::handle>));
65   });
66 }
67 
68 // Test specific for string and string_view.
69 //
70 // Since both result in a string_view there's no need to pass this as a
71 // template argument.
72 template <class Context, class From>
73 void test_string_view(From value) {
74   auto store = std::make_format_args<Context>(value);
75   std::basic_format_args<Context> format_args{store};
76 
77   LIBCPP_ASSERT(format_args.__size() == 1);
78   assert(format_args.get(0));
79 
80   using CharT = typename Context::char_type;
81   using To    = std::basic_string_view<CharT>;
82   using V     = std::basic_string<CharT>;
83 
84   auto result = format_args.get(0).visit([v = V(value.begin(), value.end())](auto a) -> To {
85     if constexpr (std::is_same_v<To, decltype(a)>) {
86       assert(v == a);
87       return a;
88     } else {
89       assert(false);
90       return {};
91     }
92   });
93 
94   assert(std::equal(value.begin(), value.end(), result.begin(), result.end()));
95 }
96 
97 template <class CharT>
98 void test() {
99   using Context = std::basic_format_context<CharT*, CharT>;
100   std::basic_string<CharT> empty;
101   std::basic_string<CharT> str = MAKE_STRING(CharT, "abc");
102 
103   // Test boolean types.
104 
105   test<Context, bool>(true);
106   test<Context, bool>(false);
107 
108   // Test CharT types.
109 
110   test<Context, CharT, CharT>('a');
111   test<Context, CharT, CharT>('z');
112   test<Context, CharT, CharT>('0');
113   test<Context, CharT, CharT>('9');
114 
115   // Test char types.
116 
117   if (std::is_same_v<CharT, char>) {
118     // char to char -> char
119     test<Context, CharT, char>('a');
120     test<Context, CharT, char>('z');
121     test<Context, CharT, char>('0');
122     test<Context, CharT, char>('9');
123   } else {
124     if (std::is_same_v<CharT, wchar_t>) {
125       // char to wchar_t -> wchar_t
126       test<Context, wchar_t, char>('a');
127       test<Context, wchar_t, char>('z');
128       test<Context, wchar_t, char>('0');
129       test<Context, wchar_t, char>('9');
130     } else if (std::is_signed_v<char>) {
131       // char to CharT -> int
132       // This happens when CharT is a char8_t, char16_t, or char32_t and char
133       // is a signed type.
134       // Note if sizeof(CharT) > sizeof(int) this test fails. If there are
135       // platforms where that occurs extra tests need to be added for char32_t
136       // testing it against a long long.
137       test<Context, int, char>('a');
138       test<Context, int, char>('z');
139       test<Context, int, char>('0');
140       test<Context, int, char>('9');
141     } else {
142       // char to CharT -> unsigned
143       // This happens when CharT is a char8_t, char16_t, or char32_t and char
144       // is an unsigned type.
145       // Note if sizeof(CharT) > sizeof(unsigned) this test fails. If there are
146       // platforms where that occurs extra tests need to be added for char32_t
147       // testing it against an unsigned long long.
148       test<Context, unsigned, char>('a');
149       test<Context, unsigned, char>('z');
150       test<Context, unsigned, char>('0');
151       test<Context, unsigned, char>('9');
152     }
153   }
154 
155   // Test signed integer types.
156 
157   test<Context, int, signed char>(std::numeric_limits<signed char>::min());
158   test<Context, int, signed char>(0);
159   test<Context, int, signed char>(std::numeric_limits<signed char>::max());
160 
161   test<Context, int, short>(std::numeric_limits<short>::min());
162   test<Context, int, short>(std::numeric_limits<signed char>::min());
163   test<Context, int, short>(0);
164   test<Context, int, short>(std::numeric_limits<signed char>::max());
165   test<Context, int, short>(std::numeric_limits<short>::max());
166 
167   test<Context, int, int>(std::numeric_limits<int>::min());
168   test<Context, int, int>(std::numeric_limits<short>::min());
169   test<Context, int, int>(std::numeric_limits<signed char>::min());
170   test<Context, int, int>(0);
171   test<Context, int, int>(std::numeric_limits<signed char>::max());
172   test<Context, int, int>(std::numeric_limits<short>::max());
173   test<Context, int, int>(std::numeric_limits<int>::max());
174 
175   using LongToType = std::conditional_t<sizeof(long) == sizeof(int), int, long long>;
176 
177   test<Context, LongToType, long>(std::numeric_limits<long>::min());
178   test<Context, LongToType, long>(std::numeric_limits<int>::min());
179   test<Context, LongToType, long>(std::numeric_limits<short>::min());
180   test<Context, LongToType, long>(std::numeric_limits<signed char>::min());
181   test<Context, LongToType, long>(0);
182   test<Context, LongToType, long>(std::numeric_limits<signed char>::max());
183   test<Context, LongToType, long>(std::numeric_limits<short>::max());
184   test<Context, LongToType, long>(std::numeric_limits<int>::max());
185   test<Context, LongToType, long>(std::numeric_limits<long>::max());
186 
187   test<Context, long long, long long>(std::numeric_limits<long long>::min());
188   test<Context, long long, long long>(std::numeric_limits<long>::min());
189   test<Context, long long, long long>(std::numeric_limits<int>::min());
190   test<Context, long long, long long>(std::numeric_limits<short>::min());
191   test<Context, long long, long long>(std::numeric_limits<signed char>::min());
192   test<Context, long long, long long>(0);
193   test<Context, long long, long long>(std::numeric_limits<signed char>::max());
194   test<Context, long long, long long>(std::numeric_limits<short>::max());
195   test<Context, long long, long long>(std::numeric_limits<int>::max());
196   test<Context, long long, long long>(std::numeric_limits<long>::max());
197   test<Context, long long, long long>(std::numeric_limits<long long>::max());
198 
199 #ifndef TEST_HAS_NO_INT128
200   test_handle<Context, __int128_t>(0);
201 #endif // TEST_HAS_NO_INT128
202 
203   // Test unsigned integer types.
204 
205   test<Context, unsigned, unsigned char>(0);
206   test<Context, unsigned, unsigned char>(std::numeric_limits<unsigned char>::max());
207 
208   test<Context, unsigned, unsigned short>(0);
209   test<Context, unsigned, unsigned short>(std::numeric_limits<unsigned char>::max());
210   test<Context, unsigned, unsigned short>(std::numeric_limits<unsigned short>::max());
211 
212   test<Context, unsigned, unsigned>(0);
213   test<Context, unsigned, unsigned>(std::numeric_limits<unsigned char>::max());
214   test<Context, unsigned, unsigned>(std::numeric_limits<unsigned short>::max());
215   test<Context, unsigned, unsigned>(std::numeric_limits<unsigned>::max());
216 
217   using UnsignedLongToType =
218       std::conditional_t<sizeof(unsigned long) == sizeof(unsigned), unsigned, unsigned long long>;
219 
220   test<Context, UnsignedLongToType, unsigned long>(0);
221   test<Context, UnsignedLongToType, unsigned long>(std::numeric_limits<unsigned char>::max());
222   test<Context, UnsignedLongToType, unsigned long>(std::numeric_limits<unsigned short>::max());
223   test<Context, UnsignedLongToType, unsigned long>(std::numeric_limits<unsigned>::max());
224   test<Context, UnsignedLongToType, unsigned long>(std::numeric_limits<unsigned long>::max());
225 
226   test<Context, unsigned long long, unsigned long long>(0);
227   test<Context, unsigned long long, unsigned long long>(std::numeric_limits<unsigned char>::max());
228   test<Context, unsigned long long, unsigned long long>(std::numeric_limits<unsigned short>::max());
229   test<Context, unsigned long long, unsigned long long>(std::numeric_limits<unsigned>::max());
230   test<Context, unsigned long long, unsigned long long>(std::numeric_limits<unsigned long>::max());
231   test<Context, unsigned long long, unsigned long long>(std::numeric_limits<unsigned long long>::max());
232 
233 #ifndef TEST_HAS_NO_INT128
234   test_handle<Context, __uint128_t>(0);
235 #endif // TEST_HAS_NO_INT128
236 
237   // Test floating point types.
238 
239   test<Context, float, float>(-std::numeric_limits<float>::max());
240   test<Context, float, float>(-std::numeric_limits<float>::min());
241   test<Context, float, float>(-0.0);
242   test<Context, float, float>(0.0);
243   test<Context, float, float>(std::numeric_limits<float>::min());
244   test<Context, float, float>(std::numeric_limits<float>::max());
245 
246   test<Context, double, double>(-std::numeric_limits<double>::max());
247   test<Context, double, double>(-std::numeric_limits<double>::min());
248   test<Context, double, double>(-0.0);
249   test<Context, double, double>(0.0);
250   test<Context, double, double>(std::numeric_limits<double>::min());
251   test<Context, double, double>(std::numeric_limits<double>::max());
252 
253   test<Context, long double, long double>(-std::numeric_limits<long double>::max());
254   test<Context, long double, long double>(-std::numeric_limits<long double>::min());
255   test<Context, long double, long double>(-0.0);
256   test<Context, long double, long double>(0.0);
257   test<Context, long double, long double>(std::numeric_limits<long double>::min());
258   test<Context, long double, long double>(std::numeric_limits<long double>::max());
259 
260   // Test const CharT pointer types.
261 
262   test<Context, const CharT*, const CharT*>(empty.c_str());
263   test<Context, const CharT*, const CharT*>(str.c_str());
264 
265   // Test string_view types.
266 
267   {
268     using From = std::basic_string_view<CharT>;
269 
270     test_string_view<Context>(From());
271     test_string_view<Context>(From(empty.c_str()));
272     test_string_view<Context>(From(str.c_str()));
273   }
274 
275   {
276     using From = std::basic_string_view<CharT, constexpr_char_traits<CharT>>;
277 
278     test_string_view<Context>(From());
279     test_string_view<Context>(From(empty.c_str()));
280     test_string_view<Context>(From(str.c_str()));
281   }
282 
283   // Test string types.
284 
285   {
286     using From = std::basic_string<CharT>;
287 
288     test_string_view<Context>(From());
289     test_string_view<Context>(From(empty.c_str()));
290     test_string_view<Context>(From(str.c_str()));
291   }
292 
293   {
294     using From = std::basic_string<CharT, constexpr_char_traits<CharT>, std::allocator<CharT>>;
295 
296     test_string_view<Context>(From());
297     test_string_view<Context>(From(empty.c_str()));
298     test_string_view<Context>(From(str.c_str()));
299   }
300 
301   {
302     using From = std::basic_string<CharT, std::char_traits<CharT>, min_allocator<CharT>>;
303 
304     test_string_view<Context>(From());
305     test_string_view<Context>(From(empty.c_str()));
306     test_string_view<Context>(From(str.c_str()));
307   }
308 
309   {
310     using From = std::basic_string<CharT, constexpr_char_traits<CharT>, min_allocator<CharT>>;
311 
312     test_string_view<Context>(From());
313     test_string_view<Context>(From(empty.c_str()));
314     test_string_view<Context>(From(str.c_str()));
315   }
316 
317   // Test pointer types.
318 
319   test<Context, const void*>(nullptr);
320   int i = 0;
321   test<Context, const void*>(static_cast<void*>(&i));
322   const int ci = 0;
323   test<Context, const void*>(static_cast<const void*>(&ci));
324 }
325 
326 int main(int, char**) {
327   test<char>();
328 #ifndef TEST_HAS_NO_WIDE_CHARACTERS
329   test<wchar_t>();
330 #endif
331 
332   return 0;
333 }
334