From 4f0bc5272f90bfcb7a18e6a1a4359a202a1b8cf9 Mon Sep 17 00:00:00 2001 From: Soren Lassen Date: Sun, 18 Nov 2012 14:43:07 -0800 Subject: [PATCH] use std::underlying_type to support enum classes Summary: Noticed these TODOs in Conv.h and decided to fix them. Discovered that the underlying_type implementation also covers enum classes, which didn't work with the pre-existing code. Test Plan: fbconfig -r folly --platform=gcc-4.7.1-glibc-2.14.1-fb fbmake dbg _bin/folly/test/conv_test _bin/folly/test/conv_test --platform=gcc-4.6.2-glibc-2.13 fbmake dbg _bin/folly/test/conv_test _bin/folly/test/conv_test Reviewed By: tudorb@fb.com FB internal diff: D634309 --- folly/Conv.h | 43 +++++++++++++++++++++++++++++++----- folly/test/ConvTest.cpp | 49 ++++++++++++++++++++++++++++++++++++++++- 2 files changed, 85 insertions(+), 7 deletions(-) diff --git a/folly/Conv.h b/folly/Conv.h index 98ff2c01..de369c0a 100644 --- a/folly/Conv.h +++ b/folly/Conv.h @@ -280,6 +280,22 @@ toAppend(Src value, Tgt * result) { toAppend(static_cast(value), result); } +#if defined(__GNUC__) && __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 7) +// std::underlying_type became available by gcc 4.7.0 + +/** + * Enumerated values get appended as integers. + */ +template +typename std::enable_if< + std::is_enum::value && detail::IsSomeString::value>::type +toAppend(Src value, Tgt * result) { + toAppend( + static_cast::type>(value), result); +} + +#else + /** * Enumerated values get appended as integers. */ @@ -302,6 +318,8 @@ toAppend(Src value, Tgt * result) { } } +#endif // gcc 4.7 onwards + /******************************************************************************* * Conversions from floating-point types to string types. ******************************************************************************/ @@ -912,12 +930,26 @@ to(const Src & value) { * Enum to anything and back ******************************************************************************/ +#if defined(__GNUC__) && __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 7) +// std::underlying_type became available by gcc 4.7.0 + +template +typename std::enable_if::value, Tgt>::type +to(const Src & value) { + return to(static_cast::type>(value)); +} + +template +typename std::enable_if::value, Tgt>::type +to(const Src & value) { + return static_cast(to::type>(value)); +} + +#else + template typename std::enable_if::value, Tgt>::type to(const Src & value) { - // TODO: uncomment this when underlying_type is available - // return to(static_cast::type>( - // value)); /* static */ if (Src(-1) < 0) { /* static */ if (sizeof(Src) <= sizeof(int)) { return to(static_cast(value)); @@ -936,9 +968,6 @@ to(const Src & value) { template typename std::enable_if::value, Tgt>::type to(const Src & value) { - // TODO: uncomment this when underlying_type is available - // return static_cast( - // to::type>(value)); /* static */ if (Tgt(-1) < 0) { /* static */ if (sizeof(Tgt) <= sizeof(int)) { return static_cast(to(value)); @@ -954,6 +983,8 @@ to(const Src & value) { } } +#endif // gcc 4.7 onwards + } // namespace folly // FOLLY_CONV_INTERNAL is defined by Conv.cpp. Keep the FOLLY_RANGE_CHECK diff --git a/folly/test/ConvTest.cpp b/folly/test/ConvTest.cpp index fed93e5a..1cde6198 100644 --- a/folly/test/ConvTest.cpp +++ b/folly/test/ConvTest.cpp @@ -495,7 +495,7 @@ TEST(Conv, EnumToString) { TEST(Conv, IntToEnum) { enum A { x = 42, y = 420 }; auto i = to(42); - EXPECT_EQ(i, A::x); + EXPECT_EQ(i, x); auto j = to(100); EXPECT_EQ(j, 100); try { @@ -506,6 +506,53 @@ TEST(Conv, IntToEnum) { } } +TEST(Conv, UnsignedEnum) { + enum E : uint32_t { x = 3000000000U }; + auto u = to(x); + EXPECT_EQ(u, 3000000000U); + auto s = to(x); + EXPECT_EQ("3000000000", s); + auto e = to(3000000000U); + EXPECT_EQ(e, x); + try { + auto i = to(x); + LOG(ERROR) << to(x); + EXPECT_TRUE(false); + } catch (std::range_error& e) { + } +} + +#if defined(__GNUC__) && __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 7) +// to and to(enum class) only supported in gcc 4.7 onwards + +TEST(Conv, UnsignedEnumClass) { + enum class E : uint32_t { x = 3000000000U }; + auto u = to(E::x); + EXPECT_GT(u, 0); + EXPECT_EQ(u, 3000000000U); + auto s = to(E::x); + EXPECT_EQ("3000000000", s); + auto e = to(3000000000U); + EXPECT_EQ(e, E::x); + try { + auto i = to(E::x); + LOG(ERROR) << to(E::x); + EXPECT_TRUE(false); + } catch (std::range_error& e) { + } +} + +// Multi-argument to uses toAppend, a different code path than +// to(enum). +TEST(Conv, EnumClassToString) { + enum class A { x = 4, y = 420, z = 65 }; + EXPECT_EQ("foo.4", to("foo.", A::x)); + EXPECT_EQ("foo.420", to("foo.", A::y)); + EXPECT_EQ("foo.65", to("foo.", A::z)); +} + +#endif // gcc 4.7 onwards + template void testStr2Bool() { EXPECT_FALSE(to(Src("0"))); -- 2.34.1