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