logging: fix compiler compatibility for one more constexpr function
[folly.git] / folly / Conv.h
index 9fc196965763bfddcfb997c1f1e4b781abd2637c..72518d808f5ad895ad04c33eadb5a87a711f5cc9 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright 2016 Facebook, Inc.
+ * Copyright 2017 Facebook, Inc.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -43,6 +43,7 @@
 #include <folly/FBString.h>
 #include <folly/Likely.h>
 #include <folly/Range.h>
+#include <folly/Traits.h>
 #include <folly/Unit.h>
 #include <folly/portability/Math.h>
 
@@ -187,6 +188,33 @@ to(const Src& value) {
 
 namespace detail {
 
+#ifdef _MSC_VER
+// MSVC can't quite figure out the LastElementImpl::call() stuff
+// in the base implementation, so we have to use tuples instead,
+// which result in significantly more templates being compiled,
+// though the runtime performance is the same.
+
+template <typename... Ts>
+auto getLastElement(Ts&&... ts) -> decltype(
+    std::get<sizeof...(Ts)-1>(std::forward_as_tuple(std::forward<Ts>(ts)...))) {
+  return std::get<sizeof...(Ts)-1>(
+      std::forward_as_tuple(std::forward<Ts>(ts)...));
+}
+
+inline void getLastElement() {}
+
+template <size_t size, typename... Ts>
+struct LastElementType : std::tuple_element<size - 1, std::tuple<Ts...>> {};
+
+template <>
+struct LastElementType<0> {
+  using type = void;
+};
+
+template <class... Ts>
+struct LastElement
+    : std::decay<typename LastElementType<sizeof...(Ts), Ts...>::type> {};
+#else
 template <typename... Ts>
 struct LastElementImpl {
   static void call(Ignored<Ts>...) {}
@@ -210,6 +238,7 @@ template <class... Ts>
 struct LastElement : std::decay<decltype(
                          LastElementImpl<Ts...>::call(std::declval<Ts>()...))> {
 };
+#endif
 
 } // namespace detail
 
@@ -354,12 +383,12 @@ inline uint32_t uint64ToBufferUnsafe(uint64_t v, char *const buffer) {
     // Keep these together so a peephole optimization "sees" them and
     // computes them in one shot.
     auto const q = v / 10;
-    auto const r = static_cast<uint32_t>(v % 10);
+    auto const r = static_cast<char>(v % 10);
     buffer[pos--] = '0' + r;
     v = q;
   }
   // Last digit is trivial to handle
-  buffer[pos] = static_cast<uint32_t>(v) + '0';
+  buffer[pos] = static_cast<char>(v) + '0';
   return result;
 }
 
@@ -528,9 +557,11 @@ toAppend(Src value, Tgt * result) {
   char buffer[20];
   if (value < 0) {
     result->push_back('-');
-    result->append(buffer, uint64ToBufferUnsafe(-uint64_t(value), buffer));
+    result->append(
+        buffer,
+        uint64ToBufferUnsafe(~static_cast<uint64_t>(value) + 1, buffer));
   } else {
-    result->append(buffer, uint64ToBufferUnsafe(value, buffer));
+    result->append(buffer, uint64ToBufferUnsafe(uint64_t(value), buffer));
   }
 }
 
@@ -652,14 +683,14 @@ toAppend(
       conv.ToShortest(value, &builder);
       break;
     case DoubleToStringConverter::FIXED:
-      conv.ToFixed(value, numDigits, &builder);
+      conv.ToFixed(value, int(numDigits), &builder);
       break;
     default:
       CHECK(mode == DoubleToStringConverter::PRECISION);
-      conv.ToPrecision(value, numDigits, &builder);
+      conv.ToPrecision(value, int(numDigits), &builder);
       break;
   }
-  const size_t length = builder.position();
+  const size_t length = size_t(builder.position());
   builder.Finalize();
   result->append(buffer, length);
 }
@@ -701,7 +732,9 @@ estimateSpaceNeeded(Src value) {
       // so 21 is the longest non-exponential number > 1.
       detail::kConvMaxDecimalInShortestHigh
     });
-  return kMaxPositiveSpace + (value < 0);  // +1 for minus sign, if negative
+  return size_t(
+      kMaxPositiveSpace +
+      (value < 0 ? 1 : 0)); // +1 for minus sign, if negative
 }
 
 /**
@@ -928,15 +961,37 @@ to(const Ts&... vs) {
   return result;
 }
 
+/**
+ * Special version of to<SomeString> for floating point. When calling
+ * folly::to<SomeString>(double), generic implementation above will
+ * firstly reserve 24 (or 25 when negative value) bytes. This will
+ * introduce a malloc call for most mainstream string implementations.
+ *
+ * But for most cases, a floating point doesn't need 24 (or 25) bytes to
+ * be converted as a string.
+ *
+ * This special version will not do string reserve.
+ */
+template <class Tgt, class Src>
+typename std::enable_if<
+    IsSomeString<Tgt>::value && std::is_floating_point<Src>::value,
+    Tgt>::type
+to(Src value) {
+  Tgt result;
+  toAppend(value, &result);
+  return result;
+}
+
 /**
  * toDelim<SomeString>(SomeString str) returns itself.
  */
 template <class Tgt, class Delim, class Src>
-typename std::enable_if<IsSomeString<Tgt>::value &&
-                            std::is_same<Tgt, Src>::value,
-                        Tgt>::type
-toDelim(const Delim& /* delim */, const Src& value) {
-  return value;
+typename std::enable_if<
+    IsSomeString<Tgt>::value &&
+        std::is_same<Tgt, typename std::decay<Src>::type>::value,
+    Tgt>::type
+toDelim(const Delim& /* delim */, Src&& value) {
+  return std::forward<Src>(value);
 }
 
 /**
@@ -1111,7 +1166,7 @@ to(const char* b, const char* e) {
  * Parsing strings to numeric types.
  */
 template <typename Tgt>
-FOLLY_WARN_UNUSED_RESULT inline typename std::enable_if<
+FOLLY_NODISCARD inline typename std::enable_if<
     std::is_arithmetic<Tgt>::value,
     Expected<StringPiece, ConversionCode>>::type
 parseTo(StringPiece src, Tgt& out) {
@@ -1125,6 +1180,20 @@ parseTo(StringPiece src, Tgt& out) {
 
 namespace detail {
 
+/**
+ * Bool to integral/float doesn't need any special checks, and this
+ * overload means we aren't trying to see if a bool is less than
+ * an integer.
+ */
+template <class Tgt>
+typename std::enable_if<
+    !std::is_same<Tgt, bool>::value &&
+        (std::is_integral<Tgt>::value || std::is_floating_point<Tgt>::value),
+    Expected<Tgt, ConversionCode>>::type
+convertTo(const bool& value) noexcept {
+  return static_cast<Tgt>(value ? 1 : 0);
+}
+
 /**
  * Checked conversion from integral to integral. The checks are only
  * performed when meaningful, e.g. conversion from int to long goes
@@ -1138,7 +1207,8 @@ typename std::enable_if<
     Expected<Tgt, ConversionCode>>::type
 convertTo(const Src& value) noexcept {
   /* static */ if (
-      std::numeric_limits<Tgt>::max() < std::numeric_limits<Src>::max()) {
+      folly::_t<std::make_unsigned<Tgt>>(std::numeric_limits<Tgt>::max()) <
+      folly::_t<std::make_unsigned<Src>>(std::numeric_limits<Src>::max())) {
     if (greater_than<Tgt, std::numeric_limits<Tgt>::max()>(value)) {
       return makeUnexpected(ConversionCode::ARITH_POSITIVE_OVERFLOW);
     }
@@ -1173,7 +1243,7 @@ convertTo(const Src& value) noexcept {
       return makeUnexpected(ConversionCode::ARITH_NEGATIVE_OVERFLOW);
     }
   }
-  return boost::implicit_cast<Tgt>(value);
+  return static_cast<Tgt>(value);
 }
 
 /**
@@ -1303,7 +1373,7 @@ typename std::enable_if<detail::IsArithToArith<Tgt, Src>::value, Tgt>::type to(
  * }
  ******************************************************************************/
 template <class T>
-FOLLY_WARN_UNUSED_RESULT typename std::enable_if<
+FOLLY_NODISCARD typename std::enable_if<
     std::is_enum<T>::value,
     Expected<StringPiece, ConversionCode>>::type
 parseTo(StringPiece in, T& out) noexcept {
@@ -1313,7 +1383,7 @@ parseTo(StringPiece in, T& out) noexcept {
   return restOrError;
 }
 
-FOLLY_WARN_UNUSED_RESULT
+FOLLY_NODISCARD
 inline Expected<StringPiece, ConversionCode> parseTo(
     StringPiece in,
     StringPiece& out) noexcept {
@@ -1321,7 +1391,7 @@ inline Expected<StringPiece, ConversionCode> parseTo(
   return StringPiece{in.end(), in.end()};
 }
 
-FOLLY_WARN_UNUSED_RESULT
+FOLLY_NODISCARD
 inline Expected<StringPiece, ConversionCode> parseTo(
     StringPiece in,
     std::string& out) {
@@ -1330,7 +1400,7 @@ inline Expected<StringPiece, ConversionCode> parseTo(
   return StringPiece{in.end(), in.end()};
 }
 
-FOLLY_WARN_UNUSED_RESULT
+FOLLY_NODISCARD
 inline Expected<StringPiece, ConversionCode> parseTo(
     StringPiece in,
     fbstring& out) {
@@ -1440,7 +1510,7 @@ Expected<Tgt, detail::ParseToError<Tgt>> tryTo(StringPiece* src) {
 
 template <class Tgt>
 Tgt to(StringPiece* src) {
-  Tgt result;
+  Tgt result{};
   using Error = detail::ParseToError<Tgt>;
   return parseTo(*src, result)
       .thenOrThrow(
@@ -1467,7 +1537,7 @@ tryTo(const Src& value) {
 template <class Tgt, class Src>
 typename std::enable_if<
     std::is_enum<Tgt>::value && !std::is_same<Src, Tgt>::value,
-    Tgt>::type
+    Expected<Tgt, ConversionCode>>::type
 tryTo(const Src& value) {
   using I = typename std::underlying_type<Tgt>::type;
   return tryTo<I>(value).then([](I i) { return static_cast<Tgt>(i); });