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