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