X-Git-Url: http://plrg.eecs.uci.edu/git/?a=blobdiff_plain;f=folly%2FConv.h;h=c90b4c03144413c3fd90d2faa6191d506c57b5d3;hb=4c68315cba37cc5704a4627165570cac96c06664;hp=98ff2c01ea9733d4fd39509877028cc696519a44;hpb=43bdc5d7d0bee944c1f437f3e159f32d4c57f3cd;p=folly.git diff --git a/folly/Conv.h b/folly/Conv.h index 98ff2c01..c90b4c03 100644 --- a/folly/Conv.h +++ b/folly/Conv.h @@ -1,5 +1,5 @@ /* - * Copyright 2012 Facebook, Inc. + * Copyright 2014 Facebook, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -24,12 +24,13 @@ #ifndef FOLLY_BASE_CONV_H_ #define FOLLY_BASE_CONV_H_ -#include "folly/FBString.h" -#include "folly/Likely.h" -#include "folly/Preprocessor.h" -#include "folly/Range.h" +#include +#include +#include +#include #include +#include #include #include #include @@ -37,11 +38,17 @@ #include #include -#include "double-conversion.h" // V8 JavaScript implementation +#include -#define FOLLY_RANGE_CHECK(condition, message) \ - ((condition) ? (void)0 : throw std::range_error( \ - (__FILE__ "(" + std::to_string((long long int) __LINE__) + "): " \ +// V8 JavaScript implementation +#include + +#define FOLLY_RANGE_CHECK_STRINGIZE(x) #x +#define FOLLY_RANGE_CHECK_STRINGIZE2(x) FOLLY_RANGE_CHECK_STRINGIZE(x) + +#define FOLLY_RANGE_CHECK(condition, message) \ + ((condition) ? (void)0 : throw std::range_error( \ + (std::string(__FILE__ "(" FOLLY_RANGE_CHECK_STRINGIZE2(__LINE__) "): ") \ + (message)).c_str())) namespace folly { @@ -62,13 +69,17 @@ typename std::enable_if< to(const Src & value) { /* static */ if (std::numeric_limits::max() < std::numeric_limits::max()) { - FOLLY_RANGE_CHECK(value <= std::numeric_limits::max(), - "Overflow"); + FOLLY_RANGE_CHECK( + (!greater_than::max()>(value)), + "Overflow" + ); } /* static */ if (std::is_signed::value && (!std::is_signed::value || sizeof(Src) > sizeof(Tgt))) { - FOLLY_RANGE_CHECK(value >= std::numeric_limits::min(), - "Negative overflow"); + FOLLY_RANGE_CHECK( + (!less_than::min()>(value)), + "Negative overflow" + ); } return static_cast(value); } @@ -98,11 +109,6 @@ to(const Src & value) { namespace detail { -template struct IsSomeString { - enum { value = std::is_same::value - || std::is_same::value }; -}; - template const T& getLastElement(const T & v) { return v; @@ -116,12 +122,69 @@ typename std::tuple_element< return getLastElement(vs...); } +// This class exists to specialize away std::tuple_element in the case where we +// have 0 template arguments. Without this, Clang/libc++ will blow a +// static_assert even if tuple_element is protected by an enable_if. +template +struct last_element { + typedef typename std::enable_if< + sizeof...(Ts) >= 1, + typename std::tuple_element< + sizeof...(Ts) - 1, std::tuple + >::type>::type type; +}; + +template <> +struct last_element<> { + typedef void type; +}; + } // namespace detail /******************************************************************************* * Conversions from integral types to string types. ******************************************************************************/ +#if FOLLY_HAVE_INT128_T +namespace detail { + +template +constexpr unsigned int +digitsEnough() { + return ceil((double(sizeof(IntegerType) * CHAR_BIT) * M_LN2) / M_LN10); +} + +inline size_t +unsafeTelescope128(char * buffer, size_t room, unsigned __int128 x) { + typedef unsigned __int128 Usrc; + size_t p = room - 1; + + while (x >= (Usrc(1) << 64)) { // Using 128-bit division while needed + const auto y = x / 10; + const auto digit = x % 10; + + buffer[p--] = '0' + digit; + x = y; + } + + uint64_t xx = x; // Moving to faster 64-bit division thereafter + + while (xx >= 10) { + const auto y = xx / 10ULL; + const auto digit = xx % 10ULL; + + buffer[p--] = '0' + digit; + xx = y; + } + + buffer[p] = '0' + xx; + + return p; +} + +} +#endif + /** * Returns the number of digits in the base 10 representation of an * uint64_t. Useful for preallocating buffers and such. It's also used @@ -182,13 +245,29 @@ void toAppend(char value, Tgt * result) { *result += value; } +template +constexpr typename std::enable_if< + std::is_same::value, + size_t>::type +estimateSpaceNeeded(T) { + return 1; +} + +/** + * Ubiquitous helper template for writing string appenders + */ +template struct IsSomeString { + enum { value = std::is_same::value + || std::is_same::value }; +}; + /** * Everything implicitly convertible to const char* gets appended. */ template typename std::enable_if< std::is_convertible::value - && detail::IsSomeString::value>::type + && IsSomeString::value>::type toAppend(Src value, Tgt * result) { // Treat null pointers like an empty string, as in: // operator<<(std::ostream&, const char*). @@ -198,12 +277,43 @@ toAppend(Src value, Tgt * result) { } } +template +typename std::enable_if< + std::is_convertible::value, + size_t>::type +estimateSpaceNeeded(Src value) { + const char *c = value; + if (c) { + return folly::StringPiece(value).size(); + }; + return 0; +} + +template +typename std::enable_if< + (std::is_convertible::value || + IsSomeString::value) && + !std::is_convertible::value, + size_t>::type +estimateSpaceNeeded(Src value) { + return folly::StringPiece(value).size(); +} + +template +typename std::enable_if< + std::is_pointer::value && + IsSomeString>::value, + size_t>::type +estimateSpaceNeeded(Src value) { + return value->size(); +} + /** * Strings get appended, too. */ template typename std::enable_if< - detail::IsSomeString::value && detail::IsSomeString::value>::type + IsSomeString::value && IsSomeString::value>::type toAppend(const Src& value, Tgt * result) { result->append(value); } @@ -213,7 +323,7 @@ toAppend(const Src& value, Tgt * result) { */ template typename std::enable_if< - detail::IsSomeString::value>::type + IsSomeString::value>::type toAppend(StringPiece value, Tgt * result) { result->append(value.data(), value.size()); } @@ -224,26 +334,75 @@ toAppend(StringPiece value, Tgt * result) { */ template typename std::enable_if< - detail::IsSomeString::value>::type + IsSomeString::value>::type toAppend(const fbstring& value, Tgt * result) { result->append(value.data(), value.size()); } +#if FOLLY_HAVE_INT128_T +/** + * Special handling for 128 bit integers. + */ + +template +void +toAppend(__int128 value, Tgt * result) { + typedef unsigned __int128 Usrc; + char buffer[detail::digitsEnough() + 1]; + size_t p; + + if (value < 0) { + p = detail::unsafeTelescope128(buffer, sizeof(buffer), Usrc(-value)); + buffer[--p] = '-'; + } else { + p = detail::unsafeTelescope128(buffer, sizeof(buffer), value); + } + + result->append(buffer + p, buffer + sizeof(buffer)); +} + +template +void +toAppend(unsigned __int128 value, Tgt * result) { + char buffer[detail::digitsEnough()]; + size_t p; + + p = detail::unsafeTelescope128(buffer, sizeof(buffer), value); + + result->append(buffer + p, buffer + sizeof(buffer)); +} + +template +constexpr typename std::enable_if< + std::is_same::value, + size_t>::type +estimateSpaceNeeded(T) { + return detail::digitsEnough<__int128>(); +} + +template +constexpr typename std::enable_if< + std::is_same::value, + size_t>::type +estimateSpaceNeeded(T) { + return detail::digitsEnough(); +} + +#endif + /** * int32_t and int64_t to string (by appending) go through here. The * result is APPENDED to a preexisting string passed as the second - * parameter. For convenience, the function also returns a reference - * to *result. This should be efficient with fbstring because fbstring + * parameter. This should be efficient with fbstring because fbstring * incurs no dynamic allocation below 23 bytes and no number has more * than 22 bytes in its textual representation (20 for digits, one for * sign, one for the terminating 0). */ template typename std::enable_if< - std::is_integral::value && std::is_signed::value - && detail::IsSomeString::value && sizeof(Src) >= 4>::type + std::is_integral::value && std::is_signed::value && + IsSomeString::value && sizeof(Src) >= 4>::type toAppend(Src value, Tgt * result) { - typedef typename std::make_unsigned::type Usrc; char buffer[20]; if (value < 0) { result->push_back('-'); @@ -253,18 +412,40 @@ toAppend(Src value, Tgt * result) { } } +template +typename std::enable_if< + std::is_integral::value && std::is_signed::value + && sizeof(Src) >= 4 && sizeof(Src) < 16, + size_t>::type +estimateSpaceNeeded(Src value) { + if (value < 0) { + return 1 + digits10(static_cast(-value)); + } + + return digits10(static_cast(value)); +} + /** * As above, but for uint32_t and uint64_t. */ template typename std::enable_if< std::is_integral::value && !std::is_signed::value - && detail::IsSomeString::value && sizeof(Src) >= 4>::type + && IsSomeString::value && sizeof(Src) >= 4>::type toAppend(Src value, Tgt * result) { char buffer[20]; result->append(buffer, buffer + uint64ToBufferUnsafe(value, buffer)); } +template +typename std::enable_if< + std::is_integral::value && !std::is_signed::value + && sizeof(Src) >= 4 && sizeof(Src) < 16, + size_t>::type +estimateSpaceNeeded(Src value) { + return digits10(value); +} + /** * All small signed and unsigned integers to string go through 32-bit * types int32_t and uint32_t, respectively. @@ -272,7 +453,7 @@ toAppend(Src value, Tgt * result) { template typename std::enable_if< std::is_integral::value - && detail::IsSomeString::value && sizeof(Src) < 4>::type + && IsSomeString::value && sizeof(Src) < 4>::type toAppend(Src value, Tgt * result) { typedef typename std::conditional::value, int64_t, uint64_t>::type @@ -280,12 +461,49 @@ toAppend(Src value, Tgt * result) { toAppend(static_cast(value), result); } +template +typename std::enable_if< + std::is_integral::value + && sizeof(Src) < 4 + && !std::is_same::value, + size_t>::type +estimateSpaceNeeded(Src value) { + typedef typename + std::conditional::value, int64_t, uint64_t>::type + Intermediate; + return estimateSpaceNeeded(static_cast(value)); +} + +#if defined(__clang__) || __GNUC_PREREQ(4, 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 + std::is_enum::value && IsSomeString::value>::type +toAppend(Src value, Tgt * result) { + toAppend( + static_cast::type>(value), result); +} + +template +typename std::enable_if< + std::is_enum::value, size_t>::type +estimateSpaceNeeded(Src value) { + return estimateSpaceNeeded( + static_cast::type>(value)); +} + +#else + +/** + * Enumerated values get appended as integers. + */ +template +typename std::enable_if< + std::is_enum::value && IsSomeString::value>::type toAppend(Src value, Tgt * result) { /* static */ if (Src(-1) < 0) { /* static */ if (sizeof(Src) <= sizeof(int)) { @@ -302,15 +520,41 @@ toAppend(Src value, Tgt * result) { } } +template +typename std::enable_if< + std::is_enum::value, size_t>::type +estimateSpaceNeeded(Src value) { + /* static */ if (Src(-1) < 0) { + /* static */ if (sizeof(Src) <= sizeof(int)) { + return estimateSpaceNeeded(static_cast(value)); + } else { + return estimateSpaceNeeded(static_cast(value)); + } + } else { + /* static */ if (sizeof(Src) <= sizeof(int)) { + return estimateSpaceNeeded(static_cast(value)); + } else { + return estimateSpaceNeeded(static_cast(value)); + } + } +} + +#endif // gcc 4.7 onwards + /******************************************************************************* * Conversions from floating-point types to string types. ******************************************************************************/ +namespace detail { +constexpr int kConvMaxDecimalInShortestLow = -6; +constexpr int kConvMaxDecimalInShortestHigh = 21; +} // folly::detail + /** Wrapper around DoubleToStringConverter **/ template typename std::enable_if< std::is_floating_point::value - && detail::IsSomeString::value>::type + && IsSomeString::value>::type toAppend( Src value, Tgt * result, @@ -320,8 +564,8 @@ toAppend( DoubleToStringConverter conv(DoubleToStringConverter::NO_FLAGS, "infinity", "NaN", 'E', - -6, // decimal in shortest low - 21, // decimal in shortest high + detail::kConvMaxDecimalInShortestLow, + detail::kConvMaxDecimalInShortestHigh, 6, // max leading padding zeros 1); // max trailing padding zeros char buffer[256]; @@ -349,44 +593,285 @@ toAppend( template typename std::enable_if< std::is_floating_point::value - && detail::IsSomeString::value>::type + && IsSomeString::value>::type toAppend(Src value, Tgt * result) { toAppend( value, result, double_conversion::DoubleToStringConverter::SHORTEST, 0); } /** - * Variadic conversion to string. Appends each element in turn. + * Upper bound of the length of the output from + * DoubleToStringConverter::ToShortest(double, StringBuilder*), + * as used in toAppend(double, string*). + */ +template +typename std::enable_if< + std::is_floating_point::value, size_t>::type +estimateSpaceNeeded(Src value) { + // kBase10MaximalLength is 17. We add 1 for decimal point, + // e.g. 10.0/9 is 17 digits and 18 characters, including the decimal point. + constexpr int kMaxMantissaSpace = + double_conversion::DoubleToStringConverter::kBase10MaximalLength + 1; + // strlen("E-") + digits10(numeric_limits::max_exponent10) + constexpr int kMaxExponentSpace = 2 + 3; + static const int kMaxPositiveSpace = std::max({ + // E.g. 1.1111111111111111E-100. + kMaxMantissaSpace + kMaxExponentSpace, + // E.g. 0.000001.1111111111111111, if kConvMaxDecimalInShortestLow is -6. + kMaxMantissaSpace - detail::kConvMaxDecimalInShortestLow, + // If kConvMaxDecimalInShortestHigh is 21, then 1e21 is the smallest + // number > 1 which ToShortest outputs in exponential notation, + // so 21 is the longest non-exponential number > 1. + detail::kConvMaxDecimalInShortestHigh + }); + return kMaxPositiveSpace + (value < 0); // +1 for minus sign, if negative +} + +/** + * This can be specialized, together with adding specialization + * for estimateSpaceNeed for your type, so that we allocate + * as much as you need instead of the default + */ +template +struct HasLengthEstimator : std::false_type {}; + +template +constexpr typename std::enable_if< + !std::is_fundamental::value + && !IsSomeString::value + && !std::is_convertible::value + && !std::is_convertible::value + && !std::is_enum::value + && !HasLengthEstimator::value, + size_t>::type +estimateSpaceNeeded(const Src&) { + return sizeof(Src) + 1; // dumbest best effort ever? +} + +namespace detail { + +inline size_t estimateSpaceToReserve(size_t sofar) { + return sofar; +} + +template +size_t estimateSpaceToReserve(size_t sofar, const T& v, const Ts&... vs) { + return estimateSpaceToReserve(sofar + estimateSpaceNeeded(v), vs...); +} + +template +size_t estimateSpaceToReserve(size_t sofar, const T& v) { + return sofar + estimateSpaceNeeded(v); +} + +template +void reserveInTarget(const Ts&...vs) { + getLastElement(vs...)->reserve(estimateSpaceToReserve(0, vs...)); +} + +template +void reserveInTargetDelim(const Delimiter& d, const Ts&...vs) { + static_assert(sizeof...(vs) >= 2, "Needs at least 2 args"); + size_t fordelim = (sizeof...(vs) - 2) * estimateSpaceToReserve(0, d); + getLastElement(vs...)->reserve(estimateSpaceToReserve(fordelim, vs...)); +} + +/** + * Variadic base case: append one element */ +template +typename std::enable_if< + IsSomeString::type> + ::value>::type +toAppendStrImpl(const T& v, Tgt result) { + toAppend(v, result); +} + template typename std::enable_if= 2 - && detail::IsSomeString< + && IsSomeString< typename std::remove_pointer< - typename std::tuple_element< - sizeof...(Ts) - 1, std::tuple - >::type>::type>::value>::type -toAppend(const T& v, const Ts&... vs) { + typename detail::last_element::type + >::type>::value>::type +toAppendStrImpl(const T& v, const Ts&... vs) { + toAppend(v, getLastElement(vs...)); + toAppendStrImpl(vs...); +} + +template +typename std::enable_if< + IsSomeString::type> + ::value>::type +toAppendDelimStrImpl(const Delimiter& delim, const T& v, Tgt result) { + toAppend(v, result); +} + +template +typename std::enable_if= 2 + && IsSomeString< + typename std::remove_pointer< + typename detail::last_element::type + >::type>::value>::type +toAppendDelimStrImpl(const Delimiter& delim, const T& v, const Ts&... vs) { + // we are really careful here, calling toAppend with just one element does + // not try to estimate space needed (as we already did that). If we call + // toAppend(v, delim, ....) we would do unnecesary size calculation toAppend(v, detail::getLastElement(vs...)); + toAppend(delim, detail::getLastElement(vs...)); + toAppendDelimStrImpl(delim, vs...); +} +} // folly::detail + + +/** + * Variadic conversion to string. Appends each element in turn. + * If we have two or more things to append, we it will not reserve + * the space for them and will depend on strings exponential growth. + * If you just append once consider using toAppendFit which reserves + * the space needed (but does not have exponential as a result). + */ +template +typename std::enable_if= 3 + && IsSomeString< + typename std::remove_pointer< + typename detail::last_element::type + >::type>::value>::type +toAppend(const Ts&... vs) { + detail::toAppendStrImpl(vs...); +} + +/** + * Special version of the call that preallocates exaclty as much memory + * as need for arguments to be stored in target. This means we are + * not doing exponential growth when we append. If you are using it + * in a loop you are aiming at your foot with a big perf-destroying + * bazooka. + * On the other hand if you are appending to a string once, this + * will probably save a few calls to malloc. + */ +template +typename std::enable_if< + IsSomeString< + typename std::remove_pointer< + typename detail::last_element::type + >::type>::value>::type +toAppendFit(const Ts&... vs) { + detail::reserveInTarget(vs...); toAppend(vs...); } +template +void toAppendFit(const Ts&) {} + /** * Variadic base case: do nothing. */ template -typename std::enable_if::value>::type +typename std::enable_if::value>::type toAppend(Tgt* result) { } +/** + * Variadic base case: do nothing. + */ +template +typename std::enable_if::value>::type +toAppendDelim(const Delimiter& delim, Tgt* result) { +} + +/** + * 1 element: same as toAppend. + */ +template +typename std::enable_if::value>::type +toAppendDelim(const Delimiter& delim, const T& v, Tgt* tgt) { + toAppend(v, tgt); +} + +/** + * Append to string with a delimiter in between elements. Check out + * comments for toAppend for details about memory allocation. + */ +template +typename std::enable_if= 3 + && IsSomeString< + typename std::remove_pointer< + typename detail::last_element::type + >::type>::value>::type +toAppendDelim(const Delimiter& delim, const Ts&... vs) { + detail::toAppendDelimStrImpl(delim, vs...); +} + +/** + * Detail in comment for toAppendFit + */ +template +typename std::enable_if< + IsSomeString< + typename std::remove_pointer< + typename detail::last_element::type + >::type>::value>::type +toAppendDelimFit(const Delimiter& delim, const Ts&... vs) { + detail::reserveInTargetDelim(delim, vs...); + toAppendDelim(delim, vs...); +} + +template +void toAppendDelimFit(const De&, const Ts&) {} + +/** + * to(SomeString str) returns itself. As both std::string and + * folly::fbstring use Copy-on-Write, it's much more efficient by + * avoiding copying the underlying char array. + */ +template +typename std::enable_if< + IsSomeString::value && std::is_same::value, + Tgt>::type +to(const Src & value) { + return value; +} + /** * to(v1, v2, ...) uses toAppend() (see below) as back-end * for all types. */ template -typename std::enable_if::value, Tgt>::type +typename std::enable_if< + IsSomeString::value && ( + sizeof...(Ts) != 1 || + !std::is_same::type>::value), + Tgt>::type to(const Ts&... vs) { Tgt result; - toAppend(vs..., &result); + toAppendFit(vs..., &result); + return result; +} + +/** + * toDelim(SomeString str) returns itself. + */ +template +typename std::enable_if< + IsSomeString::value && std::is_same::value, + Tgt>::type +toDelim(const Delim& delim, const Src & value) { + return value; +} + +/** + * toDelim(delim, v1, v2, ...) uses toAppendDelim() as + * back-end for all types. + */ +template +typename std::enable_if< + IsSomeString::value && ( + sizeof...(Ts) != 1 || + !std::is_same::type>::value), + Tgt>::type +toDelim(const Delim& delim, const Ts&... vs) { + Tgt result; + toAppendDelimFit(delim, vs..., &result); return result; } @@ -436,7 +921,7 @@ namespace detail { // still not overflow uint16_t. constexpr int32_t OOR = 10000; -__attribute__((aligned(16))) constexpr uint16_t shift1[] = { +__attribute__((__aligned__(16))) constexpr uint16_t shift1[] = { OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 0-9 OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 10 OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 20 @@ -465,7 +950,7 @@ __attribute__((aligned(16))) constexpr uint16_t shift1[] = { OOR, OOR, OOR, OOR, OOR, OOR // 250 }; -__attribute__((aligned(16))) constexpr uint16_t shift10[] = { +__attribute__((__aligned__(16))) constexpr uint16_t shift10[] = { OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 0-9 OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 10 OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 20 @@ -494,7 +979,7 @@ __attribute__((aligned(16))) constexpr uint16_t shift10[] = { OOR, OOR, OOR, OOR, OOR, OOR // 250 }; -__attribute__((aligned(16))) constexpr uint16_t shift100[] = { +__attribute__((__aligned__(16))) constexpr uint16_t shift100[] = { OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 0-9 OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 10 OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 20 @@ -523,7 +1008,7 @@ __attribute__((aligned(16))) constexpr uint16_t shift100[] = { OOR, OOR, OOR, OOR, OOR, OOR // 250 }; -__attribute__((aligned(16))) constexpr uint16_t shift1000[] = { +__attribute__((__aligned__(16))) constexpr uint16_t shift1000[] = { OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 0-9 OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 10 OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 20 @@ -720,10 +1205,10 @@ to(StringPiece * src) { auto t = detail::digits_to::type>(b, m); if (negative) { result = -t; - FOLLY_RANGE_CHECK(result <= 0, "Negative overflow"); + FOLLY_RANGE_CHECK(is_non_positive(result), "Negative overflow"); } else { result = t; - FOLLY_RANGE_CHECK(result >= 0, "Overflow"); + FOLLY_RANGE_CHECK(is_non_negative(result), "Overflow"); } } src->advance(m - src->data()); @@ -794,8 +1279,9 @@ to(StringPiece *const src) { FOLLY_RANGE_CHECK(!src->empty(), "No digits found in input string"); int length; - auto result = conv.StringToDouble(src->data(), src->size(), - &length); // processed char count + auto result = conv.StringToDouble(src->data(), + static_cast(src->size()), + &length); // processed char count if (!std::isnan(result)) { src->advance(length); @@ -912,12 +1398,26 @@ to(const Src & value) { * Enum to anything and back ******************************************************************************/ +#if defined(__clang__) || __GNUC_PREREQ(4, 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 +1436,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 +1451,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 @@ -961,6 +1460,8 @@ to(const Src & value) { // to avoid defining this global macro name in other files that include Conv.h. #ifndef FOLLY_CONV_INTERNAL #undef FOLLY_RANGE_CHECK +#undef FOLLY_RANGE_CHECK_STRINGIZE2 +#undef FOLLY_RANGE_CHECK_STRINGIZE #endif #endif /* FOLLY_BASE_CONV_H_ */