use std::underlying_type to support enum classes
authorSoren Lassen <soren@fb.com>
Sun, 18 Nov 2012 22:43:07 +0000 (14:43 -0800)
committerJordan DeLong <jdelong@fb.com>
Sun, 16 Dec 2012 22:45:37 +0000 (14:45 -0800)
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
folly/test/ConvTest.cpp

index 98ff2c01ea9733d4fd39509877028cc696519a44..de369c0a29c3cb0d10b5d980a0650ee6729095ec 100644 (file)
@@ -280,6 +280,22 @@ toAppend(Src value, Tgt * result) {
   toAppend<Tgt>(static_cast<Intermediate>(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 <class Tgt, class Src>
+typename std::enable_if<
+  std::is_enum<Src>::value && detail::IsSomeString<Tgt>::value>::type
+toAppend(Src value, Tgt * result) {
+  toAppend(
+      static_cast<typename std::underlying_type<Src>::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 <class Tgt, class Src>
+typename std::enable_if<std::is_enum<Src>::value, Tgt>::type
+to(const Src & value) {
+  return to<Tgt>(static_cast<typename std::underlying_type<Src>::type>(value));
+}
+
+template <class Tgt, class Src>
+typename std::enable_if<std::is_enum<Tgt>::value, Tgt>::type
+to(const Src & value) {
+  return static_cast<Tgt>(to<typename std::underlying_type<Tgt>::type>(value));
+}
+
+#else
+
 template <class Tgt, class Src>
 typename std::enable_if<std::is_enum<Src>::value, Tgt>::type
 to(const Src & value) {
-  // TODO: uncomment this when underlying_type is available
-  // return to<Tgt>(static_cast<typename std::underlying_type<Src>::type>(
-  //    value));
   /* static */ if (Src(-1) < 0) {
     /* static */ if (sizeof(Src) <= sizeof(int)) {
       return to<Tgt>(static_cast<int>(value));
@@ -936,9 +968,6 @@ to(const Src & value) {
 template <class Tgt, class Src>
 typename std::enable_if<std::is_enum<Tgt>::value, Tgt>::type
 to(const Src & value) {
-  // TODO: uncomment this when underlying_type is available
-  // return static_cast<Tgt>(
-  //    to<typename std::underlying_type<Tgt>::type>(value));
   /* static */ if (Tgt(-1) < 0) {
     /* static */ if (sizeof(Tgt) <= sizeof(int)) {
       return static_cast<Tgt>(to<int>(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
index fed93e5a2415aab8b2645cdcdf4cc36be1baf39c..1cde6198f4b5c0b8acae7bc9036bc99a90f5dac8 100644 (file)
@@ -495,7 +495,7 @@ TEST(Conv, EnumToString) {
 TEST(Conv, IntToEnum) {
   enum A { x = 42, y = 420 };
   auto i = to<A>(42);
-  EXPECT_EQ(i, A::x);
+  EXPECT_EQ(i, x);
   auto j = to<A>(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<uint32_t>(x);
+  EXPECT_EQ(u, 3000000000U);
+  auto s = to<string>(x);
+  EXPECT_EQ("3000000000", s);
+  auto e = to<E>(3000000000U);
+  EXPECT_EQ(e, x);
+  try {
+    auto i = to<int32_t>(x);
+    LOG(ERROR) << to<uint32_t>(x);
+    EXPECT_TRUE(false);
+  } catch (std::range_error& e) {
+  }
+}
+
+#if defined(__GNUC__) && __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 7)
+// to<enum class> and to(enum class) only supported in gcc 4.7 onwards
+
+TEST(Conv, UnsignedEnumClass) {
+  enum class E : uint32_t { x = 3000000000U };
+  auto u = to<uint32_t>(E::x);
+  EXPECT_GT(u, 0);
+  EXPECT_EQ(u, 3000000000U);
+  auto s = to<string>(E::x);
+  EXPECT_EQ("3000000000", s);
+  auto e = to<E>(3000000000U);
+  EXPECT_EQ(e, E::x);
+  try {
+    auto i = to<int32_t>(E::x);
+    LOG(ERROR) << to<uint32_t>(E::x);
+    EXPECT_TRUE(false);
+  } catch (std::range_error& e) {
+  }
+}
+
+// Multi-argument to<string> uses toAppend, a different code path than
+// to<string>(enum).
+TEST(Conv, EnumClassToString) {
+  enum class A { x = 4, y = 420, z = 65 };
+  EXPECT_EQ("foo.4", to<string>("foo.", A::x));
+  EXPECT_EQ("foo.420", to<string>("foo.", A::y));
+  EXPECT_EQ("foo.65", to<string>("foo.", A::z));
+}
+
+#endif // gcc 4.7 onwards
+
 template<typename Src>
 void testStr2Bool() {
   EXPECT_FALSE(to<bool>(Src("0")));