Adds support for "%v" in absl::StrFormat and related functions for numeric types, including integer and floating point values. Users may now specify %v and have the format specifier deduced. Integer values will print according to %d specifications, unsigned values will use %u, and floating point values will use %g. Note that %v does not work for char due to ambiguity regarding the intended output. Please continue to use %c for char.

PiperOrigin-RevId: 474658166
Change-Id: Iecae39263e368b27232db440535f2bf7da8d863c
This commit is contained in:
Abseil Team
2022-09-15 14:29:39 -07:00
committed by Copybara-Service
parent d423ac0ef0
commit ab2e2c4f60
6 changed files with 250 additions and 79 deletions

View File

@@ -42,6 +42,18 @@ TEST_F(FormatEntryPointTest, Format) {
EXPECT_TRUE(Format(&sink, pc, 123));
EXPECT_EQ("A format 123", sink);
}
TEST_F(FormatEntryPointTest, FormatWithV) {
std::string sink;
EXPECT_TRUE(Format(&sink, "A format %v", 123));
EXPECT_EQ("A format 123", sink);
sink.clear();
ParsedFormat<'v'> pc("A format %v");
EXPECT_TRUE(Format(&sink, pc, 123));
EXPECT_EQ("A format 123", sink);
}
TEST_F(FormatEntryPointTest, UntypedFormat) {
constexpr const char* formats[] = {
"",
@@ -87,6 +99,9 @@ TEST_F(FormatEntryPointTest, StringFormat) {
TEST_F(FormatEntryPointTest, StringFormatV) {
std::string hello = "hello";
EXPECT_EQ("hello", StrFormat("%v", hello));
EXPECT_EQ("123", StrFormat("%v", 123));
constexpr absl::string_view view("=%v=", 4);
EXPECT_EQ("=123=", StrFormat(view, 123));
}
TEST_F(FormatEntryPointTest, AppendFormat) {
@@ -96,6 +111,13 @@ TEST_F(FormatEntryPointTest, AppendFormat) {
EXPECT_EQ("123", r);
}
TEST_F(FormatEntryPointTest, AppendFormatWithV) {
std::string s;
std::string& r = StrAppendFormat(&s, "%v", 123);
EXPECT_EQ(&s, &r); // should be same object
EXPECT_EQ("123", r);
}
TEST_F(FormatEntryPointTest, AppendFormatFail) {
std::string s = "orig";
@@ -108,6 +130,17 @@ TEST_F(FormatEntryPointTest, AppendFormatFail) {
{&arg, 1}));
}
TEST_F(FormatEntryPointTest, AppendFormatFailWithV) {
std::string s = "orig";
UntypedFormatSpec format(" more %v");
FormatArgImpl arg("not an int");
EXPECT_EQ("orig",
str_format_internal::AppendPack(
&s, str_format_internal::UntypedFormatSpecImpl::Extract(format),
{&arg, 1}));
}
TEST_F(FormatEntryPointTest, ManyArgs) {
EXPECT_EQ("24", StrFormat("%24$d", 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13,
@@ -128,6 +161,15 @@ TEST_F(FormatEntryPointTest, Preparsed) {
EXPECT_EQ("=123=", StrFormat(ParsedFormat<'d'>(view), 123));
}
TEST_F(FormatEntryPointTest, PreparsedWithV) {
ParsedFormat<'v'> pc("%v");
EXPECT_EQ("123", StrFormat(pc, 123));
// rvalue ok?
EXPECT_EQ("123", StrFormat(ParsedFormat<'v'>("%v"), 123));
constexpr absl::string_view view("=%v=", 4);
EXPECT_EQ("=123=", StrFormat(ParsedFormat<'v'>(view), 123));
}
TEST_F(FormatEntryPointTest, FormatCountCapture) {
int n = 0;
EXPECT_EQ("", StrFormat("%n", FormatCountCapture(&n)));
@@ -136,6 +178,14 @@ TEST_F(FormatEntryPointTest, FormatCountCapture) {
EXPECT_EQ(3, n);
}
TEST_F(FormatEntryPointTest, FormatCountCaptureWithV) {
int n = 0;
EXPECT_EQ("", StrFormat("%n", FormatCountCapture(&n)));
EXPECT_EQ(0, n);
EXPECT_EQ("123", StrFormat("%v%n", 123, FormatCountCapture(&n)));
EXPECT_EQ(3, n);
}
TEST_F(FormatEntryPointTest, FormatCountCaptureWrongType) {
// Should reject int*.
int n = 0;
@@ -148,6 +198,18 @@ TEST_F(FormatEntryPointTest, FormatCountCaptureWrongType) {
absl::MakeSpan(args)));
}
TEST_F(FormatEntryPointTest, FormatCountCaptureWrongTypeWithV) {
// Should reject int*.
int n = 0;
UntypedFormatSpec format("%v%n");
int i = 123, *ip = &n;
FormatArgImpl args[2] = {FormatArgImpl(i), FormatArgImpl(ip)};
EXPECT_EQ("", str_format_internal::FormatPack(
str_format_internal::UntypedFormatSpecImpl::Extract(format),
absl::MakeSpan(args)));
}
TEST_F(FormatEntryPointTest, FormatCountCaptureMultiple) {
int n1 = 0;
int n2 = 0;
@@ -212,23 +274,33 @@ TEST_F(FormatEntryPointTest, Stream) {
}
TEST_F(FormatEntryPointTest, StreamWithV) {
const std::string format = "%d %u %c %v %f %g";
const std::string formats[] = {
"",
"a",
"%v %u %c %v %f %v",
};
const std::string format_for_buf = "%d %u %c %s %f %g";
const std::string formats_for_buf[] = {
"",
"a",
"%d %u %c %s %f %g",
};
std::string buf(4096, '\0');
const auto parsed =
ParsedFormat<'d', 'u', 'c', 'v', 'f', 'g'>::NewAllowIgnored(format);
std::ostringstream oss;
oss << StreamFormat(*parsed, 123, 3, 49,
absl::string_view("multistreaming!!!"), 1.01, 1.01);
int fmt_result =
snprintf(&*buf.begin(), buf.size(), format_for_buf.c_str(), //
123, 3, 49, "multistreaming!!!", 1.01, 1.01);
ASSERT_TRUE(oss) << format;
ASSERT_TRUE(fmt_result >= 0 && static_cast<size_t>(fmt_result) < buf.size())
<< fmt_result;
EXPECT_EQ(buf.c_str(), oss.str());
for (auto i = 0; i < ABSL_ARRAYSIZE(formats); ++i) {
const auto parsed =
ParsedFormat<'v', 'u', 'c', 'v', 'f', 'v'>::NewAllowIgnored(formats[i]);
std::ostringstream oss;
oss << StreamFormat(*parsed, 123, 3, 49,
absl::string_view("multistreaming!!!"), 1.01, 1.01);
int fmt_result =
snprintf(&*buf.begin(), buf.size(), formats_for_buf[i].c_str(), //
123, 3, 49, "multistreaming!!!", 1.01, 1.01);
ASSERT_TRUE(oss) << formats[i];
ASSERT_TRUE(fmt_result >= 0 && static_cast<size_t>(fmt_result) < buf.size())
<< fmt_result;
EXPECT_EQ(buf.c_str(), oss.str());
}
}
TEST_F(FormatEntryPointTest, StreamOk) {
@@ -238,6 +310,13 @@ TEST_F(FormatEntryPointTest, StreamOk) {
EXPECT_TRUE(oss.good());
}
TEST_F(FormatEntryPointTest, StreamOkWithV) {
std::ostringstream oss;
oss << StreamFormat("hello %v", 123);
EXPECT_EQ("hello 123", oss.str());
EXPECT_TRUE(oss.good());
}
TEST_F(FormatEntryPointTest, StreamFail) {
std::ostringstream oss;
UntypedFormatSpec format("hello %d");
@@ -248,6 +327,16 @@ TEST_F(FormatEntryPointTest, StreamFail) {
EXPECT_TRUE(oss.fail());
}
TEST_F(FormatEntryPointTest, StreamFailWithV) {
std::ostringstream oss;
UntypedFormatSpec format("hello %v");
FormatArgImpl arg("non-numeric");
oss << str_format_internal::Streamable(
str_format_internal::UntypedFormatSpecImpl::Extract(format), {&arg, 1});
EXPECT_EQ("hello ", oss.str()); // partial write
EXPECT_TRUE(oss.fail());
}
std::string WithSnprintf(const char* fmt, ...) {
std::string buf;
buf.resize(128);
@@ -425,19 +514,19 @@ TEST_F(FormatEntryPointTest, SNPrintFWithV) {
EXPECT_EQ(result, 11);
EXPECT_EQ(std::string(buffer), "STRING: ABC");
result = SNPrintF(buffer, sizeof(buffer), "NUMBER: %d", 123456);
result = SNPrintF(buffer, sizeof(buffer), "NUMBER: %v", 123456);
EXPECT_EQ(result, 14);
EXPECT_EQ(std::string(buffer), "NUMBER: 123456");
result = SNPrintF(buffer, sizeof(buffer), "NUMBER: %d", 1234567);
result = SNPrintF(buffer, sizeof(buffer), "NUMBER: %v", 1234567);
EXPECT_EQ(result, 15);
EXPECT_EQ(std::string(buffer), "NUMBER: 1234567");
result = SNPrintF(buffer, sizeof(buffer), "NUMBER: %d", 12345678);
result = SNPrintF(buffer, sizeof(buffer), "NUMBER: %v", 12345678);
EXPECT_EQ(result, 16);
EXPECT_EQ(std::string(buffer), "NUMBER: 1234567");
result = SNPrintF(buffer, sizeof(buffer), "NUMBER: %d", 123456789);
result = SNPrintF(buffer, sizeof(buffer), "NUMBER: %v", 123456789);
EXPECT_EQ(result, 17);
EXPECT_EQ(std::string(buffer), "NUMBER: 1234567");
@@ -451,7 +540,7 @@ TEST(StrFormat, BehavesAsDocumented) {
std::string s = absl::StrFormat("%s, %d!", "Hello", 123);
EXPECT_EQ("Hello, 123!", s);
std::string hello = "Hello";
std::string s2 = absl::StrFormat("%v, %d!", hello, 123);
std::string s2 = absl::StrFormat("%v, %v!", hello, 123);
EXPECT_EQ("Hello, 123!", s2);
// The format of a replacement is
// '%'[position][flags][width['.'precision]][length_modifier][format]
@@ -480,13 +569,18 @@ TEST(StrFormat, BehavesAsDocumented) {
EXPECT_EQ(StrFormat("%d", int{10}), "10");
EXPECT_EQ(StrFormat("%d", long{10}), "10"); // NOLINT
EXPECT_EQ(StrFormat("%d", uint64_t{10}), "10");
EXPECT_EQ(StrFormat("%v", int{10}), "10");
EXPECT_EQ(StrFormat("%v", long{10}), "10"); // NOLINT
EXPECT_EQ(StrFormat("%v", uint64_t{10}), "10");
// d,i - signed decimal Eg: -10 -> "-10"
EXPECT_EQ(StrFormat("%d", -10), "-10");
EXPECT_EQ(StrFormat("%i", -10), "-10");
EXPECT_EQ(StrFormat("%v", -10), "-10");
// o - octal Eg: 10 -> "12"
EXPECT_EQ(StrFormat("%o", 10), "12");
// u - unsigned decimal Eg: 10 -> "10"
EXPECT_EQ(StrFormat("%u", 10), "10");
EXPECT_EQ(StrFormat("%v", 10), "10");
// x/X - lower,upper case hex Eg: 10 -> "a"/"A"
EXPECT_EQ(StrFormat("%x", 10), "a");
EXPECT_EQ(StrFormat("%X", 10), "A");
@@ -511,6 +605,8 @@ TEST(StrFormat, BehavesAsDocumented) {
EXPECT_EQ(StrFormat("%g", .01), "0.01");
EXPECT_EQ(StrFormat("%g", 1e10), "1e+10");
EXPECT_EQ(StrFormat("%G", 1e10), "1E+10");
EXPECT_EQ(StrFormat("%v", .01), "0.01");
EXPECT_EQ(StrFormat("%v", 1e10), "1e+10");
// a/A - lower,upper case hex Eg: -3.0 -> "-0x1.8p+1"/"-0X1.8P+1"
// On Android platform <=21, there is a regression in hexfloat formatting.
@@ -598,10 +694,10 @@ TEST_F(ParsedFormatTest, SimpleChecked) {
}
TEST_F(ParsedFormatTest, SimpleCheckedWithV) {
EXPECT_EQ("[ABC]{d:1$d}[DEF]",
SummarizeParsedFormat(ParsedFormat<'d'>("ABC%dDEF")));
EXPECT_EQ("{v:1$v}[FFF]{d:2$d}[ZZZ]{f:3$f}",
SummarizeParsedFormat(ParsedFormat<'v', 'd', 'f'>("%vFFF%dZZZ%f")));
EXPECT_EQ("[ABC]{v:1$v}[DEF]",
SummarizeParsedFormat(ParsedFormat<'v'>("ABC%vDEF")));
EXPECT_EQ("{v:1$v}[FFF]{v:2$v}[ZZZ]{f:3$f}",
SummarizeParsedFormat(ParsedFormat<'v', 'v', 'f'>("%vFFF%vZZZ%f")));
EXPECT_EQ("{v:1$v}[ ]{.*d:3$.2$*d}",
SummarizeParsedFormat(ParsedFormat<'v', '*', 'd'>("%v %.*d")));
}
@@ -637,20 +733,20 @@ TEST_F(ParsedFormatTest, SimpleUncheckedCorrect) {
}
TEST_F(ParsedFormatTest, SimpleUncheckedCorrectWithV) {
auto f = ParsedFormat<'d'>::New("ABC%dDEF");
auto f = ParsedFormat<'v'>::New("ABC%vDEF");
ASSERT_TRUE(f);
EXPECT_EQ("[ABC]{d:1$d}[DEF]", SummarizeParsedFormat(*f));
EXPECT_EQ("[ABC]{v:1$v}[DEF]", SummarizeParsedFormat(*f));
std::string format = "%vFFF%dZZZ%f";
auto f2 = ParsedFormat<'v', 'd', 'f'>::New(format);
std::string format = "%vFFF%vZZZ%f";
auto f2 = ParsedFormat<'v', 'v', 'f'>::New(format);
ASSERT_TRUE(f2);
EXPECT_EQ("{v:1$v}[FFF]{d:2$d}[ZZZ]{f:3$f}", SummarizeParsedFormat(*f2));
EXPECT_EQ("{v:1$v}[FFF]{v:2$v}[ZZZ]{f:3$f}", SummarizeParsedFormat(*f2));
f2 = ParsedFormat<'v', 'd', 'f'>::New("%v %d %f");
f2 = ParsedFormat<'v', 'v', 'f'>::New("%v %v %f");
ASSERT_TRUE(f2);
EXPECT_EQ("{v:1$v}[ ]{d:2$d}[ ]{f:3$f}", SummarizeParsedFormat(*f2));
EXPECT_EQ("{v:1$v}[ ]{v:2$v}[ ]{f:3$f}", SummarizeParsedFormat(*f2));
}
TEST_F(ParsedFormatTest, SimpleUncheckedIgnoredArgs) {
@@ -668,6 +764,18 @@ TEST_F(ParsedFormatTest, SimpleUncheckedIgnoredArgs) {
EXPECT_EQ("[ABC]{2$s:2$s}", SummarizeParsedFormat(*f));
}
TEST_F(ParsedFormatTest, SimpleUncheckedIgnoredArgsWithV) {
EXPECT_FALSE((ParsedFormat<'v', 'v'>::New("ABC")));
EXPECT_FALSE((ParsedFormat<'v', 'v'>::New("%vABC")));
EXPECT_FALSE((ParsedFormat<'v', 's'>::New("ABC%2$s")));
auto f = ParsedFormat<'v', 'v'>::NewAllowIgnored("ABC");
ASSERT_TRUE(f);
EXPECT_EQ("[ABC]", SummarizeParsedFormat(*f));
f = ParsedFormat<'v', 'v'>::NewAllowIgnored("%vABC");
ASSERT_TRUE(f);
EXPECT_EQ("{v:1$v}[ABC]", SummarizeParsedFormat(*f));
}
TEST_F(ParsedFormatTest, SimpleUncheckedUnsupported) {
EXPECT_FALSE(ParsedFormat<'d'>::New("%1$d %1$x"));
EXPECT_FALSE(ParsedFormat<'x'>::New("%1$d %1$x"));
@@ -683,12 +791,12 @@ TEST_F(ParsedFormatTest, SimpleUncheckedIncorrect) {
}
TEST_F(ParsedFormatTest, SimpleUncheckedIncorrectWithV) {
EXPECT_FALSE(ParsedFormat<'d'>::New(""));
EXPECT_FALSE(ParsedFormat<'v'>::New(""));
EXPECT_FALSE(ParsedFormat<'d'>::New("ABC%dDEF%d"));
EXPECT_FALSE(ParsedFormat<'v'>::New("ABC%vDEF%v"));
std::string format = "%vFFF%dZZZ%f";
EXPECT_FALSE((ParsedFormat<'v', 'd', 'g'>::New(format)));
std::string format = "%vFFF%vZZZ%f";
EXPECT_FALSE((ParsedFormat<'v', 'v', 'g'>::New(format)));
}
#if defined(__cpp_nontype_template_parameter_auto)
@@ -739,17 +847,17 @@ TEST_F(ParsedFormatTest, ExtendedTyping) {
}
TEST_F(ParsedFormatTest, ExtendedTypingWithV) {
EXPECT_FALSE(ParsedFormat<FormatConversionCharSet::d>::New(""));
ASSERT_TRUE(ParsedFormat<absl::FormatConversionCharSet::d>::New("%d"));
auto v1 = ParsedFormat<'d', absl::FormatConversionCharSet::v>::New("%d%v");
EXPECT_FALSE(ParsedFormat<FormatConversionCharSet::v>::New(""));
ASSERT_TRUE(ParsedFormat<absl::FormatConversionCharSet::v>::New("%v"));
auto v1 = ParsedFormat<'v', absl::FormatConversionCharSet::v>::New("%v%v");
ASSERT_TRUE(v1);
auto v2 = ParsedFormat<absl::FormatConversionCharSet::d, 'v'>::New("%d%v");
auto v2 = ParsedFormat<absl::FormatConversionCharSet::v, 'v'>::New("%v%v");
ASSERT_TRUE(v2);
auto v3 = ParsedFormat<absl::FormatConversionCharSet::d |
auto v3 = ParsedFormat<absl::FormatConversionCharSet::v |
absl::FormatConversionCharSet::v,
'v'>::New("%d%v");
'v'>::New("%v%v");
ASSERT_TRUE(v3);
auto v4 = ParsedFormat<absl::FormatConversionCharSet::d |
auto v4 = ParsedFormat<absl::FormatConversionCharSet::v |
absl::FormatConversionCharSet::v,
'v'>::New("%v%v");
ASSERT_TRUE(v4);
@@ -799,24 +907,24 @@ TEST_F(ParsedFormatTest, UncheckedCorrect) {
TEST_F(ParsedFormatTest, UncheckedCorrectWithV) {
auto f =
ExtendedParsedFormat<absl::FormatConversionCharSet::d>::New("ABC%dDEF");
ExtendedParsedFormat<absl::FormatConversionCharSet::v>::New("ABC%vDEF");
ASSERT_TRUE(f);
EXPECT_EQ("[ABC]{d:1$d}[DEF]", SummarizeParsedFormat(*f));
EXPECT_EQ("[ABC]{v:1$v}[DEF]", SummarizeParsedFormat(*f));
std::string format = "%vFFF%dZZZ%f";
std::string format = "%vFFF%vZZZ%f";
auto f2 = ExtendedParsedFormat<
absl::FormatConversionCharSet::v, absl::FormatConversionCharSet::d,
absl::FormatConversionCharSet::v, absl::FormatConversionCharSet::v,
absl::FormatConversionCharSet::kFloating>::New(format);
ASSERT_TRUE(f2);
EXPECT_EQ("{v:1$v}[FFF]{d:2$d}[ZZZ]{f:3$f}", SummarizeParsedFormat(*f2));
EXPECT_EQ("{v:1$v}[FFF]{v:2$v}[ZZZ]{f:3$f}", SummarizeParsedFormat(*f2));
f2 = ExtendedParsedFormat<
absl::FormatConversionCharSet::v, absl::FormatConversionCharSet::d,
absl::FormatConversionCharSet::kFloating>::New("%v %d %f");
absl::FormatConversionCharSet::v, absl::FormatConversionCharSet::v,
absl::FormatConversionCharSet::kFloating>::New("%v %v %f");
ASSERT_TRUE(f2);
EXPECT_EQ("{v:1$v}[ ]{d:2$d}[ ]{f:3$f}", SummarizeParsedFormat(*f2));
EXPECT_EQ("{v:1$v}[ ]{v:2$v}[ ]{f:3$f}", SummarizeParsedFormat(*f2));
}
TEST_F(ParsedFormatTest, UncheckedIgnoredArgs) {
@@ -846,6 +954,28 @@ TEST_F(ParsedFormatTest, UncheckedIgnoredArgs) {
EXPECT_EQ("[ABC]{2$s:2$s}", SummarizeParsedFormat(*f));
}
TEST_F(ParsedFormatTest, UncheckedIgnoredArgsWithV) {
EXPECT_FALSE(
(ExtendedParsedFormat<absl::FormatConversionCharSet::v,
absl::FormatConversionCharSet::v>::New("ABC")));
EXPECT_FALSE(
(ExtendedParsedFormat<absl::FormatConversionCharSet::v,
absl::FormatConversionCharSet::v>::New("%vABC")));
EXPECT_FALSE((ExtendedParsedFormat<absl::FormatConversionCharSet::v,
absl::FormatConversionCharSet::s>::
New("ABC%2$s")));
auto f = ExtendedParsedFormat<
absl::FormatConversionCharSet::v,
absl::FormatConversionCharSet::v>::NewAllowIgnored("ABC");
ASSERT_TRUE(f);
EXPECT_EQ("[ABC]", SummarizeParsedFormat(*f));
f = ExtendedParsedFormat<
absl::FormatConversionCharSet::v,
absl::FormatConversionCharSet::v>::NewAllowIgnored("%vABC");
ASSERT_TRUE(f);
EXPECT_EQ("{v:1$v}[ABC]", SummarizeParsedFormat(*f));
}
TEST_F(ParsedFormatTest, UncheckedMultipleTypes) {
auto dx =
ExtendedParsedFormat<absl::FormatConversionCharSet::d |
@@ -873,15 +1003,14 @@ TEST_F(ParsedFormatTest, UncheckedIncorrect) {
}
TEST_F(ParsedFormatTest, UncheckedIncorrectWithV) {
EXPECT_FALSE(ExtendedParsedFormat<absl::FormatConversionCharSet::d>::New(""));
EXPECT_FALSE(ExtendedParsedFormat<absl::FormatConversionCharSet::v>::New(""));
EXPECT_FALSE(ExtendedParsedFormat<absl::FormatConversionCharSet::d>::New(
"ABC%dDEF%d"));
EXPECT_FALSE(ExtendedParsedFormat<absl::FormatConversionCharSet::v>::New(
"ABC%vDEF%v"));
std::string format = "%vFFF%dZZZ%f";
std::string format = "%vFFF%vZZZ%f";
EXPECT_FALSE(
(ExtendedParsedFormat<absl::FormatConversionCharSet::v,
absl::FormatConversionCharSet::d,
absl::FormatConversionCharSet::g>::New(format)));
}