Commit 9bff2a93 by Phoebe Liang Committed by Copybara-Service

Fixes issue where AbslStringify() breaks formatting with %d for enums

PiperOrigin-RevId: 493682437
Change-Id: I30f2ac36b998b86c24fe7513cd952b860560a66e
parent e9787e7d
...@@ -297,6 +297,37 @@ constexpr auto ConvertV(T) { ...@@ -297,6 +297,37 @@ constexpr auto ConvertV(T) {
} }
template <typename 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) { bool ConvertIntArg(T v, FormatConversionSpecImpl conv, FormatSinkImpl *sink) {
using U = typename MakeUnsigned<T>::type; using U = typename MakeUnsigned<T>::type;
IntDigits as_digits; IntDigits as_digits;
...@@ -354,36 +385,37 @@ bool ConvertIntArg(T v, FormatConversionSpecImpl conv, FormatSinkImpl *sink) { ...@@ -354,36 +385,37 @@ bool ConvertIntArg(T v, FormatConversionSpecImpl conv, FormatSinkImpl *sink) {
return ConvertIntImplInnerSlow(as_digits, conv, sink); return ConvertIntImplInnerSlow(as_digits, conv, sink);
} }
template <typename T> template bool ConvertIntArg<char>(char v, FormatConversionSpecImpl conv,
bool ConvertFloatArg(T v, FormatConversionSpecImpl conv, FormatSinkImpl *sink) { FormatSinkImpl *sink);
if (conv.conversion_char() == FormatConversionCharInternal::v) { template bool ConvertIntArg<signed char>(signed char v,
conv.set_conversion_char(FormatConversionCharInternal::g); FormatConversionSpecImpl conv,
} FormatSinkImpl *sink);
template bool ConvertIntArg<unsigned char>(unsigned char v,
return FormatConversionCharIsFloat(conv.conversion_char()) && FormatConversionSpecImpl conv,
ConvertFloatImpl(v, conv, sink); FormatSinkImpl *sink);
} template bool ConvertIntArg<short>(short v, // NOLINT
FormatConversionSpecImpl conv,
inline bool ConvertStringArg(string_view v, const FormatConversionSpecImpl conv, FormatSinkImpl *sink);
FormatSinkImpl *sink) { template bool ConvertIntArg<unsigned short>(unsigned short v, // NOLINT
if (conv.is_basic()) { FormatConversionSpecImpl conv,
sink->Append(v); FormatSinkImpl *sink);
return true; template bool ConvertIntArg<int>(int v, FormatConversionSpecImpl conv,
} FormatSinkImpl *sink);
return sink->PutPaddedString(v, conv.width(), conv.precision(), template bool ConvertIntArg<unsigned int>(unsigned int v,
conv.has_left_flag()); FormatConversionSpecImpl conv,
} FormatSinkImpl *sink);
template bool ConvertIntArg<long>(long v, // NOLINT
} // namespace FormatConversionSpecImpl conv,
FormatSinkImpl *sink);
bool ConvertBoolArg(bool v, FormatSinkImpl *sink) { template bool ConvertIntArg<unsigned long>(unsigned long v, // NOLINT
if (v) { FormatConversionSpecImpl conv,
sink->Append("true"); FormatSinkImpl *sink);
} else { template bool ConvertIntArg<long long>(long long v, // NOLINT
sink->Append("false"); FormatConversionSpecImpl conv,
} FormatSinkImpl *sink);
return true; template bool ConvertIntArg<unsigned long long>(unsigned long long v, // NOLINT
} FormatConversionSpecImpl conv,
FormatSinkImpl *sink);
// ==================== Strings ==================== // ==================== Strings ====================
StringConvertResult FormatConvertImpl(const std::string &v, StringConvertResult FormatConvertImpl(const std::string &v,
......
...@@ -53,6 +53,19 @@ struct ArgConvertResult { ...@@ -53,6 +53,19 @@ struct ArgConvertResult {
bool value; 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> template <typename T, typename = void>
struct HasUserDefinedConvert : std::false_type {}; struct HasUserDefinedConvert : std::false_type {};
...@@ -70,6 +83,44 @@ void AbslFormatConvert(); ...@@ -70,6 +83,44 @@ void AbslFormatConvert();
void AbslStringify(); void AbslStringify();
template <typename T> 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, auto FormatConvertImpl(const T& v, FormatConversionSpecImpl conv,
FormatSinkImpl* sink) FormatSinkImpl* sink)
-> decltype(AbslFormatConvert(v, -> decltype(AbslFormatConvert(v,
...@@ -85,10 +136,30 @@ auto FormatConvertImpl(const T& v, FormatConversionSpecImpl conv, ...@@ -85,10 +136,30 @@ auto FormatConvertImpl(const T& v, FormatConversionSpecImpl conv,
} }
template <typename T> 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, auto FormatConvertImpl(const T& v, FormatConversionSpecImpl,
FormatSinkImpl* sink) FormatSinkImpl* sink)
-> std::enable_if_t<std::is_void<decltype(AbslStringify( -> std::enable_if_t<!std::is_enum<T>::value &&
std::declval<FormatSink&>(), v))>::value, std::is_void<decltype(AbslStringify(
std::declval<FormatSink&>(), v))>::value,
ArgConvertResult<FormatConversionCharSetInternal::v>> { ArgConvertResult<FormatConversionCharSetInternal::v>> {
using FormatSinkT = using FormatSinkT =
absl::enable_if_t<sizeof(const T& (*)()) != 0, FormatSink>; absl::enable_if_t<sizeof(const T& (*)()) != 0, FormatSink>;
...@@ -194,19 +265,6 @@ StringConvertResult FormatConvertImpl(const AbslCord& value, ...@@ -194,19 +265,6 @@ StringConvertResult FormatConvertImpl(const AbslCord& value,
return {true}; 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); bool ConvertBoolArg(bool v, FormatSinkImpl* sink);
// Floats. // Floats.
......
...@@ -1142,18 +1142,51 @@ TEST_F(FormatExtensionTest, AbslStringifyExampleUsingFormat) { ...@@ -1142,18 +1142,51 @@ TEST_F(FormatExtensionTest, AbslStringifyExampleUsingFormat) {
EXPECT_EQ(absl::StrFormat("a %v z", p), "a (10, 20) z"); 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> template <typename Sink>
void AbslStringify(Sink& sink, EnumWithStringify e) { void AbslStringify(Sink& sink, EnumWithStringify e) {
absl::Format(&sink, "%s", e == EnumWithStringify::Many ? "Many" : "Choices"); 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; const auto e = EnumWithStringify::Choices;
EXPECT_EQ(absl::StrFormat("My choice is %v", e), "My choice is 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 } // namespace
// Some codegen thunks that we can use to easily dump the generated assembly for // Some codegen thunks that we can use to easily dump the generated assembly for
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment