summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--absl/strings/char_formatting_test.cc54
-rw-r--r--absl/strings/internal/str_format/arg.cc16
-rw-r--r--absl/strings/internal/str_format/arg.h16
-rw-r--r--absl/strings/str_cat.h18
-rw-r--r--absl/strings/str_format.h1
5 files changed, 48 insertions, 57 deletions
diff --git a/absl/strings/char_formatting_test.cc b/absl/strings/char_formatting_test.cc
index 60416af3..1692da70 100644
--- a/absl/strings/char_formatting_test.cc
+++ b/absl/strings/char_formatting_test.cc
@@ -37,17 +37,12 @@ TEST(CharFormatting, CharEnum) {
auto v = static_cast<CharEnum>('A');
// Desired behavior: format as decimal
- // (No APIs do this today.)
-
- // BUG: internally fails
- EXPECT_EQ(absl::StrFormat("%vB", v), "");
-
- // Legacy behavior: does not compile:
- // EXPECT_EQ(absl::StrCat(ch, "B"), "AB");
+ EXPECT_EQ(absl::StrFormat("%vB", v), "65B");
+ EXPECT_EQ(absl::StrCat(v, "B"), "65B");
// Legacy behavior: format as character:
- // Some older versions of gcc behave differently in this one case.
+ // Some older versions of gcc behave differently in this one case
#if !defined(__GNUC__) || defined(__clang__)
EXPECT_EQ(absl::Substitute("$0B", v), "AB");
#endif
@@ -57,14 +52,9 @@ enum class CharEnumClass: char {};
TEST(CharFormatting, CharEnumClass) {
auto v = static_cast<CharEnumClass>('A');
- // Desired behavior: format as decimal
- // (No APIs do this today.)
-
- // BUG: internally fails
- EXPECT_EQ(absl::StrFormat("%vB", v), "");
-
- // Legacy behavior: does not compile:
- // EXPECT_EQ(absl::StrCat(ch, "B"), "AB");
+ // Desired behavior: format as decimal:
+ EXPECT_EQ(absl::StrFormat("%vB", v), "65B");
+ EXPECT_EQ(absl::StrCat(v, "B"), "65B");
// Legacy behavior: format as character:
EXPECT_EQ(absl::Substitute("$0B", v), "AB");
@@ -76,9 +66,7 @@ TEST(CharFormatting, UnsignedChar) {
// Desired behavior: format as decimal:
EXPECT_EQ(absl::StrCat(v, "B"), "65B");
EXPECT_EQ(absl::Substitute("$0B", v), "65B");
-
- // Legacy behavior: does not compile:
- // EXPECT_EQ(absl::StrFormat("%vB", v), "65B");
+ EXPECT_EQ(absl::StrFormat("%vB", v), "65B");
// Signedness check
const unsigned char w = 255;
@@ -93,9 +81,7 @@ TEST(CharFormatting, SignedChar) {
// Desired behavior: format as decimal:
EXPECT_EQ(absl::StrCat(v, "B"), "65B");
EXPECT_EQ(absl::Substitute("$0B", v), "65B");
-
- // Legacy behavior: does not compile:
- // EXPECT_EQ(absl::StrFormat("%vB", v), "AB");
+ EXPECT_EQ(absl::StrFormat("%vB", v), "65B");
// Signedness check
const signed char w = -128;
@@ -110,14 +96,13 @@ TEST(CharFormatting, UnsignedCharEnum) {
// Desired behavior: format as decimal:
EXPECT_EQ(absl::StrCat(v, "B"), "65B");
EXPECT_EQ(absl::Substitute("$0B", v), "65B");
-
- // BUG: internally fails
- EXPECT_EQ(absl::StrFormat("%vB", v), "");
+ EXPECT_EQ(absl::StrFormat("%vB", v), "65B");
// Signedness check
auto w = static_cast<UnsignedCharEnum>(255);
EXPECT_EQ(absl::StrCat(w, "B"), "255B");
EXPECT_EQ(absl::Substitute("$0B", w), "255B");
+ EXPECT_EQ(absl::StrFormat("%vB", w), "255B");
}
enum SignedCharEnum : signed char {};
@@ -127,14 +112,13 @@ TEST(CharFormatting, SignedCharEnum) {
// Desired behavior: format as decimal:
EXPECT_EQ(absl::StrCat(v, "B"), "65B");
EXPECT_EQ(absl::Substitute("$0B", v), "65B");
-
- // BUG: internally fails
- EXPECT_EQ(absl::StrFormat("%vB", v), "");
+ EXPECT_EQ(absl::StrFormat("%vB", v), "65B");
// Signedness check
auto w = static_cast<SignedCharEnum>(-128);
EXPECT_EQ(absl::StrCat(w, "B"), "-128B");
EXPECT_EQ(absl::Substitute("$0B", w), "-128B");
+ EXPECT_EQ(absl::StrFormat("%vB", w), "-128B");
}
enum class UnsignedCharEnumClass : unsigned char {};
@@ -144,14 +128,13 @@ TEST(CharFormatting, UnsignedCharEnumClass) {
// Desired behavior: format as decimal:
EXPECT_EQ(absl::StrCat(v, "B"), "65B");
EXPECT_EQ(absl::Substitute("$0B", v), "65B");
-
- // BUG: internally fails
- EXPECT_EQ(absl::StrFormat("%vB", v), "");
+ EXPECT_EQ(absl::StrFormat("%vB", v), "65B");
// Signedness check
auto w = static_cast<UnsignedCharEnumClass>(255);
EXPECT_EQ(absl::StrCat(w, "B"), "255B");
EXPECT_EQ(absl::Substitute("$0B", w), "255B");
+ EXPECT_EQ(absl::StrFormat("%vB", w), "255B");
}
enum SignedCharEnumClass : signed char {};
@@ -161,14 +144,13 @@ TEST(CharFormatting, SignedCharEnumClass) {
// Desired behavior: format as decimal:
EXPECT_EQ(absl::StrCat(v, "B"), "65B");
EXPECT_EQ(absl::Substitute("$0B", v), "65B");
-
- // BUG: internally fails
- EXPECT_EQ(absl::StrFormat("%vB", v), "");
+ EXPECT_EQ(absl::StrFormat("%vB", v), "65B");
// Signedness check
auto w = static_cast<SignedCharEnumClass>(-128);
EXPECT_EQ(absl::StrCat(w, "B"), "-128B");
EXPECT_EQ(absl::Substitute("$0B", w), "-128B");
+ EXPECT_EQ(absl::StrFormat("%vB", w), "-128B");
}
#ifdef __cpp_lib_byte
@@ -177,12 +159,10 @@ TEST(CharFormatting, StdByte) {
// Desired behavior: format as 0xff
// (No APIs do this today.)
- // BUG: internally fails
- EXPECT_EQ(absl::StrFormat("%vB", v), "");
-
// Legacy behavior: format as decimal:
EXPECT_EQ(absl::StrCat(v, "B"), "65B");
EXPECT_EQ(absl::Substitute("$0B", v), "65B");
+ EXPECT_EQ(absl::StrFormat("%vB", v), "65B");
}
#endif // _cpp_lib_byte
diff --git a/absl/strings/internal/str_format/arg.cc b/absl/strings/internal/str_format/arg.cc
index 6033a75a..7f4057f9 100644
--- a/absl/strings/internal/str_format/arg.cc
+++ b/absl/strings/internal/str_format/arg.cc
@@ -461,18 +461,18 @@ CharConvertResult FormatConvertImpl(char v, const FormatConversionSpecImpl conv,
FormatSinkImpl *sink) {
return {ConvertIntArg(v, conv, sink)};
}
-CharConvertResult FormatConvertImpl(signed char v,
- const FormatConversionSpecImpl conv,
- FormatSinkImpl *sink) {
+
+// ==================== Ints ====================
+IntegralConvertResult FormatConvertImpl(signed char v,
+ const FormatConversionSpecImpl conv,
+ FormatSinkImpl *sink) {
return {ConvertIntArg(v, conv, sink)};
}
-CharConvertResult FormatConvertImpl(unsigned char v,
- const FormatConversionSpecImpl conv,
- FormatSinkImpl *sink) {
+IntegralConvertResult FormatConvertImpl(unsigned char v,
+ const FormatConversionSpecImpl conv,
+ FormatSinkImpl *sink) {
return {ConvertIntArg(v, conv, sink)};
}
-
-// ==================== Ints ====================
IntegralConvertResult FormatConvertImpl(short v, // NOLINT
const FormatConversionSpecImpl conv,
FormatSinkImpl *sink) {
diff --git a/absl/strings/internal/str_format/arg.h b/absl/strings/internal/str_format/arg.h
index e4b16628..3ce30feb 100644
--- a/absl/strings/internal/str_format/arg.h
+++ b/absl/strings/internal/str_format/arg.h
@@ -279,14 +279,14 @@ FloatingConvertResult FormatConvertImpl(long double v,
// Chars.
CharConvertResult FormatConvertImpl(char v, FormatConversionSpecImpl conv,
FormatSinkImpl* sink);
-CharConvertResult FormatConvertImpl(signed char v,
- FormatConversionSpecImpl conv,
- FormatSinkImpl* sink);
-CharConvertResult FormatConvertImpl(unsigned char v,
- FormatConversionSpecImpl conv,
- FormatSinkImpl* sink);
// Ints.
+IntegralConvertResult FormatConvertImpl(signed char v,
+ FormatConversionSpecImpl conv,
+ FormatSinkImpl* sink);
+IntegralConvertResult FormatConvertImpl(unsigned char v,
+ FormatConversionSpecImpl conv,
+ FormatSinkImpl* sink);
IntegralConvertResult FormatConvertImpl(short v, // NOLINT
FormatConversionSpecImpl conv,
FormatSinkImpl* sink);
@@ -441,7 +441,7 @@ class FormatArgImpl {
// For everything else:
// - Decay char* and char arrays into `const char*`
// - Decay any other pointer to `const void*`
- // - Decay all enums to their underlying type.
+ // - Decay all enums to the integral promotion of their underlying type.
// - Decay function pointers to void*.
template <typename T, typename = void>
struct DecayType {
@@ -461,7 +461,7 @@ class FormatArgImpl {
!str_format_internal::HasUserDefinedConvert<T>::value &&
!strings_internal::HasAbslStringify<T>::value &&
std::is_enum<T>::value>::type> {
- using type = typename std::underlying_type<T>::type;
+ using type = decltype(+typename std::underlying_type<T>::type());
};
public:
diff --git a/absl/strings/str_cat.h b/absl/strings/str_cat.h
index fcd48c4e..d5f71ff0 100644
--- a/absl/strings/str_cat.h
+++ b/absl/strings/str_cat.h
@@ -374,14 +374,24 @@ class AlphaNum {
const char* data() const { return piece_.data(); }
absl::string_view Piece() const { return piece_; }
- // Normal enums are already handled by the integer formatters.
- // This overload matches only scoped enums.
+ // Match unscoped enums. Use integral promotion so that a `char`-backed
+ // enum becomes a wider integral type AlphaNum will accept.
template <typename T,
typename = typename std::enable_if<
- std::is_enum<T>{} && !std::is_convertible<T, int>{} &&
+ std::is_enum<T>{} && std::is_convertible<T, int>{} &&
!strings_internal::HasAbslStringify<T>::value>::type>
AlphaNum(T e) // NOLINT(runtime/explicit)
- : AlphaNum(static_cast<typename std::underlying_type<T>::type>(e)) {}
+ : AlphaNum(+e) {}
+
+ // This overload matches scoped enums. We must explicitly cast to the
+ // underlying type, but use integral promotion for the same reason as above.
+ template <typename T,
+ typename std::enable_if<
+ std::is_enum<T>{} && !std::is_convertible<T, int>{} &&
+ !strings_internal::HasAbslStringify<T>::value,
+ char*>::type = nullptr>
+ AlphaNum(T e) // NOLINT(runtime/explicit)
+ : AlphaNum(+static_cast<typename std::underlying_type<T>::type>(e)) {}
// vector<bool>::reference and const_reference require special help to
// convert to `AlphaNum` because it requires two user defined conversions.
diff --git a/absl/strings/str_format.h b/absl/strings/str_format.h
index fc4bf39e..023e4350 100644
--- a/absl/strings/str_format.h
+++ b/absl/strings/str_format.h
@@ -259,6 +259,7 @@ class FormatCountCapture {
// * Characters: `char`, `signed char`, `unsigned char`
// * Integers: `int`, `short`, `unsigned short`, `unsigned`, `long`,
// `unsigned long`, `long long`, `unsigned long long`
+// * Enums: printed as their underlying integral value
// * Floating-point: `float`, `double`, `long double`
//
// However, in the `str_format` library, a format conversion specifies a broader