Commit ab2e2c4f by Abseil Team Committed by Copybara-Service

Adds support for "%v" in absl::StrFormat and related functions for numeric…

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
parent d423ac0e
...@@ -278,12 +278,33 @@ bool ConvertIntImplInnerSlow(const IntDigits &as_digits, ...@@ -278,12 +278,33 @@ bool ConvertIntImplInnerSlow(const IntDigits &as_digits,
return true; return true;
} }
template <typename T,
typename std::enable_if<(std::is_integral<T>::value &&
std::is_signed<T>::value) ||
std::is_same<T, int128>::value,
int>::type = 0>
constexpr auto ConvertV(T) {
return FormatConversionCharInternal::d;
}
template <typename T,
typename std::enable_if<(std::is_integral<T>::value &&
std::is_unsigned<T>::value) ||
std::is_same<T, uint128>::value,
int>::type = 0>
constexpr auto ConvertV(T) {
return FormatConversionCharInternal::u;
}
template <typename T> template <typename T>
bool ConvertIntArg(T v, const FormatConversionSpecImpl conv, bool ConvertIntArg(T v, FormatConversionSpecImpl conv, FormatSinkImpl *sink) {
FormatSinkImpl *sink) {
using U = typename MakeUnsigned<T>::type; using U = typename MakeUnsigned<T>::type;
IntDigits as_digits; IntDigits as_digits;
if (conv.conversion_char() == FormatConversionCharInternal::v) {
conv.set_conversion_char(ConvertV(T{}));
}
// This odd casting is due to a bug in -Wswitch behavior in gcc49 which causes // This odd casting is due to a bug in -Wswitch behavior in gcc49 which causes
// it to complain about a switch/case type mismatch, even though both are // it to complain about a switch/case type mismatch, even though both are
// FormatConverionChar. Likely this is because at this point // FormatConverionChar. Likely this is because at this point
...@@ -334,8 +355,11 @@ bool ConvertIntArg(T v, const FormatConversionSpecImpl conv, ...@@ -334,8 +355,11 @@ bool ConvertIntArg(T v, const FormatConversionSpecImpl conv,
} }
template <typename T> template <typename T>
bool ConvertFloatArg(T v, const FormatConversionSpecImpl conv, bool ConvertFloatArg(T v, FormatConversionSpecImpl conv, FormatSinkImpl *sink) {
FormatSinkImpl *sink) { if (conv.conversion_char() == FormatConversionCharInternal::v) {
conv.set_conversion_char(FormatConversionCharInternal::g);
}
return FormatConversionCharIsFloat(conv.conversion_char()) && return FormatConversionCharIsFloat(conv.conversion_char()) &&
ConvertFloatImpl(v, conv, sink); ConvertFloatImpl(v, conv, sink);
} }
...@@ -413,17 +437,16 @@ FloatingConvertResult FormatConvertImpl(long double v, ...@@ -413,17 +437,16 @@ FloatingConvertResult FormatConvertImpl(long double v,
} }
// ==================== Chars ==================== // ==================== Chars ====================
IntegralConvertResult FormatConvertImpl(char v, CharConvertResult FormatConvertImpl(char v, const FormatConversionSpecImpl conv,
const FormatConversionSpecImpl conv,
FormatSinkImpl *sink) { FormatSinkImpl *sink) {
return {ConvertIntArg(v, conv, sink)}; return {ConvertIntArg(v, conv, sink)};
} }
IntegralConvertResult FormatConvertImpl(signed char v, CharConvertResult FormatConvertImpl(signed char v,
const FormatConversionSpecImpl conv, const FormatConversionSpecImpl conv,
FormatSinkImpl *sink) { FormatSinkImpl *sink) {
return {ConvertIntArg(v, conv, sink)}; return {ConvertIntArg(v, conv, sink)};
} }
IntegralConvertResult FormatConvertImpl(unsigned char v, CharConvertResult FormatConvertImpl(unsigned char v,
const FormatConversionSpecImpl conv, const FormatConversionSpecImpl conv,
FormatSinkImpl *sink) { FormatSinkImpl *sink) {
return {ConvertIntArg(v, conv, sink)}; return {ConvertIntArg(v, conv, sink)};
......
...@@ -176,9 +176,15 @@ StringConvertResult FormatConvertImpl(const AbslCord& value, ...@@ -176,9 +176,15 @@ StringConvertResult FormatConvertImpl(const AbslCord& value,
using IntegralConvertResult = ArgConvertResult<FormatConversionCharSetUnion( using IntegralConvertResult = ArgConvertResult<FormatConversionCharSetUnion(
FormatConversionCharSetInternal::c, FormatConversionCharSetInternal::c,
FormatConversionCharSetInternal::kNumeric, FormatConversionCharSetInternal::kNumeric,
FormatConversionCharSetInternal::kStar,
FormatConversionCharSetInternal::v)>;
using FloatingConvertResult = ArgConvertResult<FormatConversionCharSetUnion(
FormatConversionCharSetInternal::kFloating,
FormatConversionCharSetInternal::v)>;
using CharConvertResult = ArgConvertResult<FormatConversionCharSetUnion(
FormatConversionCharSetInternal::c,
FormatConversionCharSetInternal::kNumeric,
FormatConversionCharSetInternal::kStar)>; FormatConversionCharSetInternal::kStar)>;
using FloatingConvertResult =
ArgConvertResult<FormatConversionCharSetInternal::kFloating>;
// Floats. // Floats.
FloatingConvertResult FormatConvertImpl(float v, FormatConversionSpecImpl conv, FloatingConvertResult FormatConvertImpl(float v, FormatConversionSpecImpl conv,
...@@ -190,12 +196,12 @@ FloatingConvertResult FormatConvertImpl(long double v, ...@@ -190,12 +196,12 @@ FloatingConvertResult FormatConvertImpl(long double v,
FormatSinkImpl* sink); FormatSinkImpl* sink);
// Chars. // Chars.
IntegralConvertResult FormatConvertImpl(char v, FormatConversionSpecImpl conv, CharConvertResult FormatConvertImpl(char v, FormatConversionSpecImpl conv,
FormatSinkImpl* sink); FormatSinkImpl* sink);
IntegralConvertResult FormatConvertImpl(signed char v, CharConvertResult FormatConvertImpl(signed char v,
FormatConversionSpecImpl conv, FormatConversionSpecImpl conv,
FormatSinkImpl* sink); FormatSinkImpl* sink);
IntegralConvertResult FormatConvertImpl(unsigned char v, CharConvertResult FormatConvertImpl(unsigned char v,
FormatConversionSpecImpl conv, FormatConversionSpecImpl conv,
FormatSinkImpl* sink); FormatSinkImpl* sink);
......
...@@ -45,10 +45,10 @@ TEST(StrFormatChecker, ArgumentToConv) { ...@@ -45,10 +45,10 @@ TEST(StrFormatChecker, ArgumentToConv) {
EXPECT_EQ(ConvToString(conv), "sp"); EXPECT_EQ(ConvToString(conv), "sp");
conv = ArgumentToConv<double>(); conv = ArgumentToConv<double>();
EXPECT_EQ(ConvToString(conv), "fFeEgGaA"); EXPECT_EQ(ConvToString(conv), "fFeEgGaAv");
conv = ArgumentToConv<int>(); conv = ArgumentToConv<int>();
EXPECT_EQ(ConvToString(conv), "cdiouxXfFeEgGaA*"); EXPECT_EQ(ConvToString(conv), "cdiouxXfFeEgGaAv*");
conv = ArgumentToConv<std::string*>(); conv = ArgumentToConv<std::string*>();
EXPECT_EQ(ConvToString(conv), "p"); EXPECT_EQ(ConvToString(conv), "p");
......
...@@ -292,6 +292,8 @@ class FormatConversionSpecImpl { ...@@ -292,6 +292,8 @@ class FormatConversionSpecImpl {
return conv_; return conv_;
} }
void set_conversion_char(FormatConversionChar c) { conv_ = c; }
// Returns the specified width. If width is unspecfied, it returns a negative // Returns the specified width. If width is unspecfied, it returns a negative
// value. // value.
int width() const { return width_; } int width() const { return width_; }
......
...@@ -19,6 +19,7 @@ ...@@ -19,6 +19,7 @@
#include <random> #include <random>
#include <string> #include <string>
#include "gmock/gmock.h"
#include "gtest/gtest.h" #include "gtest/gtest.h"
#include "absl/strings/str_format.h" #include "absl/strings/str_format.h"
#include "absl/strings/string_view.h" #include "absl/strings/string_view.h"
...@@ -95,4 +96,14 @@ TEST(FormatExtensionTest, VerifyEnumEquality) { ...@@ -95,4 +96,14 @@ TEST(FormatExtensionTest, VerifyEnumEquality) {
#undef X_VAL #undef X_VAL
} }
TEST(FormatExtensionTest, SetConversionChar) {
absl::str_format_internal::FormatConversionSpecImpl spec;
EXPECT_EQ(spec.conversion_char(),
absl::str_format_internal::FormatConversionCharInternal::kNone);
spec.set_conversion_char(
absl::str_format_internal::FormatConversionCharInternal::d);
EXPECT_EQ(spec.conversion_char(),
absl::str_format_internal::FormatConversionCharInternal::d);
}
} // namespace } // namespace
...@@ -42,6 +42,18 @@ TEST_F(FormatEntryPointTest, Format) { ...@@ -42,6 +42,18 @@ TEST_F(FormatEntryPointTest, Format) {
EXPECT_TRUE(Format(&sink, pc, 123)); EXPECT_TRUE(Format(&sink, pc, 123));
EXPECT_EQ("A format 123", sink); 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) { TEST_F(FormatEntryPointTest, UntypedFormat) {
constexpr const char* formats[] = { constexpr const char* formats[] = {
"", "",
...@@ -87,6 +99,9 @@ TEST_F(FormatEntryPointTest, StringFormat) { ...@@ -87,6 +99,9 @@ TEST_F(FormatEntryPointTest, StringFormat) {
TEST_F(FormatEntryPointTest, StringFormatV) { TEST_F(FormatEntryPointTest, StringFormatV) {
std::string hello = "hello"; std::string hello = "hello";
EXPECT_EQ("hello", StrFormat("%v", 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) { TEST_F(FormatEntryPointTest, AppendFormat) {
...@@ -96,6 +111,13 @@ TEST_F(FormatEntryPointTest, AppendFormat) { ...@@ -96,6 +111,13 @@ TEST_F(FormatEntryPointTest, AppendFormat) {
EXPECT_EQ("123", r); 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) { TEST_F(FormatEntryPointTest, AppendFormatFail) {
std::string s = "orig"; std::string s = "orig";
...@@ -108,6 +130,17 @@ TEST_F(FormatEntryPointTest, AppendFormatFail) { ...@@ -108,6 +130,17 @@ TEST_F(FormatEntryPointTest, AppendFormatFail) {
{&arg, 1})); {&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) { TEST_F(FormatEntryPointTest, ManyArgs) {
EXPECT_EQ("24", StrFormat("%24$d", 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 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) { ...@@ -128,6 +161,15 @@ TEST_F(FormatEntryPointTest, Preparsed) {
EXPECT_EQ("=123=", StrFormat(ParsedFormat<'d'>(view), 123)); 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) { TEST_F(FormatEntryPointTest, FormatCountCapture) {
int n = 0; int n = 0;
EXPECT_EQ("", StrFormat("%n", FormatCountCapture(&n))); EXPECT_EQ("", StrFormat("%n", FormatCountCapture(&n)));
...@@ -136,6 +178,14 @@ TEST_F(FormatEntryPointTest, FormatCountCapture) { ...@@ -136,6 +178,14 @@ TEST_F(FormatEntryPointTest, FormatCountCapture) {
EXPECT_EQ(3, n); 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) { TEST_F(FormatEntryPointTest, FormatCountCaptureWrongType) {
// Should reject int*. // Should reject int*.
int n = 0; int n = 0;
...@@ -148,6 +198,18 @@ TEST_F(FormatEntryPointTest, FormatCountCaptureWrongType) { ...@@ -148,6 +198,18 @@ TEST_F(FormatEntryPointTest, FormatCountCaptureWrongType) {
absl::MakeSpan(args))); 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) { TEST_F(FormatEntryPointTest, FormatCountCaptureMultiple) {
int n1 = 0; int n1 = 0;
int n2 = 0; int n2 = 0;
...@@ -212,23 +274,33 @@ TEST_F(FormatEntryPointTest, Stream) { ...@@ -212,23 +274,33 @@ TEST_F(FormatEntryPointTest, Stream) {
} }
TEST_F(FormatEntryPointTest, StreamWithV) { 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'); std::string buf(4096, '\0');
for (auto i = 0; i < ABSL_ARRAYSIZE(formats); ++i) {
const auto parsed = const auto parsed =
ParsedFormat<'d', 'u', 'c', 'v', 'f', 'g'>::NewAllowIgnored(format); ParsedFormat<'v', 'u', 'c', 'v', 'f', 'v'>::NewAllowIgnored(formats[i]);
std::ostringstream oss; std::ostringstream oss;
oss << StreamFormat(*parsed, 123, 3, 49, oss << StreamFormat(*parsed, 123, 3, 49,
absl::string_view("multistreaming!!!"), 1.01, 1.01); absl::string_view("multistreaming!!!"), 1.01, 1.01);
int fmt_result = int fmt_result =
snprintf(&*buf.begin(), buf.size(), format_for_buf.c_str(), // snprintf(&*buf.begin(), buf.size(), formats_for_buf[i].c_str(), //
123, 3, 49, "multistreaming!!!", 1.01, 1.01); 123, 3, 49, "multistreaming!!!", 1.01, 1.01);
ASSERT_TRUE(oss) << format; ASSERT_TRUE(oss) << formats[i];
ASSERT_TRUE(fmt_result >= 0 && static_cast<size_t>(fmt_result) < buf.size()) ASSERT_TRUE(fmt_result >= 0 && static_cast<size_t>(fmt_result) < buf.size())
<< fmt_result; << fmt_result;
EXPECT_EQ(buf.c_str(), oss.str()); EXPECT_EQ(buf.c_str(), oss.str());
}
} }
TEST_F(FormatEntryPointTest, StreamOk) { TEST_F(FormatEntryPointTest, StreamOk) {
...@@ -238,6 +310,13 @@ TEST_F(FormatEntryPointTest, StreamOk) { ...@@ -238,6 +310,13 @@ TEST_F(FormatEntryPointTest, StreamOk) {
EXPECT_TRUE(oss.good()); 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) { TEST_F(FormatEntryPointTest, StreamFail) {
std::ostringstream oss; std::ostringstream oss;
UntypedFormatSpec format("hello %d"); UntypedFormatSpec format("hello %d");
...@@ -248,6 +327,16 @@ TEST_F(FormatEntryPointTest, StreamFail) { ...@@ -248,6 +327,16 @@ TEST_F(FormatEntryPointTest, StreamFail) {
EXPECT_TRUE(oss.fail()); 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 WithSnprintf(const char* fmt, ...) {
std::string buf; std::string buf;
buf.resize(128); buf.resize(128);
...@@ -425,19 +514,19 @@ TEST_F(FormatEntryPointTest, SNPrintFWithV) { ...@@ -425,19 +514,19 @@ TEST_F(FormatEntryPointTest, SNPrintFWithV) {
EXPECT_EQ(result, 11); EXPECT_EQ(result, 11);
EXPECT_EQ(std::string(buffer), "STRING: ABC"); 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(result, 14);
EXPECT_EQ(std::string(buffer), "NUMBER: 123456"); 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(result, 15);
EXPECT_EQ(std::string(buffer), "NUMBER: 1234567"); 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(result, 16);
EXPECT_EQ(std::string(buffer), "NUMBER: 1234567"); 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(result, 17);
EXPECT_EQ(std::string(buffer), "NUMBER: 1234567"); EXPECT_EQ(std::string(buffer), "NUMBER: 1234567");
...@@ -451,7 +540,7 @@ TEST(StrFormat, BehavesAsDocumented) { ...@@ -451,7 +540,7 @@ TEST(StrFormat, BehavesAsDocumented) {
std::string s = absl::StrFormat("%s, %d!", "Hello", 123); std::string s = absl::StrFormat("%s, %d!", "Hello", 123);
EXPECT_EQ("Hello, 123!", s); EXPECT_EQ("Hello, 123!", s);
std::string hello = "Hello"; 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); EXPECT_EQ("Hello, 123!", s2);
// The format of a replacement is // The format of a replacement is
// '%'[position][flags][width['.'precision]][length_modifier][format] // '%'[position][flags][width['.'precision]][length_modifier][format]
...@@ -480,13 +569,18 @@ TEST(StrFormat, BehavesAsDocumented) { ...@@ -480,13 +569,18 @@ TEST(StrFormat, BehavesAsDocumented) {
EXPECT_EQ(StrFormat("%d", int{10}), "10"); EXPECT_EQ(StrFormat("%d", int{10}), "10");
EXPECT_EQ(StrFormat("%d", long{10}), "10"); // NOLINT EXPECT_EQ(StrFormat("%d", long{10}), "10"); // NOLINT
EXPECT_EQ(StrFormat("%d", uint64_t{10}), "10"); 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" // d,i - signed decimal Eg: -10 -> "-10"
EXPECT_EQ(StrFormat("%d", -10), "-10"); EXPECT_EQ(StrFormat("%d", -10), "-10");
EXPECT_EQ(StrFormat("%i", -10), "-10"); EXPECT_EQ(StrFormat("%i", -10), "-10");
EXPECT_EQ(StrFormat("%v", -10), "-10");
// o - octal Eg: 10 -> "12" // o - octal Eg: 10 -> "12"
EXPECT_EQ(StrFormat("%o", 10), "12"); EXPECT_EQ(StrFormat("%o", 10), "12");
// u - unsigned decimal Eg: 10 -> "10" // u - unsigned decimal Eg: 10 -> "10"
EXPECT_EQ(StrFormat("%u", 10), "10"); EXPECT_EQ(StrFormat("%u", 10), "10");
EXPECT_EQ(StrFormat("%v", 10), "10");
// x/X - lower,upper case hex Eg: 10 -> "a"/"A" // x/X - lower,upper case hex Eg: 10 -> "a"/"A"
EXPECT_EQ(StrFormat("%x", 10), "a"); EXPECT_EQ(StrFormat("%x", 10), "a");
EXPECT_EQ(StrFormat("%X", 10), "A"); EXPECT_EQ(StrFormat("%X", 10), "A");
...@@ -511,6 +605,8 @@ TEST(StrFormat, BehavesAsDocumented) { ...@@ -511,6 +605,8 @@ TEST(StrFormat, BehavesAsDocumented) {
EXPECT_EQ(StrFormat("%g", .01), "0.01"); EXPECT_EQ(StrFormat("%g", .01), "0.01");
EXPECT_EQ(StrFormat("%g", 1e10), "1e+10"); EXPECT_EQ(StrFormat("%g", 1e10), "1e+10");
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" // 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. // On Android platform <=21, there is a regression in hexfloat formatting.
...@@ -598,10 +694,10 @@ TEST_F(ParsedFormatTest, SimpleChecked) { ...@@ -598,10 +694,10 @@ TEST_F(ParsedFormatTest, SimpleChecked) {
} }
TEST_F(ParsedFormatTest, SimpleCheckedWithV) { TEST_F(ParsedFormatTest, SimpleCheckedWithV) {
EXPECT_EQ("[ABC]{d:1$d}[DEF]", EXPECT_EQ("[ABC]{v:1$v}[DEF]",
SummarizeParsedFormat(ParsedFormat<'d'>("ABC%dDEF"))); SummarizeParsedFormat(ParsedFormat<'v'>("ABC%vDEF")));
EXPECT_EQ("{v:1$v}[FFF]{d:2$d}[ZZZ]{f:3$f}", EXPECT_EQ("{v:1$v}[FFF]{v:2$v}[ZZZ]{f:3$f}",
SummarizeParsedFormat(ParsedFormat<'v', 'd', 'f'>("%vFFF%dZZZ%f"))); SummarizeParsedFormat(ParsedFormat<'v', 'v', 'f'>("%vFFF%vZZZ%f")));
EXPECT_EQ("{v:1$v}[ ]{.*d:3$.2$*d}", EXPECT_EQ("{v:1$v}[ ]{.*d:3$.2$*d}",
SummarizeParsedFormat(ParsedFormat<'v', '*', 'd'>("%v %.*d"))); SummarizeParsedFormat(ParsedFormat<'v', '*', 'd'>("%v %.*d")));
} }
...@@ -637,20 +733,20 @@ TEST_F(ParsedFormatTest, SimpleUncheckedCorrect) { ...@@ -637,20 +733,20 @@ TEST_F(ParsedFormatTest, SimpleUncheckedCorrect) {
} }
TEST_F(ParsedFormatTest, SimpleUncheckedCorrectWithV) { TEST_F(ParsedFormatTest, SimpleUncheckedCorrectWithV) {
auto f = ParsedFormat<'d'>::New("ABC%dDEF"); auto f = ParsedFormat<'v'>::New("ABC%vDEF");
ASSERT_TRUE(f); 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 = ParsedFormat<'v', 'd', 'f'>::New(format); auto f2 = ParsedFormat<'v', 'v', 'f'>::New(format);
ASSERT_TRUE(f2); 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); 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) { TEST_F(ParsedFormatTest, SimpleUncheckedIgnoredArgs) {
...@@ -668,6 +764,18 @@ TEST_F(ParsedFormatTest, SimpleUncheckedIgnoredArgs) { ...@@ -668,6 +764,18 @@ TEST_F(ParsedFormatTest, SimpleUncheckedIgnoredArgs) {
EXPECT_EQ("[ABC]{2$s:2$s}", SummarizeParsedFormat(*f)); 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) { TEST_F(ParsedFormatTest, SimpleUncheckedUnsupported) {
EXPECT_FALSE(ParsedFormat<'d'>::New("%1$d %1$x")); EXPECT_FALSE(ParsedFormat<'d'>::New("%1$d %1$x"));
EXPECT_FALSE(ParsedFormat<'x'>::New("%1$d %1$x")); EXPECT_FALSE(ParsedFormat<'x'>::New("%1$d %1$x"));
...@@ -683,12 +791,12 @@ TEST_F(ParsedFormatTest, SimpleUncheckedIncorrect) { ...@@ -683,12 +791,12 @@ TEST_F(ParsedFormatTest, SimpleUncheckedIncorrect) {
} }
TEST_F(ParsedFormatTest, SimpleUncheckedIncorrectWithV) { 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"; std::string format = "%vFFF%vZZZ%f";
EXPECT_FALSE((ParsedFormat<'v', 'd', 'g'>::New(format))); EXPECT_FALSE((ParsedFormat<'v', 'v', 'g'>::New(format)));
} }
#if defined(__cpp_nontype_template_parameter_auto) #if defined(__cpp_nontype_template_parameter_auto)
...@@ -739,17 +847,17 @@ TEST_F(ParsedFormatTest, ExtendedTyping) { ...@@ -739,17 +847,17 @@ TEST_F(ParsedFormatTest, ExtendedTyping) {
} }
TEST_F(ParsedFormatTest, ExtendedTypingWithV) { TEST_F(ParsedFormatTest, ExtendedTypingWithV) {
EXPECT_FALSE(ParsedFormat<FormatConversionCharSet::d>::New("")); EXPECT_FALSE(ParsedFormat<FormatConversionCharSet::v>::New(""));
ASSERT_TRUE(ParsedFormat<absl::FormatConversionCharSet::d>::New("%d")); ASSERT_TRUE(ParsedFormat<absl::FormatConversionCharSet::v>::New("%v"));
auto v1 = ParsedFormat<'d', absl::FormatConversionCharSet::v>::New("%d%v"); auto v1 = ParsedFormat<'v', absl::FormatConversionCharSet::v>::New("%v%v");
ASSERT_TRUE(v1); 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); ASSERT_TRUE(v2);
auto v3 = ParsedFormat<absl::FormatConversionCharSet::d | auto v3 = ParsedFormat<absl::FormatConversionCharSet::v |
absl::FormatConversionCharSet::v, absl::FormatConversionCharSet::v,
'v'>::New("%d%v"); 'v'>::New("%v%v");
ASSERT_TRUE(v3); ASSERT_TRUE(v3);
auto v4 = ParsedFormat<absl::FormatConversionCharSet::d | auto v4 = ParsedFormat<absl::FormatConversionCharSet::v |
absl::FormatConversionCharSet::v, absl::FormatConversionCharSet::v,
'v'>::New("%v%v"); 'v'>::New("%v%v");
ASSERT_TRUE(v4); ASSERT_TRUE(v4);
...@@ -799,24 +907,24 @@ TEST_F(ParsedFormatTest, UncheckedCorrect) { ...@@ -799,24 +907,24 @@ TEST_F(ParsedFormatTest, UncheckedCorrect) {
TEST_F(ParsedFormatTest, UncheckedCorrectWithV) { TEST_F(ParsedFormatTest, UncheckedCorrectWithV) {
auto f = auto f =
ExtendedParsedFormat<absl::FormatConversionCharSet::d>::New("ABC%dDEF"); ExtendedParsedFormat<absl::FormatConversionCharSet::v>::New("ABC%vDEF");
ASSERT_TRUE(f); 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< auto f2 = ExtendedParsedFormat<
absl::FormatConversionCharSet::v, absl::FormatConversionCharSet::d, absl::FormatConversionCharSet::v, absl::FormatConversionCharSet::v,
absl::FormatConversionCharSet::kFloating>::New(format); absl::FormatConversionCharSet::kFloating>::New(format);
ASSERT_TRUE(f2); 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< f2 = ExtendedParsedFormat<
absl::FormatConversionCharSet::v, absl::FormatConversionCharSet::d, absl::FormatConversionCharSet::v, absl::FormatConversionCharSet::v,
absl::FormatConversionCharSet::kFloating>::New("%v %d %f"); absl::FormatConversionCharSet::kFloating>::New("%v %v %f");
ASSERT_TRUE(f2); 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) { TEST_F(ParsedFormatTest, UncheckedIgnoredArgs) {
...@@ -846,6 +954,28 @@ TEST_F(ParsedFormatTest, UncheckedIgnoredArgs) { ...@@ -846,6 +954,28 @@ TEST_F(ParsedFormatTest, UncheckedIgnoredArgs) {
EXPECT_EQ("[ABC]{2$s:2$s}", SummarizeParsedFormat(*f)); 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) { TEST_F(ParsedFormatTest, UncheckedMultipleTypes) {
auto dx = auto dx =
ExtendedParsedFormat<absl::FormatConversionCharSet::d | ExtendedParsedFormat<absl::FormatConversionCharSet::d |
...@@ -873,15 +1003,14 @@ TEST_F(ParsedFormatTest, UncheckedIncorrect) { ...@@ -873,15 +1003,14 @@ TEST_F(ParsedFormatTest, UncheckedIncorrect) {
} }
TEST_F(ParsedFormatTest, UncheckedIncorrectWithV) { 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( EXPECT_FALSE(ExtendedParsedFormat<absl::FormatConversionCharSet::v>::New(
"ABC%dDEF%d")); "ABC%vDEF%v"));
std::string format = "%vFFF%dZZZ%f"; std::string format = "%vFFF%vZZZ%f";
EXPECT_FALSE( EXPECT_FALSE(
(ExtendedParsedFormat<absl::FormatConversionCharSet::v, (ExtendedParsedFormat<absl::FormatConversionCharSet::v,
absl::FormatConversionCharSet::d,
absl::FormatConversionCharSet::g>::New(format))); absl::FormatConversionCharSet::g>::New(format)));
} }
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment