summaryrefslogtreecommitdiff
path: root/absl/strings
diff options
context:
space:
mode:
authorGravatar Phoebe Liang <phoebeliang@google.com>2022-12-07 12:49:08 -0800
committerGravatar Copybara-Service <copybara-worker@google.com>2022-12-07 12:49:54 -0800
commit9bff2a9302a8dbf91712fc215eb2e2cf8ec234e7 (patch)
tree2f34d9f653da6ba62047d525575671349a387271 /absl/strings
parente9787e7d1d46e2b9687b150f18c76d85c2eaee71 (diff)
Fixes issue where AbslStringify() breaks formatting with %d for enums
PiperOrigin-RevId: 493682437 Change-Id: I30f2ac36b998b86c24fe7513cd952b860560a66e
Diffstat (limited to 'absl/strings')
-rw-r--r--absl/strings/internal/str_format/arg.cc92
-rw-r--r--absl/strings/internal/str_format/arg.h88
-rw-r--r--absl/strings/str_format_test.cc37
3 files changed, 170 insertions, 47 deletions
diff --git a/absl/strings/internal/str_format/arg.cc b/absl/strings/internal/str_format/arg.cc
index 967fe9ca..018dd052 100644
--- a/absl/strings/internal/str_format/arg.cc
+++ b/absl/strings/internal/str_format/arg.cc
@@ -297,6 +297,37 @@ constexpr auto ConvertV(T) {
}
template <typename T>
+bool ConvertFloatArg(T v, FormatConversionSpecImpl conv, FormatSinkImpl *sink) {
+ if (conv.conversion_char() == FormatConversionCharInternal::v) {
+ conv.set_conversion_char(FormatConversionCharInternal::g);
+ }
+
+ return FormatConversionCharIsFloat(conv.conversion_char()) &&
+ ConvertFloatImpl(v, conv, sink);
+}
+
+inline bool ConvertStringArg(string_view v, const FormatConversionSpecImpl conv,
+ FormatSinkImpl *sink) {
+ if (conv.is_basic()) {
+ sink->Append(v);
+ return true;
+ }
+ return sink->PutPaddedString(v, conv.width(), conv.precision(),
+ conv.has_left_flag());
+}
+
+} // namespace
+
+bool ConvertBoolArg(bool v, FormatSinkImpl *sink) {
+ if (v) {
+ sink->Append("true");
+ } else {
+ sink->Append("false");
+ }
+ return true;
+}
+
+template <typename T>
bool ConvertIntArg(T v, FormatConversionSpecImpl conv, FormatSinkImpl *sink) {
using U = typename MakeUnsigned<T>::type;
IntDigits as_digits;
@@ -354,36 +385,37 @@ bool ConvertIntArg(T v, FormatConversionSpecImpl conv, FormatSinkImpl *sink) {
return ConvertIntImplInnerSlow(as_digits, conv, sink);
}
-template <typename T>
-bool ConvertFloatArg(T v, FormatConversionSpecImpl conv, FormatSinkImpl *sink) {
- if (conv.conversion_char() == FormatConversionCharInternal::v) {
- conv.set_conversion_char(FormatConversionCharInternal::g);
- }
-
- return FormatConversionCharIsFloat(conv.conversion_char()) &&
- ConvertFloatImpl(v, conv, sink);
-}
-
-inline bool ConvertStringArg(string_view v, const FormatConversionSpecImpl conv,
- FormatSinkImpl *sink) {
- if (conv.is_basic()) {
- sink->Append(v);
- return true;
- }
- return sink->PutPaddedString(v, conv.width(), conv.precision(),
- conv.has_left_flag());
-}
-
-} // namespace
-
-bool ConvertBoolArg(bool v, FormatSinkImpl *sink) {
- if (v) {
- sink->Append("true");
- } else {
- sink->Append("false");
- }
- return true;
-}
+template bool ConvertIntArg<char>(char v, FormatConversionSpecImpl conv,
+ FormatSinkImpl *sink);
+template bool ConvertIntArg<signed char>(signed char v,
+ FormatConversionSpecImpl conv,
+ FormatSinkImpl *sink);
+template bool ConvertIntArg<unsigned char>(unsigned char v,
+ FormatConversionSpecImpl conv,
+ FormatSinkImpl *sink);
+template bool ConvertIntArg<short>(short v, // NOLINT
+ FormatConversionSpecImpl conv,
+ FormatSinkImpl *sink);
+template bool ConvertIntArg<unsigned short>(unsigned short v, // NOLINT
+ FormatConversionSpecImpl conv,
+ FormatSinkImpl *sink);
+template bool ConvertIntArg<int>(int v, FormatConversionSpecImpl conv,
+ FormatSinkImpl *sink);
+template bool ConvertIntArg<unsigned int>(unsigned int v,
+ FormatConversionSpecImpl conv,
+ FormatSinkImpl *sink);
+template bool ConvertIntArg<long>(long v, // NOLINT
+ FormatConversionSpecImpl conv,
+ FormatSinkImpl *sink);
+template bool ConvertIntArg<unsigned long>(unsigned long v, // NOLINT
+ FormatConversionSpecImpl conv,
+ FormatSinkImpl *sink);
+template bool ConvertIntArg<long long>(long long v, // NOLINT
+ FormatConversionSpecImpl conv,
+ FormatSinkImpl *sink);
+template bool ConvertIntArg<unsigned long long>(unsigned long long v, // NOLINT
+ FormatConversionSpecImpl conv,
+ FormatSinkImpl *sink);
// ==================== Strings ====================
StringConvertResult FormatConvertImpl(const std::string &v,
diff --git a/absl/strings/internal/str_format/arg.h b/absl/strings/internal/str_format/arg.h
index bc4cde96..e4b16628 100644
--- a/absl/strings/internal/str_format/arg.h
+++ b/absl/strings/internal/str_format/arg.h
@@ -53,6 +53,19 @@ struct ArgConvertResult {
bool value;
};
+using IntegralConvertResult = ArgConvertResult<FormatConversionCharSetUnion(
+ FormatConversionCharSetInternal::c,
+ FormatConversionCharSetInternal::kNumeric,
+ FormatConversionCharSetInternal::kStar,
+ FormatConversionCharSetInternal::v)>;
+using FloatingConvertResult = ArgConvertResult<FormatConversionCharSetUnion(
+ FormatConversionCharSetInternal::kFloating,
+ FormatConversionCharSetInternal::v)>;
+using CharConvertResult = ArgConvertResult<FormatConversionCharSetUnion(
+ FormatConversionCharSetInternal::c,
+ FormatConversionCharSetInternal::kNumeric,
+ FormatConversionCharSetInternal::kStar)>;
+
template <typename T, typename = void>
struct HasUserDefinedConvert : std::false_type {};
@@ -70,6 +83,44 @@ void AbslFormatConvert();
void AbslStringify();
template <typename T>
+bool ConvertIntArg(T v, FormatConversionSpecImpl conv, FormatSinkImpl* sink);
+
+// Forward declarations of internal `ConvertIntArg` function template
+// instantiations are here to avoid including the template body in the headers
+// and instantiating it in large numbers of translation units. Explicit
+// instantiations can be found in "absl/strings/internal/str_format/arg.cc"
+extern template bool ConvertIntArg<char>(char v, FormatConversionSpecImpl conv,
+ FormatSinkImpl* sink);
+extern template bool ConvertIntArg<signed char>(signed char v,
+ FormatConversionSpecImpl conv,
+ FormatSinkImpl* sink);
+extern template bool ConvertIntArg<unsigned char>(unsigned char v,
+ FormatConversionSpecImpl conv,
+ FormatSinkImpl* sink);
+extern template bool ConvertIntArg<short>(short v, // NOLINT
+ FormatConversionSpecImpl conv,
+ FormatSinkImpl* sink);
+extern template bool ConvertIntArg<unsigned short>( // NOLINT
+ unsigned short v, FormatConversionSpecImpl conv, // NOLINT
+ FormatSinkImpl* sink);
+extern template bool ConvertIntArg<int>(int v, FormatConversionSpecImpl conv,
+ FormatSinkImpl* sink);
+extern template bool ConvertIntArg<unsigned int>(unsigned int v,
+ FormatConversionSpecImpl conv,
+ FormatSinkImpl* sink);
+extern template bool ConvertIntArg<long>( // NOLINT
+ long v, FormatConversionSpecImpl conv, FormatSinkImpl* sink); // NOLINT
+extern template bool ConvertIntArg<unsigned long>(unsigned long v, // NOLINT
+ FormatConversionSpecImpl conv,
+ FormatSinkImpl* sink);
+extern template bool ConvertIntArg<long long>(long long v, // NOLINT
+ FormatConversionSpecImpl conv,
+ FormatSinkImpl* sink);
+extern template bool ConvertIntArg<unsigned long long>( // NOLINT
+ unsigned long long v, FormatConversionSpecImpl conv, // NOLINT
+ FormatSinkImpl* sink);
+
+template <typename T>
auto FormatConvertImpl(const T& v, FormatConversionSpecImpl conv,
FormatSinkImpl* sink)
-> decltype(AbslFormatConvert(v,
@@ -85,10 +136,30 @@ auto FormatConvertImpl(const T& v, FormatConversionSpecImpl conv,
}
template <typename T>
+auto FormatConvertImpl(const T& v, FormatConversionSpecImpl conv,
+ FormatSinkImpl* sink)
+ -> std::enable_if_t<std::is_enum<T>::value &&
+ std::is_void<decltype(AbslStringify(
+ std::declval<FormatSink&>(), v))>::value,
+ IntegralConvertResult> {
+ if (conv.conversion_char() == FormatConversionCharInternal::v) {
+ using FormatSinkT =
+ absl::enable_if_t<sizeof(const T& (*)()) != 0, FormatSink>;
+ auto fs = sink->Wrap<FormatSinkT>();
+ AbslStringify(fs, v);
+ return {true};
+ } else {
+ return {ConvertIntArg(
+ static_cast<typename std::underlying_type<T>::type>(v), conv, sink)};
+ }
+}
+
+template <typename T>
auto FormatConvertImpl(const T& v, FormatConversionSpecImpl,
FormatSinkImpl* sink)
- -> std::enable_if_t<std::is_void<decltype(AbslStringify(
- std::declval<FormatSink&>(), v))>::value,
+ -> std::enable_if_t<!std::is_enum<T>::value &&
+ std::is_void<decltype(AbslStringify(
+ std::declval<FormatSink&>(), v))>::value,
ArgConvertResult<FormatConversionCharSetInternal::v>> {
using FormatSinkT =
absl::enable_if_t<sizeof(const T& (*)()) != 0, FormatSink>;
@@ -194,19 +265,6 @@ StringConvertResult FormatConvertImpl(const AbslCord& value,
return {true};
}
-using IntegralConvertResult = ArgConvertResult<FormatConversionCharSetUnion(
- FormatConversionCharSetInternal::c,
- FormatConversionCharSetInternal::kNumeric,
- FormatConversionCharSetInternal::kStar,
- FormatConversionCharSetInternal::v)>;
-using FloatingConvertResult = ArgConvertResult<FormatConversionCharSetUnion(
- FormatConversionCharSetInternal::kFloating,
- FormatConversionCharSetInternal::v)>;
-using CharConvertResult = ArgConvertResult<FormatConversionCharSetUnion(
- FormatConversionCharSetInternal::c,
- FormatConversionCharSetInternal::kNumeric,
- FormatConversionCharSetInternal::kStar)>;
-
bool ConvertBoolArg(bool v, FormatSinkImpl* sink);
// Floats.
diff --git a/absl/strings/str_format_test.cc b/absl/strings/str_format_test.cc
index 36def1e0..5198fb33 100644
--- a/absl/strings/str_format_test.cc
+++ b/absl/strings/str_format_test.cc
@@ -1142,18 +1142,51 @@ TEST_F(FormatExtensionTest, AbslStringifyExampleUsingFormat) {
EXPECT_EQ(absl::StrFormat("a %v z", p), "a (10, 20) z");
}
-enum class EnumWithStringify { Many = 0, Choices = 1 };
+enum class EnumClassWithStringify { Many = 0, Choices = 1 };
+
+template <typename Sink>
+void AbslStringify(Sink& sink, EnumClassWithStringify e) {
+ absl::Format(&sink, "%s",
+ e == EnumClassWithStringify::Many ? "Many" : "Choices");
+}
+
+enum EnumWithStringify { Many, Choices };
template <typename Sink>
void AbslStringify(Sink& sink, EnumWithStringify e) {
absl::Format(&sink, "%s", e == EnumWithStringify::Many ? "Many" : "Choices");
}
-TEST_F(FormatExtensionTest, AbslStringifyWithEnum) {
+TEST_F(FormatExtensionTest, AbslStringifyWithEnumWithV) {
+ const auto e_class = EnumClassWithStringify::Choices;
+ EXPECT_EQ(absl::StrFormat("My choice is %v", e_class),
+ "My choice is Choices");
+
const auto e = EnumWithStringify::Choices;
EXPECT_EQ(absl::StrFormat("My choice is %v", e), "My choice is Choices");
}
+TEST_F(FormatExtensionTest, AbslStringifyEnumWithD) {
+ const auto e_class = EnumClassWithStringify::Many;
+ EXPECT_EQ(absl::StrFormat("My choice is %d", e_class), "My choice is 0");
+
+ const auto e = EnumWithStringify::Choices;
+ EXPECT_EQ(absl::StrFormat("My choice is %d", e), "My choice is 1");
+}
+
+enum class EnumWithLargerValue { x = 32 };
+
+template <typename Sink>
+void AbslStringify(Sink& sink, EnumWithLargerValue e) {
+ absl::Format(&sink, "%s", "Many");
+}
+
+TEST_F(FormatExtensionTest, AbslStringifyEnumOtherSpecifiers) {
+ const auto e = EnumWithLargerValue::x;
+ EXPECT_EQ(absl::StrFormat("My choice is %g", e), "My choice is 32");
+ EXPECT_EQ(absl::StrFormat("My choice is %x", e), "My choice is 20");
+}
+
} // namespace
// Some codegen thunks that we can use to easily dump the generated assembly for