Allows absl::StrFormat to accept types which implement AbslStringify()

PiperOrigin-RevId: 477507777
Change-Id: I5ecde3163ca560ac8774034e55654774e36ad230
pull/1283/head
Abseil Team 2 years ago committed by Copybara-Service
parent 6d9ea2b46f
commit f7404cd33f
  1. 34
      absl/strings/internal/str_format/arg.h
  2. 19
      absl/strings/str_format_test.cc

@ -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.

@ -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

Loading…
Cancel
Save