diff --git a/absl/strings/internal/str_format/arg.h b/absl/strings/internal/str_format/arg.h index 021013fb..98b8bcd5 100644 --- a/absl/strings/internal/str_format/arg.h +++ b/absl/strings/internal/str_format/arg.h @@ -129,9 +129,12 @@ extern template bool ConvertIntArg( // NOLINT template auto FormatConvertImpl(const T& v, FormatConversionSpecImpl conv, FormatSinkImpl* sink) - -> decltype(AbslFormatConvert(v, - std::declval(), - std::declval())) { + -> std::enable_if_t< + !HasAbslStringify::value, + decltype(AbslFormatConvert( + v, + std::declval(), + std::declval()))> { using FormatConversionSpecT = absl::enable_if_t; using FormatSinkT = @@ -423,11 +426,16 @@ struct FormatArgImplFriend { template constexpr FormatConversionCharSet ArgumentToConv() { - using ConvResult = decltype(str_format_internal::FormatConvertImpl( - std::declval(), - std::declval(), - std::declval())); - return absl::str_format_internal::ExtractCharSet(ConvResult{}); + if constexpr (HasAbslStringify::value && + HasUserDefinedConvert::value) { + return FormatConversionCharSetInternal::v; + } else { + using ConvResult = decltype(str_format_internal::FormatConvertImpl( + std::declval(), + std::declval(), + std::declval())); + return absl::str_format_internal::ExtractCharSet(ConvResult{}); + } } // A type-erased handle to a format argument. diff --git a/absl/strings/str_format_test.cc b/absl/strings/str_format_test.cc index a4c877a8..7b3b4a22 100644 --- a/absl/strings/str_format_test.cc +++ b/absl/strings/str_format_test.cc @@ -1205,6 +1205,32 @@ TEST_F(FormatExtensionTest, AbslStringifyEnumOtherSpecifiers) { EXPECT_EQ(absl::StrFormat("My choice is %x", e), "My choice is 20"); } +// When a type defines both AbslStringify and AbslFormatConvert, +// AbslStringify should take priority. +struct PointWithBothStringifyAndFormatConvert { + template + friend void AbslStringify(Sink& sink, + const PointWithBothStringifyAndFormatConvert& p) { + sink.Append(absl::StrCat("(", p.x, ", ", p.y, ")")); + } + + friend absl::FormatConvertResult + AbslFormatConvert(const PointWithBothStringifyAndFormatConvert& p, + const absl::FormatConversionSpec&, + absl::FormatSink* sink) { + sink->Append(absl::StrCat("WRONG(", p.x, ", ", p.y, ")")); + return {true}; + } + + double x = 10.0; + double y = 20.0; +}; + +TEST_F(FormatExtensionTest, AbslStringifyTakesPriorityOverFormatConvert) { + PointWithBothStringifyAndFormatConvert p; + EXPECT_EQ(absl::StrFormat("a %v z", p), "a (10, 20) z"); +} + } // namespace // Some codegen thunks that we can use to easily dump the generated assembly for