1//===----------------------------------------------------------------------===//
2// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
3// See https://llvm.org/LICENSE.txt for license information.
4// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
5//
6//===----------------------------------------------------------------------===//
7
8// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20, c++23
9// UNSUPPORTED: GCC-ALWAYS_INLINE-FIXME
10// The tested functionality needs deducing this.
11// UNSUPPORTED: clang-17
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
31template <class Context, class To, class From>
32void 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.
55template <class Context, class T>
56void 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.
72template <class Context, class From>
73void 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
97template <class CharT>
98void 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
326int 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

source code of libcxx/test/std/utilities/format/format.arguments/format.arg/visit.pass.cpp