1 // Copyright 2020 The Abseil Authors. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // https://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 #ifndef ABSL_STRINGS_INTERNAL_STR_FORMAT_ARG_H_ 16 #define ABSL_STRINGS_INTERNAL_STR_FORMAT_ARG_H_ 17 18 #include <string.h> 19 #include <wchar.h> 20 21 #include <algorithm> 22 #include <cstdio> 23 #include <iomanip> 24 #include <limits> 25 #include <memory> 26 #include <sstream> 27 #include <string> 28 #include <type_traits> 29 #include <utility> 30 31 #include "absl/base/port.h" 32 #include "absl/meta/type_traits.h" 33 #include "absl/numeric/int128.h" 34 #include "absl/strings/internal/has_absl_stringify.h" 35 #include "absl/strings/internal/str_format/extension.h" 36 #include "absl/strings/string_view.h" 37 38 namespace absl { 39 ABSL_NAMESPACE_BEGIN 40 41 class Cord; 42 class FormatCountCapture; 43 class FormatSink; 44 45 template <absl::FormatConversionCharSet C> 46 struct FormatConvertResult; 47 class FormatConversionSpec; 48 49 namespace str_format_internal { 50 51 template <FormatConversionCharSet C> 52 struct ArgConvertResult { 53 bool value; 54 }; 55 56 using IntegralConvertResult = ArgConvertResult<FormatConversionCharSetUnion( 57 FormatConversionCharSetInternal::c, 58 FormatConversionCharSetInternal::kNumeric, 59 FormatConversionCharSetInternal::kStar, 60 FormatConversionCharSetInternal::v)>; 61 using FloatingConvertResult = ArgConvertResult<FormatConversionCharSetUnion( 62 FormatConversionCharSetInternal::kFloating, 63 FormatConversionCharSetInternal::v)>; 64 using CharConvertResult = ArgConvertResult<FormatConversionCharSetUnion( 65 FormatConversionCharSetInternal::c, 66 FormatConversionCharSetInternal::kNumeric, 67 FormatConversionCharSetInternal::kStar)>; 68 69 template <typename T, typename = void> 70 struct HasUserDefinedConvert : std::false_type {}; 71 72 template <typename T> 73 struct HasUserDefinedConvert<T, void_t<decltype(AbslFormatConvert( 74 std::declval<const T&>(), 75 std::declval<const FormatConversionSpec&>(), 76 std::declval<FormatSink*>()))>> 77 : std::true_type {}; 78 79 // These declarations prevent ADL lookup from continuing in absl namespaces, 80 // we are deliberately using these as ADL hooks and want them to consider 81 // non-absl namespaces only. 82 void AbslFormatConvert(); 83 void AbslStringify(); 84 85 template <typename T> 86 bool ConvertIntArg(T v, FormatConversionSpecImpl conv, FormatSinkImpl* sink); 87 88 // Forward declarations of internal `ConvertIntArg` function template 89 // instantiations are here to avoid including the template body in the headers 90 // and instantiating it in large numbers of translation units. Explicit 91 // instantiations can be found in "absl/strings/internal/str_format/arg.cc" 92 extern template bool ConvertIntArg<char>(char v, FormatConversionSpecImpl conv, 93 FormatSinkImpl* sink); 94 extern template bool ConvertIntArg<signed char>(signed char v, 95 FormatConversionSpecImpl conv, 96 FormatSinkImpl* sink); 97 extern template bool ConvertIntArg<unsigned char>(unsigned char v, 98 FormatConversionSpecImpl conv, 99 FormatSinkImpl* sink); 100 extern template bool ConvertIntArg<short>(short v, // NOLINT 101 FormatConversionSpecImpl conv, 102 FormatSinkImpl* sink); 103 extern template bool ConvertIntArg<unsigned short>( // NOLINT 104 unsigned short v, FormatConversionSpecImpl conv, // NOLINT 105 FormatSinkImpl* sink); 106 extern template bool ConvertIntArg<int>(int v, FormatConversionSpecImpl conv, 107 FormatSinkImpl* sink); 108 extern template bool ConvertIntArg<unsigned int>(unsigned int v, 109 FormatConversionSpecImpl conv, 110 FormatSinkImpl* sink); 111 extern template bool ConvertIntArg<long>( // NOLINT 112 long v, FormatConversionSpecImpl conv, FormatSinkImpl* sink); // NOLINT 113 extern template bool ConvertIntArg<unsigned long>(unsigned long v, // NOLINT 114 FormatConversionSpecImpl conv, 115 FormatSinkImpl* sink); 116 extern template bool ConvertIntArg<long long>(long long v, // NOLINT 117 FormatConversionSpecImpl conv, 118 FormatSinkImpl* sink); 119 extern template bool ConvertIntArg<unsigned long long>( // NOLINT 120 unsigned long long v, FormatConversionSpecImpl conv, // NOLINT 121 FormatSinkImpl* sink); 122 123 template <typename T> 124 auto FormatConvertImpl(const T& v, FormatConversionSpecImpl conv, 125 FormatSinkImpl* sink) 126 -> decltype(AbslFormatConvert(v, 127 std::declval<const FormatConversionSpec&>(), 128 std::declval<FormatSink*>())) { 129 using FormatConversionSpecT = 130 absl::enable_if_t<sizeof(const T& (*)()) != 0, FormatConversionSpec>; 131 using FormatSinkT = 132 absl::enable_if_t<sizeof(const T& (*)()) != 0, FormatSink>; 133 auto fcs = conv.Wrap<FormatConversionSpecT>(); 134 auto fs = sink->Wrap<FormatSinkT>(); 135 return AbslFormatConvert(v, fcs, &fs); 136 } 137 138 template <typename T> 139 auto FormatConvertImpl(const T& v, FormatConversionSpecImpl conv, 140 FormatSinkImpl* sink) 141 -> std::enable_if_t<std::is_enum<T>::value && 142 std::is_void<decltype(AbslStringify( 143 std::declval<FormatSink&>(), v))>::value, 144 IntegralConvertResult> { 145 if (conv.conversion_char() == FormatConversionCharInternal::v) { 146 using FormatSinkT = 147 absl::enable_if_t<sizeof(const T& (*)()) != 0, FormatSink>; 148 auto fs = sink->Wrap<FormatSinkT>(); 149 AbslStringify(fs, v); 150 return {true}; 151 } else { 152 return {ConvertIntArg( 153 static_cast<typename std::underlying_type<T>::type>(v), conv, sink)}; 154 } 155 } 156 157 template <typename T> 158 auto FormatConvertImpl(const T& v, FormatConversionSpecImpl, 159 FormatSinkImpl* sink) 160 -> std::enable_if_t<!std::is_enum<T>::value && 161 std::is_void<decltype(AbslStringify( 162 std::declval<FormatSink&>(), v))>::value, 163 ArgConvertResult<FormatConversionCharSetInternal::v>> { 164 using FormatSinkT = 165 absl::enable_if_t<sizeof(const T& (*)()) != 0, FormatSink>; 166 auto fs = sink->Wrap<FormatSinkT>(); 167 AbslStringify(fs, v); 168 return {true}; 169 } 170 171 template <typename T> 172 class StreamedWrapper; 173 174 // If 'v' can be converted (in the printf sense) according to 'conv', 175 // then convert it, appending to `sink` and return `true`. 176 // Otherwise fail and return `false`. 177 178 // AbslFormatConvert(v, conv, sink) is intended to be found by ADL on 'v' 179 // as an extension mechanism. These FormatConvertImpl functions are the default 180 // implementations. 181 // The ADL search is augmented via the 'Sink*' parameter, which also 182 // serves as a disambiguator to reject possible unintended 'AbslFormatConvert' 183 // functions in the namespaces associated with 'v'. 184 185 // Raw pointers. 186 struct VoidPtr { 187 VoidPtr() = default; 188 template <typename T, 189 decltype(reinterpret_cast<uintptr_t>(std::declval<T*>())) = 0> 190 VoidPtr(T* ptr) // NOLINT 191 : value(ptr ? reinterpret_cast<uintptr_t>(ptr) : 0) {} 192 uintptr_t value; 193 }; 194 195 template <FormatConversionCharSet C> 196 constexpr FormatConversionCharSet ExtractCharSet(FormatConvertResult<C>) { 197 return C; 198 } 199 200 template <FormatConversionCharSet C> 201 constexpr FormatConversionCharSet ExtractCharSet(ArgConvertResult<C>) { 202 return C; 203 } 204 205 using StringConvertResult = ArgConvertResult<FormatConversionCharSetUnion( 206 FormatConversionCharSetInternal::s, FormatConversionCharSetInternal::v)>; 207 ArgConvertResult<FormatConversionCharSetInternal::p> FormatConvertImpl( 208 VoidPtr v, FormatConversionSpecImpl conv, FormatSinkImpl* sink); 209 210 // Strings. 211 StringConvertResult FormatConvertImpl(const std::string& v, 212 FormatConversionSpecImpl conv, 213 FormatSinkImpl* sink); 214 StringConvertResult FormatConvertImpl(string_view v, 215 FormatConversionSpecImpl conv, 216 FormatSinkImpl* sink); 217 #if defined(ABSL_HAVE_STD_STRING_VIEW) && !defined(ABSL_USES_STD_STRING_VIEW) 218 inline StringConvertResult FormatConvertImpl(std::string_view v, 219 FormatConversionSpecImpl conv, 220 FormatSinkImpl* sink) { 221 return FormatConvertImpl(absl::string_view(v.data(), v.size()), conv, sink); 222 } 223 #endif // ABSL_HAVE_STD_STRING_VIEW && !ABSL_USES_STD_STRING_VIEW 224 225 ArgConvertResult<FormatConversionCharSetUnion( 226 FormatConversionCharSetInternal::s, FormatConversionCharSetInternal::p)> 227 FormatConvertImpl(const char* v, const FormatConversionSpecImpl conv, 228 FormatSinkImpl* sink); 229 230 template <class AbslCord, typename std::enable_if<std::is_same< 231 AbslCord, absl::Cord>::value>::type* = nullptr> 232 StringConvertResult FormatConvertImpl(const AbslCord& value, 233 FormatConversionSpecImpl conv, 234 FormatSinkImpl* sink) { 235 bool is_left = conv.has_left_flag(); 236 size_t space_remaining = 0; 237 238 int width = conv.width(); 239 if (width >= 0) space_remaining = static_cast<size_t>(width); 240 241 size_t to_write = value.size(); 242 243 int precision = conv.precision(); 244 if (precision >= 0) 245 to_write = (std::min)(to_write, static_cast<size_t>(precision)); 246 247 space_remaining = Excess(to_write, space_remaining); 248 249 if (space_remaining > 0 && !is_left) sink->Append(space_remaining, ' '); 250 251 for (string_view piece : value.Chunks()) { 252 if (piece.size() > to_write) { 253 piece.remove_suffix(piece.size() - to_write); 254 to_write = 0; 255 } else { 256 to_write -= piece.size(); 257 } 258 sink->Append(piece); 259 if (to_write == 0) { 260 break; 261 } 262 } 263 264 if (space_remaining > 0 && is_left) sink->Append(space_remaining, ' '); 265 return {true}; 266 } 267 268 bool ConvertBoolArg(bool v, FormatSinkImpl* sink); 269 270 // Floats. 271 FloatingConvertResult FormatConvertImpl(float v, FormatConversionSpecImpl conv, 272 FormatSinkImpl* sink); 273 FloatingConvertResult FormatConvertImpl(double v, FormatConversionSpecImpl conv, 274 FormatSinkImpl* sink); 275 FloatingConvertResult FormatConvertImpl(long double v, 276 FormatConversionSpecImpl conv, 277 FormatSinkImpl* sink); 278 279 // Chars. 280 CharConvertResult FormatConvertImpl(char v, FormatConversionSpecImpl conv, 281 FormatSinkImpl* sink); 282 CharConvertResult FormatConvertImpl(signed char v, 283 FormatConversionSpecImpl conv, 284 FormatSinkImpl* sink); 285 CharConvertResult FormatConvertImpl(unsigned char v, 286 FormatConversionSpecImpl conv, 287 FormatSinkImpl* sink); 288 289 // Ints. 290 IntegralConvertResult FormatConvertImpl(short v, // NOLINT 291 FormatConversionSpecImpl conv, 292 FormatSinkImpl* sink); 293 IntegralConvertResult FormatConvertImpl(unsigned short v, // NOLINT 294 FormatConversionSpecImpl conv, 295 FormatSinkImpl* sink); 296 IntegralConvertResult FormatConvertImpl(int v, FormatConversionSpecImpl conv, 297 FormatSinkImpl* sink); 298 IntegralConvertResult FormatConvertImpl(unsigned v, 299 FormatConversionSpecImpl conv, 300 FormatSinkImpl* sink); 301 IntegralConvertResult FormatConvertImpl(long v, // NOLINT 302 FormatConversionSpecImpl conv, 303 FormatSinkImpl* sink); 304 IntegralConvertResult FormatConvertImpl(unsigned long v, // NOLINT 305 FormatConversionSpecImpl conv, 306 FormatSinkImpl* sink); 307 IntegralConvertResult FormatConvertImpl(long long v, // NOLINT 308 FormatConversionSpecImpl conv, 309 FormatSinkImpl* sink); 310 IntegralConvertResult FormatConvertImpl(unsigned long long v, // NOLINT 311 FormatConversionSpecImpl conv, 312 FormatSinkImpl* sink); 313 IntegralConvertResult FormatConvertImpl(int128 v, FormatConversionSpecImpl conv, 314 FormatSinkImpl* sink); 315 IntegralConvertResult FormatConvertImpl(uint128 v, 316 FormatConversionSpecImpl conv, 317 FormatSinkImpl* sink); 318 319 // This function needs to be a template due to ambiguity regarding type 320 // conversions. 321 template <typename T, enable_if_t<std::is_same<T, bool>::value, int> = 0> 322 IntegralConvertResult FormatConvertImpl(T v, FormatConversionSpecImpl conv, 323 FormatSinkImpl* sink) { 324 if (conv.conversion_char() == FormatConversionCharInternal::v) { 325 return {ConvertBoolArg(v, sink)}; 326 } 327 328 return FormatConvertImpl(static_cast<int>(v), conv, sink); 329 } 330 331 // We provide this function to help the checker, but it is never defined. 332 // FormatArgImpl will use the underlying Convert functions instead. 333 template <typename T> 334 typename std::enable_if<std::is_enum<T>::value && 335 !HasUserDefinedConvert<T>::value && 336 !strings_internal::HasAbslStringify<T>::value, 337 IntegralConvertResult>::type 338 FormatConvertImpl(T v, FormatConversionSpecImpl conv, FormatSinkImpl* sink); 339 340 template <typename T> 341 StringConvertResult FormatConvertImpl(const StreamedWrapper<T>& v, 342 FormatConversionSpecImpl conv, 343 FormatSinkImpl* out) { 344 std::ostringstream oss; 345 oss << v.v_; 346 if (!oss) return {false}; 347 return str_format_internal::FormatConvertImpl(oss.str(), conv, out); 348 } 349 350 // Use templates and dependent types to delay evaluation of the function 351 // until after FormatCountCapture is fully defined. 352 struct FormatCountCaptureHelper { 353 template <class T = int> 354 static ArgConvertResult<FormatConversionCharSetInternal::n> ConvertHelper( 355 const FormatCountCapture& v, FormatConversionSpecImpl conv, 356 FormatSinkImpl* sink) { 357 const absl::enable_if_t<sizeof(T) != 0, FormatCountCapture>& v2 = v; 358 359 if (conv.conversion_char() != 360 str_format_internal::FormatConversionCharInternal::n) { 361 return {false}; 362 } 363 *v2.p_ = static_cast<int>(sink->size()); 364 return {true}; 365 } 366 }; 367 368 template <class T = int> 369 ArgConvertResult<FormatConversionCharSetInternal::n> FormatConvertImpl( 370 const FormatCountCapture& v, FormatConversionSpecImpl conv, 371 FormatSinkImpl* sink) { 372 return FormatCountCaptureHelper::ConvertHelper(v, conv, sink); 373 } 374 375 // Helper friend struct to hide implementation details from the public API of 376 // FormatArgImpl. 377 struct FormatArgImplFriend { 378 template <typename Arg> 379 static bool ToInt(Arg arg, int* out) { 380 // A value initialized FormatConversionSpecImpl has a `none` conv, which 381 // tells the dispatcher to run the `int` conversion. 382 return arg.dispatcher_(arg.data_, {}, out); 383 } 384 385 template <typename Arg> 386 static bool Convert(Arg arg, FormatConversionSpecImpl conv, 387 FormatSinkImpl* out) { 388 return arg.dispatcher_(arg.data_, conv, out); 389 } 390 391 template <typename Arg> 392 static typename Arg::Dispatcher GetVTablePtrForTest(Arg arg) { 393 return arg.dispatcher_; 394 } 395 }; 396 397 template <typename Arg> 398 constexpr FormatConversionCharSet ArgumentToConv() { 399 using ConvResult = decltype(str_format_internal::FormatConvertImpl( 400 std::declval<const Arg&>(), 401 std::declval<const FormatConversionSpecImpl&>(), 402 std::declval<FormatSinkImpl*>())); 403 return absl::str_format_internal::ExtractCharSet(ConvResult{}); 404 } 405 406 // A type-erased handle to a format argument. 407 class FormatArgImpl { 408 private: 409 enum { kInlinedSpace = 8 }; 410 411 using VoidPtr = str_format_internal::VoidPtr; 412 413 union Data { 414 const void* ptr; 415 const volatile void* volatile_ptr; 416 char buf[kInlinedSpace]; 417 }; 418 419 using Dispatcher = bool (*)(Data, FormatConversionSpecImpl, void* out); 420 421 template <typename T> 422 struct store_by_value 423 : std::integral_constant<bool, (sizeof(T) <= kInlinedSpace) && 424 (std::is_integral<T>::value || 425 std::is_floating_point<T>::value || 426 std::is_pointer<T>::value || 427 std::is_same<VoidPtr, T>::value)> {}; 428 429 enum StoragePolicy { ByPointer, ByVolatilePointer, ByValue }; 430 template <typename T> 431 struct storage_policy 432 : std::integral_constant<StoragePolicy, 433 (std::is_volatile<T>::value 434 ? ByVolatilePointer 435 : (store_by_value<T>::value ? ByValue 436 : ByPointer))> { 437 }; 438 439 // To reduce the number of vtables we will decay values before hand. 440 // Anything with a user-defined Convert will get its own vtable. 441 // For everything else: 442 // - Decay char* and char arrays into `const char*` 443 // - Decay any other pointer to `const void*` 444 // - Decay all enums to their underlying type. 445 // - Decay function pointers to void*. 446 template <typename T, typename = void> 447 struct DecayType { 448 static constexpr bool kHasUserDefined = 449 str_format_internal::HasUserDefinedConvert<T>::value || 450 strings_internal::HasAbslStringify<T>::value; 451 using type = typename std::conditional< 452 !kHasUserDefined && std::is_convertible<T, const char*>::value, 453 const char*, 454 typename std::conditional<!kHasUserDefined && 455 std::is_convertible<T, VoidPtr>::value, 456 VoidPtr, const T&>::type>::type; 457 }; 458 template <typename T> 459 struct DecayType<T, 460 typename std::enable_if< 461 !str_format_internal::HasUserDefinedConvert<T>::value && 462 !strings_internal::HasAbslStringify<T>::value && 463 std::is_enum<T>::value>::type> { 464 using type = typename std::underlying_type<T>::type; 465 }; 466 467 public: 468 template <typename T> 469 explicit FormatArgImpl(const T& value) { 470 using D = typename DecayType<T>::type; 471 static_assert( 472 std::is_same<D, const T&>::value || storage_policy<D>::value == ByValue, 473 "Decayed types must be stored by value"); 474 Init(static_cast<D>(value)); 475 } 476 477 private: 478 friend struct str_format_internal::FormatArgImplFriend; 479 template <typename T, StoragePolicy = storage_policy<T>::value> 480 struct Manager; 481 482 template <typename T> 483 struct Manager<T, ByPointer> { 484 static Data SetValue(const T& value) { 485 Data data; 486 data.ptr = std::addressof(value); 487 return data; 488 } 489 490 static const T& Value(Data arg) { return *static_cast<const T*>(arg.ptr); } 491 }; 492 493 template <typename T> 494 struct Manager<T, ByVolatilePointer> { 495 static Data SetValue(const T& value) { 496 Data data; 497 data.volatile_ptr = &value; 498 return data; 499 } 500 501 static const T& Value(Data arg) { 502 return *static_cast<const T*>(arg.volatile_ptr); 503 } 504 }; 505 506 template <typename T> 507 struct Manager<T, ByValue> { 508 static Data SetValue(const T& value) { 509 Data data; 510 memcpy(data.buf, &value, sizeof(value)); 511 return data; 512 } 513 514 static T Value(Data arg) { 515 T value; 516 memcpy(&value, arg.buf, sizeof(T)); 517 return value; 518 } 519 }; 520 521 template <typename T> 522 void Init(const T& value) { 523 data_ = Manager<T>::SetValue(value); 524 dispatcher_ = &Dispatch<T>; 525 } 526 527 template <typename T> 528 static int ToIntVal(const T& val) { 529 using CommonType = typename std::conditional<std::is_signed<T>::value, 530 int64_t, uint64_t>::type; 531 if (static_cast<CommonType>(val) > 532 static_cast<CommonType>((std::numeric_limits<int>::max)())) { 533 return (std::numeric_limits<int>::max)(); 534 } else if (std::is_signed<T>::value && 535 static_cast<CommonType>(val) < 536 static_cast<CommonType>((std::numeric_limits<int>::min)())) { 537 return (std::numeric_limits<int>::min)(); 538 } 539 return static_cast<int>(val); 540 } 541 542 template <typename T> 543 static bool ToInt(Data arg, int* out, std::true_type /* is_integral */, 544 std::false_type) { 545 *out = ToIntVal(Manager<T>::Value(arg)); 546 return true; 547 } 548 549 template <typename T> 550 static bool ToInt(Data arg, int* out, std::false_type, 551 std::true_type /* is_enum */) { 552 *out = ToIntVal(static_cast<typename std::underlying_type<T>::type>( 553 Manager<T>::Value(arg))); 554 return true; 555 } 556 557 template <typename T> 558 static bool ToInt(Data, int*, std::false_type, std::false_type) { 559 return false; 560 } 561 562 template <typename T> 563 static bool Dispatch(Data arg, FormatConversionSpecImpl spec, void* out) { 564 // A `none` conv indicates that we want the `int` conversion. 565 if (ABSL_PREDICT_FALSE(spec.conversion_char() == 566 FormatConversionCharInternal::kNone)) { 567 return ToInt<T>(arg, static_cast<int*>(out), std::is_integral<T>(), 568 std::is_enum<T>()); 569 } 570 if (ABSL_PREDICT_FALSE(!Contains(ArgumentToConv<T>(), 571 spec.conversion_char()))) { 572 return false; 573 } 574 return str_format_internal::FormatConvertImpl( 575 Manager<T>::Value(arg), spec, 576 static_cast<FormatSinkImpl*>(out)) 577 .value; 578 } 579 580 Data data_; 581 Dispatcher dispatcher_; 582 }; 583 584 #define ABSL_INTERNAL_FORMAT_DISPATCH_INSTANTIATE_(T, E) \ 585 E template bool FormatArgImpl::Dispatch<T>(Data, FormatConversionSpecImpl, \ 586 void*) 587 588 #define ABSL_INTERNAL_FORMAT_DISPATCH_OVERLOADS_EXPAND_(...) \ 589 ABSL_INTERNAL_FORMAT_DISPATCH_INSTANTIATE_(str_format_internal::VoidPtr, \ 590 __VA_ARGS__); \ 591 ABSL_INTERNAL_FORMAT_DISPATCH_INSTANTIATE_(bool, __VA_ARGS__); \ 592 ABSL_INTERNAL_FORMAT_DISPATCH_INSTANTIATE_(char, __VA_ARGS__); \ 593 ABSL_INTERNAL_FORMAT_DISPATCH_INSTANTIATE_(signed char, __VA_ARGS__); \ 594 ABSL_INTERNAL_FORMAT_DISPATCH_INSTANTIATE_(unsigned char, __VA_ARGS__); \ 595 ABSL_INTERNAL_FORMAT_DISPATCH_INSTANTIATE_(short, __VA_ARGS__); /* NOLINT */ \ 596 ABSL_INTERNAL_FORMAT_DISPATCH_INSTANTIATE_(unsigned short, /* NOLINT */ \ 597 __VA_ARGS__); \ 598 ABSL_INTERNAL_FORMAT_DISPATCH_INSTANTIATE_(int, __VA_ARGS__); \ 599 ABSL_INTERNAL_FORMAT_DISPATCH_INSTANTIATE_(unsigned int, __VA_ARGS__); \ 600 ABSL_INTERNAL_FORMAT_DISPATCH_INSTANTIATE_(long, __VA_ARGS__); /* NOLINT */ \ 601 ABSL_INTERNAL_FORMAT_DISPATCH_INSTANTIATE_(unsigned long, /* NOLINT */ \ 602 __VA_ARGS__); \ 603 ABSL_INTERNAL_FORMAT_DISPATCH_INSTANTIATE_(long long, /* NOLINT */ \ 604 __VA_ARGS__); \ 605 ABSL_INTERNAL_FORMAT_DISPATCH_INSTANTIATE_(unsigned long long, /* NOLINT */ \ 606 __VA_ARGS__); \ 607 ABSL_INTERNAL_FORMAT_DISPATCH_INSTANTIATE_(int128, __VA_ARGS__); \ 608 ABSL_INTERNAL_FORMAT_DISPATCH_INSTANTIATE_(uint128, __VA_ARGS__); \ 609 ABSL_INTERNAL_FORMAT_DISPATCH_INSTANTIATE_(float, __VA_ARGS__); \ 610 ABSL_INTERNAL_FORMAT_DISPATCH_INSTANTIATE_(double, __VA_ARGS__); \ 611 ABSL_INTERNAL_FORMAT_DISPATCH_INSTANTIATE_(long double, __VA_ARGS__); \ 612 ABSL_INTERNAL_FORMAT_DISPATCH_INSTANTIATE_(const char*, __VA_ARGS__); \ 613 ABSL_INTERNAL_FORMAT_DISPATCH_INSTANTIATE_(std::string, __VA_ARGS__); \ 614 ABSL_INTERNAL_FORMAT_DISPATCH_INSTANTIATE_(string_view, __VA_ARGS__) 615 616 ABSL_INTERNAL_FORMAT_DISPATCH_OVERLOADS_EXPAND_(extern); 617 618 619 } // namespace str_format_internal 620 ABSL_NAMESPACE_END 621 } // namespace absl 622 623 #endif // ABSL_STRINGS_INTERNAL_STR_FORMAT_ARG_H_ 624