diff --git a/absl/strings/internal/str_format/arg.h b/absl/strings/internal/str_format/arg.h index b3e4ff15..bc4cde96 100644 --- a/absl/strings/internal/str_format/arg.h +++ b/absl/strings/internal/str_format/arg.h @@ -18,6 +18,7 @@ #include #include +#include #include #include #include @@ -25,10 +26,12 @@ #include #include #include +#include #include "absl/base/port.h" #include "absl/meta/type_traits.h" #include "absl/numeric/int128.h" +#include "absl/strings/internal/has_absl_stringify.h" #include "absl/strings/internal/str_format/extension.h" #include "absl/strings/string_view.h" @@ -271,7 +274,8 @@ IntegralConvertResult FormatConvertImpl(T v, FormatConversionSpecImpl conv, // FormatArgImpl will use the underlying Convert functions instead. template typename std::enable_if::value && - !HasUserDefinedConvert::value, + !HasUserDefinedConvert::value && + !strings_internal::HasAbslStringify::value, IntegralConvertResult>::type FormatConvertImpl(T v, FormatConversionSpecImpl conv, FormatSinkImpl* sink); @@ -384,7 +388,8 @@ class FormatArgImpl { template struct DecayType { static constexpr bool kHasUserDefined = - str_format_internal::HasUserDefinedConvert::value; + str_format_internal::HasUserDefinedConvert::value || + strings_internal::HasAbslStringify::value; using type = typename std::conditional< !kHasUserDefined && std::is_convertible::value, const char*, @@ -396,6 +401,7 @@ class FormatArgImpl { struct DecayType::value && + !strings_internal::HasAbslStringify::value && std::is_enum::value>::type> { using type = typename std::underlying_type::type; }; diff --git a/absl/strings/str_cat.h b/absl/strings/str_cat.h index 00af84ea..5ee26db0 100644 --- a/absl/strings/str_cat.h +++ b/absl/strings/str_cat.h @@ -318,7 +318,8 @@ class AlphaNum { // This overload matches only scoped enums. template {} && !std::is_convertible{}>::type> + std::is_enum{} && !std::is_convertible{} && + !strings_internal::HasAbslStringify::value>::type> AlphaNum(T e) // NOLINT(runtime/explicit) : AlphaNum(static_cast::type>(e)) {} diff --git a/absl/strings/str_cat_test.cc b/absl/strings/str_cat_test.cc index 1b3b7ece..c3fb3170 100644 --- a/absl/strings/str_cat_test.cc +++ b/absl/strings/str_cat_test.cc @@ -650,4 +650,16 @@ TEST(StrCat, AbslStringifyExampleUsingFormat) { EXPECT_EQ(absl::StrCat("a ", p, " z"), "a (10, 20) z"); } +enum class EnumWithStringify { Many = 0, Choices = 1 }; + +template +void AbslStringify(Sink& sink, EnumWithStringify e) { + absl::Format(&sink, "%s", e == EnumWithStringify::Many ? "Many" : "Choices"); +} + +TEST(StrCat, AbslStringifyWithEnum) { + const auto e = EnumWithStringify::Choices; + EXPECT_EQ(absl::StrCat(e), "Choices"); +} + } // namespace diff --git a/absl/strings/str_format_test.cc b/absl/strings/str_format_test.cc index 62ed262d..2aa22b0d 100644 --- a/absl/strings/str_format_test.cc +++ b/absl/strings/str_format_test.cc @@ -1135,6 +1135,18 @@ TEST_F(FormatExtensionTest, AbslStringifyExampleUsingFormat) { EXPECT_EQ(absl::StrFormat("a %v z", p), "a (10, 20) z"); } +enum class EnumWithStringify { Many = 0, Choices = 1 }; + +template +void AbslStringify(Sink& sink, EnumWithStringify e) { + absl::Format(&sink, "%s", e == EnumWithStringify::Many ? "Many" : "Choices"); +} + +TEST_F(FormatExtensionTest, AbslStringifyWithEnum) { + const auto e = EnumWithStringify::Choices; + EXPECT_EQ(absl::StrFormat("My choice is %v", e), "My choice is Choices"); +} + } // namespace // Some codegen thunks that we can use to easily dump the generated assembly for