about summary refs log tree commit diff
path: root/absl/strings/internal/str_format
diff options
context:
space:
mode:
Diffstat (limited to 'absl/strings/internal/str_format')
-rw-r--r--absl/strings/internal/str_format/arg.cc80
-rw-r--r--absl/strings/internal/str_format/arg.h210
-rw-r--r--absl/strings/internal/str_format/bind.h2
-rw-r--r--absl/strings/internal/str_format/extension.h10
4 files changed, 136 insertions, 166 deletions
diff --git a/absl/strings/internal/str_format/arg.cc b/absl/strings/internal/str_format/arg.cc
index eafb068fe286..b40be8ff3824 100644
--- a/absl/strings/internal/str_format/arg.cc
+++ b/absl/strings/internal/str_format/arg.cc
@@ -112,7 +112,7 @@ class ConvertedIntInfo {
 // Note: 'o' conversions do not have a base indicator, it's just that
 // the '#' flag is specified to modify the precision for 'o' conversions.
 string_view BaseIndicator(const ConvertedIntInfo &info,
-                          const ConversionSpec &conv) {
+                          const ConversionSpec conv) {
   bool alt = conv.flags().alt;
   int radix = conv.conv().radix();
   if (conv.conv().id() == ConversionChar::p)
@@ -127,7 +127,7 @@ string_view BaseIndicator(const ConvertedIntInfo &info,
   return {};
 }
 
-string_view SignColumn(bool neg, const ConversionSpec &conv) {
+string_view SignColumn(bool neg, const ConversionSpec conv) {
   if (conv.conv().is_signed()) {
     if (neg) return "-";
     if (conv.flags().show_pos) return "+";
@@ -136,7 +136,7 @@ string_view SignColumn(bool neg, const ConversionSpec &conv) {
   return {};
 }
 
-bool ConvertCharImpl(unsigned char v, const ConversionSpec &conv,
+bool ConvertCharImpl(unsigned char v, const ConversionSpec conv,
                      FormatSinkImpl *sink) {
   size_t fill = 0;
   if (conv.width() >= 0) fill = conv.width();
@@ -148,7 +148,7 @@ bool ConvertCharImpl(unsigned char v, const ConversionSpec &conv,
 }
 
 bool ConvertIntImplInner(const ConvertedIntInfo &info,
-                         const ConversionSpec &conv, FormatSinkImpl *sink) {
+                         const ConversionSpec conv, FormatSinkImpl *sink) {
   // Print as a sequence of Substrings:
   //   [left_spaces][sign][base_indicator][zeroes][formatted][right_spaces]
   size_t fill = 0;
@@ -202,8 +202,7 @@ bool ConvertIntImplInner(const ConvertedIntInfo &info,
 }
 
 template <typename T>
-bool ConvertIntImplInner(T v, const ConversionSpec &conv,
-                         FormatSinkImpl *sink) {
+bool ConvertIntImplInner(T v, const ConversionSpec conv, FormatSinkImpl *sink) {
   ConvertedIntInfo info(v, conv.conv());
   if (conv.flags().basic && conv.conv().id() != ConversionChar::p) {
     if (info.is_neg()) sink->Append(1, '-');
@@ -218,7 +217,7 @@ bool ConvertIntImplInner(T v, const ConversionSpec &conv,
 }
 
 template <typename T>
-bool ConvertIntArg(T v, const ConversionSpec &conv, FormatSinkImpl *sink) {
+bool ConvertIntArg(T v, const ConversionSpec conv, FormatSinkImpl *sink) {
   if (conv.conv().is_float()) {
     return FormatConvertImpl(static_cast<double>(v), conv, sink).value;
   }
@@ -234,11 +233,11 @@ bool ConvertIntArg(T v, const ConversionSpec &conv, FormatSinkImpl *sink) {
 }
 
 template <typename T>
-bool ConvertFloatArg(T v, const ConversionSpec &conv, FormatSinkImpl *sink) {
+bool ConvertFloatArg(T v, const ConversionSpec conv, FormatSinkImpl *sink) {
   return conv.conv().is_float() && ConvertFloatImpl(v, conv, sink);
 }
 
-inline bool ConvertStringArg(string_view v, const ConversionSpec &conv,
+inline bool ConvertStringArg(string_view v, const ConversionSpec conv,
                              FormatSinkImpl *sink) {
   if (conv.conv().id() != ConversionChar::s)
     return false;
@@ -254,19 +253,19 @@ inline bool ConvertStringArg(string_view v, const ConversionSpec &conv,
 
 // ==================== Strings ====================
 ConvertResult<Conv::s> FormatConvertImpl(const std::string &v,
-                                         const ConversionSpec &conv,
+                                         const ConversionSpec conv,
                                          FormatSinkImpl *sink) {
   return {ConvertStringArg(v, conv, sink)};
 }
 
 ConvertResult<Conv::s> FormatConvertImpl(string_view v,
-                                         const ConversionSpec &conv,
+                                         const ConversionSpec conv,
                                          FormatSinkImpl *sink) {
   return {ConvertStringArg(v, conv, sink)};
 }
 
 ConvertResult<Conv::s | Conv::p> FormatConvertImpl(const char *v,
-                                                   const ConversionSpec &conv,
+                                                   const ConversionSpec conv,
                                                    FormatSinkImpl *sink) {
   if (conv.conv().id() == ConversionChar::p)
     return {FormatConvertImpl(VoidPtr(v), conv, sink).value};
@@ -283,7 +282,7 @@ ConvertResult<Conv::s | Conv::p> FormatConvertImpl(const char *v,
 }
 
 // ==================== Raw pointers ====================
-ConvertResult<Conv::p> FormatConvertImpl(VoidPtr v, const ConversionSpec &conv,
+ConvertResult<Conv::p> FormatConvertImpl(VoidPtr v, const ConversionSpec conv,
                                          FormatSinkImpl *sink) {
   if (conv.conv().id() != ConversionChar::p)
     return {false};
@@ -295,104 +294,83 @@ ConvertResult<Conv::p> FormatConvertImpl(VoidPtr v, const ConversionSpec &conv,
 }
 
 // ==================== Floats ====================
-FloatingConvertResult FormatConvertImpl(float v, const ConversionSpec &conv,
+FloatingConvertResult FormatConvertImpl(float v, const ConversionSpec conv,
                                         FormatSinkImpl *sink) {
   return {ConvertFloatArg(v, conv, sink)};
 }
-FloatingConvertResult FormatConvertImpl(double v, const ConversionSpec &conv,
+FloatingConvertResult FormatConvertImpl(double v, const ConversionSpec conv,
                                         FormatSinkImpl *sink) {
   return {ConvertFloatArg(v, conv, sink)};
 }
 FloatingConvertResult FormatConvertImpl(long double v,
-                                        const ConversionSpec &conv,
+                                        const ConversionSpec conv,
                                         FormatSinkImpl *sink) {
   return {ConvertFloatArg(v, conv, sink)};
 }
 
 // ==================== Chars ====================
-IntegralConvertResult FormatConvertImpl(char v, const ConversionSpec &conv,
+IntegralConvertResult FormatConvertImpl(char v, const ConversionSpec conv,
                                         FormatSinkImpl *sink) {
   return {ConvertIntArg(v, conv, sink)};
 }
 IntegralConvertResult FormatConvertImpl(signed char v,
-                                        const ConversionSpec &conv,
+                                        const ConversionSpec conv,
                                         FormatSinkImpl *sink) {
   return {ConvertIntArg(v, conv, sink)};
 }
 IntegralConvertResult FormatConvertImpl(unsigned char v,
-                                        const ConversionSpec &conv,
+                                        const ConversionSpec conv,
                                         FormatSinkImpl *sink) {
   return {ConvertIntArg(v, conv, sink)};
 }
 
 // ==================== Ints ====================
 IntegralConvertResult FormatConvertImpl(short v,  // NOLINT
-                                        const ConversionSpec &conv,
+                                        const ConversionSpec conv,
                                         FormatSinkImpl *sink) {
   return {ConvertIntArg(v, conv, sink)};
 }
 IntegralConvertResult FormatConvertImpl(unsigned short v,  // NOLINT
-                                        const ConversionSpec &conv,
+                                        const ConversionSpec conv,
                                         FormatSinkImpl *sink) {
   return {ConvertIntArg(v, conv, sink)};
 }
-IntegralConvertResult FormatConvertImpl(int v, const ConversionSpec &conv,
+IntegralConvertResult FormatConvertImpl(int v, const ConversionSpec conv,
                                         FormatSinkImpl *sink) {
   return {ConvertIntArg(v, conv, sink)};
 }
-IntegralConvertResult FormatConvertImpl(unsigned v, const ConversionSpec &conv,
+IntegralConvertResult FormatConvertImpl(unsigned v, const ConversionSpec conv,
                                         FormatSinkImpl *sink) {
   return {ConvertIntArg(v, conv, sink)};
 }
 IntegralConvertResult FormatConvertImpl(long v,  // NOLINT
-                                        const ConversionSpec &conv,
+                                        const ConversionSpec conv,
                                         FormatSinkImpl *sink) {
   return {ConvertIntArg(v, conv, sink)};
 }
 IntegralConvertResult FormatConvertImpl(unsigned long v,  // NOLINT
-                                        const ConversionSpec &conv,
+                                        const ConversionSpec conv,
                                         FormatSinkImpl *sink) {
   return {ConvertIntArg(v, conv, sink)};
 }
 IntegralConvertResult FormatConvertImpl(long long v,  // NOLINT
-                                        const ConversionSpec &conv,
+                                        const ConversionSpec conv,
                                         FormatSinkImpl *sink) {
   return {ConvertIntArg(v, conv, sink)};
 }
 IntegralConvertResult FormatConvertImpl(unsigned long long v,  // NOLINT
-                                        const ConversionSpec &conv,
+                                        const ConversionSpec conv,
                                         FormatSinkImpl *sink) {
   return {ConvertIntArg(v, conv, sink)};
 }
 IntegralConvertResult FormatConvertImpl(absl::uint128 v,
-                                        const ConversionSpec &conv,
+                                        const ConversionSpec conv,
                                         FormatSinkImpl *sink) {
   return {ConvertIntArg(v, conv, sink)};
 }
 
-template struct FormatArgImpl::TypedVTable<str_format_internal::VoidPtr>;
-
-template struct FormatArgImpl::TypedVTable<bool>;
-template struct FormatArgImpl::TypedVTable<char>;
-template struct FormatArgImpl::TypedVTable<signed char>;
-template struct FormatArgImpl::TypedVTable<unsigned char>;
-template struct FormatArgImpl::TypedVTable<short>;           // NOLINT
-template struct FormatArgImpl::TypedVTable<unsigned short>;  // NOLINT
-template struct FormatArgImpl::TypedVTable<int>;
-template struct FormatArgImpl::TypedVTable<unsigned>;
-template struct FormatArgImpl::TypedVTable<long>;                // NOLINT
-template struct FormatArgImpl::TypedVTable<unsigned long>;       // NOLINT
-template struct FormatArgImpl::TypedVTable<long long>;           // NOLINT
-template struct FormatArgImpl::TypedVTable<unsigned long long>;  // NOLINT
-template struct FormatArgImpl::TypedVTable<absl::uint128>;
-
-template struct FormatArgImpl::TypedVTable<float>;
-template struct FormatArgImpl::TypedVTable<double>;
-template struct FormatArgImpl::TypedVTable<long double>;
-
-template struct FormatArgImpl::TypedVTable<const char *>;
-template struct FormatArgImpl::TypedVTable<std::string>;
-template struct FormatArgImpl::TypedVTable<string_view>;
+ABSL_INTERNAL_FORMAT_DISPATCH_OVERLOADS_EXPAND_();
+
 
 }  // namespace str_format_internal
 
diff --git a/absl/strings/internal/str_format/arg.h b/absl/strings/internal/str_format/arg.h
index a9562188ea91..3376d48afda2 100644
--- a/absl/strings/internal/str_format/arg.h
+++ b/absl/strings/internal/str_format/arg.h
@@ -33,7 +33,7 @@ struct HasUserDefinedConvert : std::false_type {};
 template <typename T>
 struct HasUserDefinedConvert<
     T, void_t<decltype(AbslFormatConvert(
-           std::declval<const T&>(), std::declval<const ConversionSpec&>(),
+           std::declval<const T&>(), std::declval<ConversionSpec>(),
            std::declval<FormatSink*>()))>> : std::true_type {};
 template <typename T>
 class StreamedWrapper;
@@ -50,25 +50,23 @@ struct VoidPtr {
       : value(ptr ? reinterpret_cast<uintptr_t>(ptr) : 0) {}
   uintptr_t value;
 };
-ConvertResult<Conv::p> FormatConvertImpl(VoidPtr v, const ConversionSpec& conv,
+ConvertResult<Conv::p> FormatConvertImpl(VoidPtr v, ConversionSpec conv,
                                          FormatSinkImpl* sink);
 
 // Strings.
-ConvertResult<Conv::s> FormatConvertImpl(const std::string& v,
-                                         const ConversionSpec& conv,
+ConvertResult<Conv::s> FormatConvertImpl(const std::string& v, ConversionSpec conv,
                                          FormatSinkImpl* sink);
-ConvertResult<Conv::s> FormatConvertImpl(string_view v,
-                                         const ConversionSpec& conv,
+ConvertResult<Conv::s> FormatConvertImpl(string_view v, ConversionSpec conv,
                                          FormatSinkImpl* sink);
 ConvertResult<Conv::s | Conv::p> FormatConvertImpl(const char* v,
-                                                   const ConversionSpec& conv,
+                                                   ConversionSpec conv,
                                                    FormatSinkImpl* sink);
 template <class AbslCord,
           typename std::enable_if<
               std::is_same<AbslCord, ::Cord>::value>::type* = nullptr,
           class AbslCordReader = ::CordReader>
 ConvertResult<Conv::s> FormatConvertImpl(const AbslCord& value,
-                                         const ConversionSpec& conv,
+                                         ConversionSpec conv,
                                          FormatSinkImpl* sink) {
   if (conv.conv().id() != ConversionChar::s) return {false};
 
@@ -104,51 +102,48 @@ using IntegralConvertResult =
 using FloatingConvertResult = ConvertResult<Conv::floating>;
 
 // Floats.
-FloatingConvertResult FormatConvertImpl(float v, const ConversionSpec& conv,
+FloatingConvertResult FormatConvertImpl(float v, ConversionSpec conv,
                                         FormatSinkImpl* sink);
-FloatingConvertResult FormatConvertImpl(double v, const ConversionSpec& conv,
+FloatingConvertResult FormatConvertImpl(double v, ConversionSpec conv,
                                         FormatSinkImpl* sink);
-FloatingConvertResult FormatConvertImpl(long double v,
-                                        const ConversionSpec& conv,
+FloatingConvertResult FormatConvertImpl(long double v, ConversionSpec conv,
                                         FormatSinkImpl* sink);
 
 // Chars.
-IntegralConvertResult FormatConvertImpl(char v, const ConversionSpec& conv,
+IntegralConvertResult FormatConvertImpl(char v, ConversionSpec conv,
                                         FormatSinkImpl* sink);
-IntegralConvertResult FormatConvertImpl(signed char v,
-                                        const ConversionSpec& conv,
+IntegralConvertResult FormatConvertImpl(signed char v, ConversionSpec conv,
                                         FormatSinkImpl* sink);
-IntegralConvertResult FormatConvertImpl(unsigned char v,
-                                        const ConversionSpec& conv,
+IntegralConvertResult FormatConvertImpl(unsigned char v, ConversionSpec conv,
                                         FormatSinkImpl* sink);
 
 // Ints.
 IntegralConvertResult FormatConvertImpl(short v,  // NOLINT
-                                        const ConversionSpec& conv,
+                                        ConversionSpec conv,
                                         FormatSinkImpl* sink);
 IntegralConvertResult FormatConvertImpl(unsigned short v,  // NOLINT
-                                        const ConversionSpec& conv,
+                                        ConversionSpec conv,
                                         FormatSinkImpl* sink);
-IntegralConvertResult FormatConvertImpl(int v, const ConversionSpec& conv,
+IntegralConvertResult FormatConvertImpl(int v, ConversionSpec conv,
                                         FormatSinkImpl* sink);
-IntegralConvertResult FormatConvertImpl(unsigned v, const ConversionSpec& conv,
+IntegralConvertResult FormatConvertImpl(unsigned v, ConversionSpec conv,
                                         FormatSinkImpl* sink);
 IntegralConvertResult FormatConvertImpl(long v,  // NOLINT
-                                        const ConversionSpec& conv,
+                                        ConversionSpec conv,
                                         FormatSinkImpl* sink);
 IntegralConvertResult FormatConvertImpl(unsigned long v,  // NOLINT
-                                        const ConversionSpec& conv,
+                                        ConversionSpec conv,
                                         FormatSinkImpl* sink);
 IntegralConvertResult FormatConvertImpl(long long v,  // NOLINT
-                                        const ConversionSpec& conv,
+                                        ConversionSpec conv,
                                         FormatSinkImpl* sink);
 IntegralConvertResult FormatConvertImpl(unsigned long long v,  // NOLINT
-                                        const ConversionSpec& conv,
+                                        ConversionSpec conv,
                                         FormatSinkImpl* sink);
-IntegralConvertResult FormatConvertImpl(uint128 v, const ConversionSpec& conv,
+IntegralConvertResult FormatConvertImpl(uint128 v, ConversionSpec conv,
                                         FormatSinkImpl* sink);
 template <typename T, enable_if_t<std::is_same<T, bool>::value, int> = 0>
-IntegralConvertResult FormatConvertImpl(T v, const ConversionSpec& conv,
+IntegralConvertResult FormatConvertImpl(T v, ConversionSpec conv,
                                         FormatSinkImpl* sink) {
   return FormatConvertImpl(static_cast<int>(v), conv, sink);
 }
@@ -159,11 +154,11 @@ template <typename T>
 typename std::enable_if<std::is_enum<T>::value &&
                             !HasUserDefinedConvert<T>::value,
                         IntegralConvertResult>::type
-FormatConvertImpl(T v, const ConversionSpec& conv, FormatSinkImpl* sink);
+FormatConvertImpl(T v, ConversionSpec conv, FormatSinkImpl* sink);
 
 template <typename T>
 ConvertResult<Conv::s> FormatConvertImpl(const StreamedWrapper<T>& v,
-                                         const ConversionSpec& conv,
+                                         ConversionSpec conv,
                                          FormatSinkImpl* out) {
   std::ostringstream oss;
   oss << v.v_;
@@ -176,7 +171,7 @@ ConvertResult<Conv::s> FormatConvertImpl(const StreamedWrapper<T>& v,
 struct FormatCountCaptureHelper {
   template <class T = int>
   static ConvertResult<Conv::n> ConvertHelper(const FormatCountCapture& v,
-                                              const ConversionSpec& conv,
+                                              ConversionSpec conv,
                                               FormatSinkImpl* sink) {
     const absl::enable_if_t<sizeof(T) != 0, FormatCountCapture>& v2 = v;
 
@@ -189,7 +184,7 @@ struct FormatCountCaptureHelper {
 
 template <class T = int>
 ConvertResult<Conv::n> FormatConvertImpl(const FormatCountCapture& v,
-                                         const ConversionSpec& conv,
+                                         ConversionSpec conv,
                                          FormatSinkImpl* sink) {
   return FormatCountCaptureHelper::ConvertHelper(v, conv, sink);
 }
@@ -199,20 +194,20 @@ ConvertResult<Conv::n> FormatConvertImpl(const FormatCountCapture& v,
 struct FormatArgImplFriend {
   template <typename Arg>
   static bool ToInt(Arg arg, int* out) {
-    if (!arg.vtbl_->to_int) return false;
-    *out = arg.vtbl_->to_int(arg.data_);
-    return true;
+    // A value initialized ConversionSpec has a `none` conv, which tells the
+    // dispatcher to run the `int` conversion.
+    return arg.dispatcher_(arg.data_, {}, out);
   }
 
   template <typename Arg>
-  static bool Convert(Arg arg, const str_format_internal::ConversionSpec& conv,
+  static bool Convert(Arg arg, str_format_internal::ConversionSpec conv,
                       FormatSinkImpl* out) {
-    return arg.vtbl_->convert(arg.data_, conv, out);
+    return arg.dispatcher_(arg.data_, conv, out);
   }
 
   template <typename Arg>
-  static const void* GetVTablePtrForTest(Arg arg) {
-    return arg.vtbl_;
+  static typename Arg::Dispatcher GetVTablePtrForTest(Arg arg) {
+    return arg.dispatcher_;
   }
 };
 
@@ -229,11 +224,7 @@ class FormatArgImpl {
     char buf[kInlinedSpace];
   };
 
-  struct VTable {
-    bool (*convert)(Data, const str_format_internal::ConversionSpec& conv,
-                    FormatSinkImpl* out);
-    int (*to_int)(Data);
-  };
+  using Dispatcher = bool (*)(Data, ConversionSpec, void* out);
 
   template <typename T>
   struct store_by_value
@@ -253,10 +244,6 @@ class FormatArgImpl {
                                                                 : ByPointer))> {
   };
 
-  // An instance of an FormatArgImpl::VTable suitable for 'T'.
-  template <typename T>
-  struct TypedVTable;
-
   // To reduce the number of vtables we will decay values before hand.
   // Anything with a user-defined Convert will get its own vtable.
   // For everything else:
@@ -338,7 +325,10 @@ class FormatArgImpl {
   };
 
   template <typename T>
-  void Init(const T& value);
+  void Init(const T& value) {
+    data_ = Manager<T>::SetValue(value);
+    dispatcher_ = &Dispatch<T>;
+  }
 
   template <typename T>
   static int ToIntVal(const T& val) {
@@ -355,79 +345,75 @@ class FormatArgImpl {
     return static_cast<int>(val);
   }
 
-  Data data_;
-  const VTable* vtbl_;
-};
-
-template <typename T>
-struct FormatArgImpl::TypedVTable {
- private:
-  static bool ConvertImpl(Data arg,
-                          const str_format_internal::ConversionSpec& conv,
-                          FormatSinkImpl* out) {
-    return str_format_internal::FormatConvertImpl(Manager<T>::Value(arg), conv,
-                                                  out)
-        .value;
+  template <typename T>
+  static bool ToInt(Data arg, int* out, std::true_type /* is_integral */,
+                    std::false_type) {
+    *out = ToIntVal(Manager<T>::Value(arg));
+    return true;
   }
 
-  template <typename U = T, typename = void>
-  struct ToIntImpl {
-    static constexpr int (*value)(Data) = nullptr;
-  };
+  template <typename T>
+  static bool ToInt(Data arg, int* out, std::false_type,
+                    std::true_type /* is_enum */) {
+    *out = ToIntVal(static_cast<typename std::underlying_type<T>::type>(
+        Manager<T>::Value(arg)));
+    return true;
+  }
 
-  template <typename U>
-  struct ToIntImpl<U,
-                   typename std::enable_if<std::is_integral<U>::value>::type> {
-    static int Invoke(Data arg) { return ToIntVal(Manager<T>::Value(arg)); }
-    static constexpr int (*value)(Data) = &Invoke;
-  };
+  template <typename T>
+  static bool ToInt(Data, int*, std::false_type, std::false_type) {
+    return false;
+  }
 
-  template <typename U>
-  struct ToIntImpl<U, typename std::enable_if<std::is_enum<U>::value>::type> {
-    static int Invoke(Data arg) {
-      return ToIntVal(static_cast<typename std::underlying_type<T>::type>(
-          Manager<T>::Value(arg)));
+  template <typename T>
+  static bool Dispatch(Data arg, ConversionSpec spec, void* out) {
+    // A `none` conv indicates that we want the `int` conversion.
+    if (ABSL_PREDICT_FALSE(spec.conv().id() == ConversionChar::none)) {
+      return ToInt<T>(arg, static_cast<int*>(out), std::is_integral<T>(),
+                      std::is_enum<T>());
     }
-    static constexpr int (*value)(Data) = &Invoke;
-  };
 
- public:
-  static constexpr VTable value{&ConvertImpl, ToIntImpl<>::value};
-};
+    return str_format_internal::FormatConvertImpl(
+               Manager<T>::Value(arg), spec, static_cast<FormatSinkImpl*>(out))
+        .value;
+  }
 
-template <typename T>
-constexpr FormatArgImpl::VTable FormatArgImpl::TypedVTable<T>::value;
+  Data data_;
+  Dispatcher dispatcher_;
+};
 
-template <typename T>
-void FormatArgImpl::Init(const T& value) {
-  data_ = Manager<T>::SetValue(value);
-  vtbl_ = &TypedVTable<T>::value;
-}
+#define ABSL_INTERNAL_FORMAT_DISPATCH_INSTANTIATE_(T, E) \
+  E template bool FormatArgImpl::Dispatch<T>(Data, ConversionSpec, void*)
+
+#define ABSL_INTERNAL_FORMAT_DISPATCH_OVERLOADS_EXPAND_(...)                   \
+  ABSL_INTERNAL_FORMAT_DISPATCH_INSTANTIATE_(str_format_internal::VoidPtr,     \
+                                             __VA_ARGS__);                     \
+  ABSL_INTERNAL_FORMAT_DISPATCH_INSTANTIATE_(bool, __VA_ARGS__);               \
+  ABSL_INTERNAL_FORMAT_DISPATCH_INSTANTIATE_(char, __VA_ARGS__);               \
+  ABSL_INTERNAL_FORMAT_DISPATCH_INSTANTIATE_(signed char, __VA_ARGS__);        \
+  ABSL_INTERNAL_FORMAT_DISPATCH_INSTANTIATE_(unsigned char, __VA_ARGS__);      \
+  ABSL_INTERNAL_FORMAT_DISPATCH_INSTANTIATE_(short, __VA_ARGS__); /* NOLINT */ \
+  ABSL_INTERNAL_FORMAT_DISPATCH_INSTANTIATE_(unsigned short,      /* NOLINT */ \
+                                             __VA_ARGS__);                     \
+  ABSL_INTERNAL_FORMAT_DISPATCH_INSTANTIATE_(int, __VA_ARGS__);                \
+  ABSL_INTERNAL_FORMAT_DISPATCH_INSTANTIATE_(unsigned int, __VA_ARGS__);       \
+  ABSL_INTERNAL_FORMAT_DISPATCH_INSTANTIATE_(long, __VA_ARGS__); /* NOLINT */  \
+  ABSL_INTERNAL_FORMAT_DISPATCH_INSTANTIATE_(unsigned long,      /* NOLINT */  \
+                                             __VA_ARGS__);                     \
+  ABSL_INTERNAL_FORMAT_DISPATCH_INSTANTIATE_(long long, /* NOLINT */           \
+                                             __VA_ARGS__);                     \
+  ABSL_INTERNAL_FORMAT_DISPATCH_INSTANTIATE_(unsigned long long, /* NOLINT */  \
+                                             __VA_ARGS__);                     \
+  ABSL_INTERNAL_FORMAT_DISPATCH_INSTANTIATE_(uint128, __VA_ARGS__);            \
+  ABSL_INTERNAL_FORMAT_DISPATCH_INSTANTIATE_(float, __VA_ARGS__);              \
+  ABSL_INTERNAL_FORMAT_DISPATCH_INSTANTIATE_(double, __VA_ARGS__);             \
+  ABSL_INTERNAL_FORMAT_DISPATCH_INSTANTIATE_(long double, __VA_ARGS__);        \
+  ABSL_INTERNAL_FORMAT_DISPATCH_INSTANTIATE_(const char*, __VA_ARGS__);        \
+  ABSL_INTERNAL_FORMAT_DISPATCH_INSTANTIATE_(std::string, __VA_ARGS__);             \
+  ABSL_INTERNAL_FORMAT_DISPATCH_INSTANTIATE_(string_view, __VA_ARGS__)
+
+ABSL_INTERNAL_FORMAT_DISPATCH_OVERLOADS_EXPAND_(extern);
 
-extern template struct FormatArgImpl::TypedVTable<str_format_internal::VoidPtr>;
-
-extern template struct FormatArgImpl::TypedVTable<bool>;
-extern template struct FormatArgImpl::TypedVTable<char>;
-extern template struct FormatArgImpl::TypedVTable<signed char>;
-extern template struct FormatArgImpl::TypedVTable<unsigned char>;
-extern template struct FormatArgImpl::TypedVTable<short>;           // NOLINT
-extern template struct FormatArgImpl::TypedVTable<unsigned short>;  // NOLINT
-extern template struct FormatArgImpl::TypedVTable<int>;
-extern template struct FormatArgImpl::TypedVTable<unsigned>;
-extern template struct FormatArgImpl::TypedVTable<long>;           // NOLINT
-extern template struct FormatArgImpl::TypedVTable<unsigned long>;  // NOLINT
-extern template struct FormatArgImpl::TypedVTable<long long>;      // NOLINT
-extern template struct FormatArgImpl::TypedVTable<
-    unsigned long long>;  // NOLINT
-extern template struct FormatArgImpl::TypedVTable<uint128>;
-
-extern template struct FormatArgImpl::TypedVTable<float>;
-extern template struct FormatArgImpl::TypedVTable<double>;
-extern template struct FormatArgImpl::TypedVTable<long double>;
-
-extern template struct FormatArgImpl::TypedVTable<const char*>;
-extern template struct FormatArgImpl::TypedVTable<std::string>;
-extern template struct FormatArgImpl::TypedVTable<string_view>;
 }  // namespace str_format_internal
 }  // namespace absl
 
diff --git a/absl/strings/internal/str_format/bind.h b/absl/strings/internal/str_format/bind.h
index a503b19bb675..1b52df9c7f56 100644
--- a/absl/strings/internal/str_format/bind.h
+++ b/absl/strings/internal/str_format/bind.h
@@ -186,7 +186,7 @@ class StreamedWrapper {
  private:
   template <typename S>
   friend ConvertResult<Conv::s> FormatConvertImpl(const StreamedWrapper<S>& v,
-                                                  const ConversionSpec& conv,
+                                                  ConversionSpec conv,
                                                   FormatSinkImpl* out);
   const T& v_;
 };
diff --git a/absl/strings/internal/str_format/extension.h b/absl/strings/internal/str_format/extension.h
index f43195c127b1..11b996aefd37 100644
--- a/absl/strings/internal/str_format/extension.h
+++ b/absl/strings/internal/str_format/extension.h
@@ -18,6 +18,7 @@
 #define ABSL_STRINGS_INTERNAL_STR_FORMAT_EXTENSION_H_
 
 #include <limits.h>
+#include <cstddef>
 #include <cstring>
 #include <ostream>
 
@@ -307,7 +308,12 @@ class ConversionSpec {
  public:
   Flags flags() const { return flags_; }
   LengthMod length_mod() const { return length_mod_; }
-  ConversionChar conv() const { return conv_; }
+  ConversionChar conv() const {
+    // Keep this field first in the struct . It generates better code when
+    // accessing it when ConversionSpec is passed by value in registers.
+    static_assert(offsetof(ConversionSpec, conv_) == 0, "");
+    return conv_;
+  }
 
   // Returns the specified width. If width is unspecfied, it returns a negative
   // value.
@@ -324,9 +330,9 @@ class ConversionSpec {
   void set_left(bool b) { flags_.left = b; }
 
  private:
+  ConversionChar conv_;
   Flags flags_;
   LengthMod length_mod_;
-  ConversionChar conv_;
   int width_;
   int precision_;
 };