Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 16 additions & 8 deletions absl/strings/internal/str_format/arg.h
Original file line number Diff line number Diff line change
Expand Up @@ -129,9 +129,12 @@ extern template bool ConvertIntArg<unsigned long long>( // NOLINT
template <typename T>
auto FormatConvertImpl(const T& v, FormatConversionSpecImpl conv,
FormatSinkImpl* sink)
-> decltype(AbslFormatConvert(v,
std::declval<const FormatConversionSpec&>(),
std::declval<FormatSink*>())) {
-> std::enable_if_t<
!HasAbslStringify<T>::value,
decltype(AbslFormatConvert(
v,
std::declval<const FormatConversionSpec&>(),
std::declval<FormatSink*>()))> {
using FormatConversionSpecT =
absl::enable_if_t<sizeof(const T& (*)()) != 0, FormatConversionSpec>;
using FormatSinkT =
Expand Down Expand Up @@ -423,11 +426,16 @@ struct FormatArgImplFriend {

template <typename Arg>
constexpr FormatConversionCharSet ArgumentToConv() {
using ConvResult = decltype(str_format_internal::FormatConvertImpl(
std::declval<const Arg&>(),
std::declval<const FormatConversionSpecImpl&>(),
std::declval<FormatSinkImpl*>()));
return absl::str_format_internal::ExtractCharSet(ConvResult{});
if constexpr (HasAbslStringify<Arg>::value &&
HasUserDefinedConvert<Arg>::value) {
return FormatConversionCharSetInternal::v;
} else {
using ConvResult = decltype(str_format_internal::FormatConvertImpl(
std::declval<const Arg&>(),
std::declval<const FormatConversionSpecImpl&>(),
std::declval<FormatSinkImpl*>()));
return absl::str_format_internal::ExtractCharSet(ConvResult{});
}
}

// A type-erased handle to a format argument.
Expand Down
26 changes: 26 additions & 0 deletions absl/strings/str_format_test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -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 <typename Sink>
friend void AbslStringify(Sink& sink,
const PointWithBothStringifyAndFormatConvert& p) {
sink.Append(absl::StrCat("(", p.x, ", ", p.y, ")"));
}

friend absl::FormatConvertResult<absl::FormatConversionCharSet::kString>
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
Expand Down