Skip to content

Commit

Permalink
use std::underlying_type to support enum classes
Browse files Browse the repository at this point in the history
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
  • Loading branch information
Soren Lassen authored and jdelong committed Dec 16, 2012
1 parent c462b06 commit 4f0bc52
Show file tree
Hide file tree
Showing 2 changed files with 85 additions and 7 deletions.
43 changes: 37 additions & 6 deletions folly/Conv.h
Expand Up @@ -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.
*/
Expand All @@ -302,6 +318,8 @@ toAppend(Src value, Tgt * result) {
}
}

#endif // gcc 4.7 onwards

/*******************************************************************************
* Conversions from floating-point types to string types.
******************************************************************************/
Expand Down Expand Up @@ -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));
Expand All @@ -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));
Expand All @@ -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
Expand Down
49 changes: 48 additions & 1 deletion folly/test/ConvTest.cpp
Expand Up @@ -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 {
Expand All @@ -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")));
Expand Down

0 comments on commit 4f0bc52

Please sign in to comment.