From f7404cd33f2457bd561fdcfbb024e43852c94bcf Mon Sep 17 00:00:00 2001 From: Abseil Team <absl-team@google.com> Date: Wed, 28 Sep 2022 11:33:29 -0700 Subject: [PATCH] Allows absl::StrFormat to accept types which implement AbslStringify() PiperOrigin-RevId: 477507777 Change-Id: I5ecde3163ca560ac8774034e55654774e36ad230 --- absl/strings/internal/str_format/arg.h | 34 ++++++++++++++++++-------- absl/strings/str_format_test.cc | 19 +++++++++++++- 2 files changed, 42 insertions(+), 11 deletions(-) diff --git a/absl/strings/internal/str_format/arg.h b/absl/strings/internal/str_format/arg.h index 2dfbf728..3d616dba 100644 --- a/absl/strings/internal/str_format/arg.h +++ b/absl/strings/internal/str_format/arg.h @@ -45,6 +45,11 @@ class FormatConversionSpec; namespace str_format_internal { +template <FormatConversionCharSet C> +struct ArgConvertResult { + bool value; +}; + template <typename T, typename = void> struct HasUserDefinedConvert : std::false_type {}; @@ -71,6 +76,20 @@ auto FormatConvertImpl(const T& v, FormatConversionSpecImpl conv, return AbslFormatConvert(v, fcs, &fs); } +void AbslStringify(); // Stops the lexical name lookup +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, + ArgConvertResult<FormatConversionCharSetInternal::v>> { + using FormatSinkT = + absl::enable_if_t<sizeof(const T& (*)()) != 0, FormatSink>; + auto fs = sink->Wrap<FormatSinkT>(); + AbslStringify(fs, v); + return {true}; +} + template <typename T> class StreamedWrapper; @@ -95,11 +114,6 @@ struct VoidPtr { uintptr_t value; }; -template <FormatConversionCharSet C> -struct ArgConvertResult { - bool value; -}; - template <FormatConversionCharSet C> constexpr FormatConversionCharSet ExtractCharSet(FormatConvertResult<C>) { return C; @@ -316,11 +330,11 @@ struct FormatArgImplFriend { template <typename Arg> constexpr FormatConversionCharSet ArgumentToConv() { - return absl::str_format_internal::ExtractCharSet( - decltype(str_format_internal::FormatConvertImpl( - std::declval<const Arg&>(), - std::declval<const FormatConversionSpecImpl&>(), - std::declval<FormatSinkImpl*>())){}); + 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. diff --git a/absl/strings/str_format_test.cc b/absl/strings/str_format_test.cc index 4b778056..0c4f10c8 100644 --- a/absl/strings/str_format_test.cc +++ b/absl/strings/str_format_test.cc @@ -1074,7 +1074,8 @@ using FormatExtensionTest = ::testing::Test; struct Point { friend absl::FormatConvertResult<absl::FormatConversionCharSet::kString | - absl::FormatConversionCharSet::kIntegral> + absl::FormatConversionCharSet::kIntegral | + absl::FormatConversionCharSet::v> AbslFormatConvert(const Point& p, const absl::FormatConversionSpec& spec, absl::FormatSink* s) { if (spec.conversion_char() == absl::FormatConversionChar::s) { @@ -1093,6 +1094,7 @@ TEST_F(FormatExtensionTest, AbslFormatConvertExample) { Point p; EXPECT_EQ(absl::StrFormat("a %s z", p), "a x=10 y=20 z"); EXPECT_EQ(absl::StrFormat("a %d z", p), "a 10,20 z"); + EXPECT_EQ(absl::StrFormat("a %v z", p), "a 10,20 z"); // Typed formatting will fail to compile an invalid format. // StrFormat("%f", p); // Does not compile. @@ -1101,6 +1103,21 @@ TEST_F(FormatExtensionTest, AbslFormatConvertExample) { // FormatUntyped will return false for bad character. EXPECT_FALSE(absl::FormatUntyped(&actual, f1, {absl::FormatArg(p)})); } + +struct PointStringify { + template <typename FormatSink> + friend void AbslStringify(FormatSink& sink, const PointStringify& p) { + sink.Append(absl::StrCat("(", p.x, ", ", p.y, ")")); + } + + double x = 10.0; + double y = 20.0; +}; + +TEST_F(FormatExtensionTest, AbslStringifyExample) { + PointStringify 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