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