Make Range.h and FBString.h mutually independent
[folly.git] / folly / Range.h
index 3bfef1f27d48d638a0f8c2922d3dd4d6667f774e..ce3e1fe8065346a54a2363045283c7faaaa8bde3 100644 (file)
@@ -19,7 +19,6 @@
 
 #pragma once
 
-#include <folly/FBString.h>
 #include <folly/Portability.h>
 #include <folly/hash/SpookyHashV2.h>
 #include <folly/portability/BitsFunctexcept.h>
@@ -30,6 +29,7 @@
 #include <glog/logging.h>
 #include <algorithm>
 #include <array>
+#include <cassert>
 #include <climits>
 #include <cstddef>
 #include <cstring>
 #include <string>
 #include <type_traits>
 
-// libc++ doesn't provide this header, nor does msvc
-#ifdef FOLLY_HAVE_BITS_CXXCONFIG_H
-// This file appears in two locations: inside fbcode and in the
-// libstdc++ source code (when embedding fbstring as std::string).
-// To aid in this schizophrenic use, two macros are defined in
-// c++config.h:
-//   _LIBSTDCXX_FBSTRING - Set inside libstdc++.  This is useful to
-//      gate use inside fbcode v. libstdc++
-#include <bits/c++config.h>
-#endif
-
 #include <folly/CpuId.h>
 #include <folly/Likely.h>
 #include <folly/Traits.h>
@@ -61,6 +50,15 @@ FOLLY_GCC_DISABLE_WARNING("-Wshadow")
 
 namespace folly {
 
+/**
+ * Ubiquitous helper template for knowing what's a string.
+ */
+template <class T>
+struct IsSomeString : std::false_type {};
+
+template <>
+struct IsSomeString<std::string> : std::true_type {};
+
 template <class Iter>
 class Range;
 
@@ -212,9 +210,12 @@ class Range : private boost::totally_ordered<Range<Iter>> {
   /* implicit */ Range(std::nullptr_t) = delete;
 #endif
 
-  template <class T = Iter, typename detail::IsCharPointer<T>::type = 0>
   constexpr /* implicit */ Range(Iter str)
-      : b_(str), e_(str + constexpr_strlen(str)) {}
+      : b_(str), e_(str + constexpr_strlen(str)) {
+    static_assert(
+        std::is_same<int, typename detail::IsCharPointer<Iter>::type>::value,
+        "This constructor is only available for character ranges");
+  }
 
   template <class T = Iter, typename detail::IsCharPointer<T>::const_type = 0>
   /* implicit */ Range(const std::string& str)
@@ -248,30 +249,58 @@ class Range : private boost::totally_ordered<Range<Iter>> {
   Range(const Range& other, size_type first, size_type length = npos)
       : Range(other.subpiece(first, length)) {}
 
-  template <class T = Iter, typename detail::IsCharPointer<T>::const_type = 0>
-  /* implicit */ Range(const fbstring& str)
-      : b_(str.data()), e_(b_ + str.size()) {}
+  template <
+      class Container,
+      class = typename std::enable_if<
+          std::is_same<Iter, typename Container::const_pointer>::value>::type,
+      class = decltype(
+          Iter(std::declval<Container const&>().data()),
+          Iter(
+              std::declval<Container const&>().data() +
+              std::declval<Container const&>().size()))>
+  /* implicit */ constexpr Range(Container const& container)
+      : b_(container.data()), e_(b_ + container.size()) {}
 
-  template <class T = Iter, typename detail::IsCharPointer<T>::const_type = 0>
-  Range(const fbstring& str, fbstring::size_type startFrom) {
-    if (UNLIKELY(startFrom > str.size())) {
+  template <
+      class Container,
+      class = typename std::enable_if<
+          std::is_same<Iter, typename Container::const_pointer>::value>::type,
+      class = decltype(
+          Iter(std::declval<Container const&>().data()),
+          Iter(
+              std::declval<Container const&>().data() +
+              std::declval<Container const&>().size()))>
+  Range(Container const& container, typename Container::size_type startFrom) {
+    auto const cdata = container.data();
+    auto const csize = container.size();
+    if (UNLIKELY(startFrom > csize)) {
       std::__throw_out_of_range("index out of range");
     }
-    b_ = str.data() + startFrom;
-    e_ = str.data() + str.size();
+    b_ = cdata + startFrom;
+    e_ = cdata + csize;
   }
 
-  template <class T = Iter, typename detail::IsCharPointer<T>::const_type = 0>
+  template <
+      class Container,
+      class = typename std::enable_if<
+          std::is_same<Iter, typename Container::const_pointer>::value>::type,
+      class = decltype(
+          Iter(std::declval<Container const&>().data()),
+          Iter(
+              std::declval<Container const&>().data() +
+              std::declval<Container const&>().size()))>
   Range(
-      const fbstring& str,
-      fbstring::size_type startFrom,
-      fbstring::size_type size) {
-    if (UNLIKELY(startFrom > str.size())) {
+      Container const& container,
+      typename Container::size_type startFrom,
+      typename Container::size_type size) {
+    auto const cdata = container.data();
+    auto const csize = container.size();
+    if (UNLIKELY(startFrom > csize)) {
       std::__throw_out_of_range("index out of range");
     }
-    b_ = str.data() + startFrom;
-    if (str.size() - startFrom < size) {
-      e_ = str.data() + str.size();
+    b_ = cdata + startFrom;
+    if (csize - startFrom < size) {
+      e_ = cdata + csize;
     } else {
       e_ = b_ + size;
     }
@@ -445,19 +474,22 @@ class Range : private boost::totally_ordered<Range<Iter>> {
     assert(b_ < e_);
     return detail::value_before(e_);
   }
-  // Works only for Range<const char*> and Range<char*>
-  std::string str() const {
-    return std::string(b_, size());
-  }
-  std::string toString() const {
-    return str();
+
+  template <typename Tgt>
+  auto to() const
+      -> decltype(Tgt(std::declval<Iter const&>(), std::declval<size_type>())) {
+    return Tgt(b_, size());
   }
   // Works only for Range<const char*> and Range<char*>
-  fbstring fbstr() const {
-    return fbstring(b_, size());
+  template <typename Tgt = std::string>
+  auto str() const
+      -> decltype(Tgt(std::declval<Iter const&>(), std::declval<size_type>())) {
+    return to<Tgt>();
   }
-  fbstring toFbstring() const {
-    return fbstr();
+  template <typename Tgt = std::string>
+  auto toString() const
+      -> decltype(Tgt(std::declval<Iter const&>(), std::declval<size_type>())) {
+    return to<Tgt>();
   }
 
   const_range_type castToConst() const {
@@ -1312,15 +1344,38 @@ struct hasher<
 };
 
 /**
- * Ubiquitous helper template for knowing what's a string
+ * _sp is a user-defined literal suffix to make an appropriate Range
+ * specialization from a literal string.
+ *
+ * Modeled after C++17's `sv` suffix.
  */
-template <class T>
-struct IsSomeString {
-  enum {
-    value =
-        std::is_same<T, std::string>::value || std::is_same<T, fbstring>::value
-  };
-};
+inline namespace literals {
+inline namespace string_piece_literals {
+constexpr Range<char const*> operator"" _sp(
+    char const* str,
+    size_t len) noexcept {
+  return Range<char const*>(str, len);
+}
+
+constexpr Range<char16_t const*> operator"" _sp(
+    char16_t const* str,
+    size_t len) noexcept {
+  return Range<char16_t const*>(str, len);
+}
+
+constexpr Range<char32_t const*> operator"" _sp(
+    char32_t const* str,
+    size_t len) noexcept {
+  return Range<char32_t const*>(str, len);
+}
+
+constexpr Range<wchar_t const*> operator"" _sp(
+    wchar_t const* str,
+    size_t len) noexcept {
+  return Range<wchar_t const*>(str, len);
+}
+} // inline namespace string_piece_literals
+} // inline namespace literals
 
 } // namespace folly