Commit 5348a349 authored by Nathan Bronson's avatar Nathan Bronson Committed by Facebook Github Bot

add implicit conversion from Range to std::string_view

Summary:
Make StringPiece and MutableStringPiece implicitly convertible
to std::string_view.  This diff also adds FOLLY_HAS_STRING_VIEW to
Portability.h, which will be defined to be 0 or 1.

Reviewed By: vitaut

Differential Revision: D10187660

fbshipit-source-id: 90e6f3ac82e47eae423dab8f1de1d39e5ac997c4
parent 1ed5cafc
...@@ -19,7 +19,6 @@ ...@@ -19,7 +19,6 @@
#include <cstddef> #include <cstddef>
#include <folly/portability/Config.h> #include <folly/portability/Config.h>
#include <folly/CPortability.h> #include <folly/CPortability.h>
// Unaligned loads and stores // Unaligned loads and stores
...@@ -476,3 +475,20 @@ constexpr auto kCpplibVer = 0; ...@@ -476,3 +475,20 @@ constexpr auto kCpplibVer = 0;
#else #else
#define FOLLY_HAS_EXCEPTIONS 1 // default assumption for unknown platforms #define FOLLY_HAS_EXCEPTIONS 1 // default assumption for unknown platforms
#endif #endif
// feature test __cpp_lib_string_view is defined in <string>, which is
// too heavy to include here. MSVC __has_include support arrived later
// than string_view, so we need an alternate case for it.
#ifdef __has_include
#if __has_include(<string_view>) && __cplusplus >= 201703L
#define FOLLY_HAS_STRING_VIEW 1
#else
#define FOLLY_HAS_STRING_VIEW 0
#endif
#else // __has_include
#if _MSC_VER >= 1910 && (_MSVC_LANG > 201402 || __cplusplus > 201402)
#define FOLLY_HAS_STRING_VIEW 1
#else
#define FOLLY_HAS_STRING_VIEW 0
#endif
#endif // __has_include
...@@ -37,6 +37,10 @@ ...@@ -37,6 +37,10 @@
#include <string> #include <string>
#include <type_traits> #include <type_traits>
#if FOLLY_HAS_STRING_VIEW
#include <string_view> // @manual
#endif
#include <folly/CpuId.h> #include <folly/CpuId.h>
#include <folly/Likely.h> #include <folly/Likely.h>
#include <folly/Traits.h> #include <folly/Traits.h>
...@@ -474,6 +478,37 @@ class Range { ...@@ -474,6 +478,37 @@ class Range {
return detail::value_before(e_); return detail::value_before(e_);
} }
private:
// It would be nice to be able to implicit convert to any target type
// T for which either an (Iter, Iter) or (Iter, size_type) noexcept
// constructor was available, and explicitly convert to any target
// type for which those signatures were available but not noexcept.
// The problem is that this creates ambiguity when there is also a
// T constructor that takes a type U that is implicitly convertible
// from Range.
//
// To avoid ambiguity, we need to avoid having explicit operator T
// and implicit operator U coexist when T is constructible from U.
// U cannot be deduced when searching for operator T (and C++ won't
// perform an existential search for it), so we must limit the implicit
// target types to a finite set that we can enumerate.
//
// At the moment the set of implicit target types consists of just
// std::string_view (when it is available).
#if FOLLY_HAS_STRING_VIEW
using StringViewType =
std::basic_string_view<std::remove_const_t<value_type>>;
template <typename Target>
using IsConstructibleViaStringView = StrictConjunction<
std::is_constructible<StringViewType, Iter const&, size_type>,
std::is_constructible<Target, StringViewType>>;
#else
template <typename Target>
using IsConstructibleViaStringView = std::false_type;
#endif
public:
/// explicit operator conversion to any compatible type /// explicit operator conversion to any compatible type
/// ///
/// A compatible type is one which is constructible with an iterator and a /// A compatible type is one which is constructible with an iterator and a
...@@ -484,7 +519,8 @@ class Range { ...@@ -484,7 +519,8 @@ class Range {
template < template <
typename Tgt, typename Tgt,
std::enable_if_t< std::enable_if_t<
std::is_constructible<Tgt, Iter const&, size_type>::value, std::is_constructible<Tgt, Iter const&, size_type>::value &&
!IsConstructibleViaStringView<Tgt>::value,
int> = 0> int> = 0>
constexpr explicit operator Tgt() const noexcept( constexpr explicit operator Tgt() const noexcept(
std::is_nothrow_constructible<Tgt, Iter const&, size_type>::value) { std::is_nothrow_constructible<Tgt, Iter const&, size_type>::value) {
...@@ -494,13 +530,29 @@ class Range { ...@@ -494,13 +530,29 @@ class Range {
typename Tgt, typename Tgt,
std::enable_if_t< std::enable_if_t<
!std::is_constructible<Tgt, Iter const&, size_type>::value && !std::is_constructible<Tgt, Iter const&, size_type>::value &&
std::is_constructible<Tgt, Iter const&, Iter const&>::value, std::is_constructible<Tgt, Iter const&, Iter const&>::value &&
!IsConstructibleViaStringView<Tgt>::value,
int> = 0> int> = 0>
constexpr explicit operator Tgt() const noexcept( constexpr explicit operator Tgt() const noexcept(
std::is_nothrow_constructible<Tgt, Iter const&, Iter const&>::value) { std::is_nothrow_constructible<Tgt, Iter const&, Iter const&>::value) {
return Tgt(b_, e_); return Tgt(b_, e_);
} }
#if FOLLY_HAS_STRING_VIEW
/// implicit operator conversion to std::string_view
template <
typename Tgt,
std::enable_if_t<
std::is_same<Tgt, StringViewType>::value &&
std::is_constructible<StringViewType, Iter const&, size_type>::
value,
int> = 0>
constexpr operator Tgt() const noexcept(
std::is_nothrow_constructible<Tgt, Iter const&, size_type>::value) {
return Tgt(b_, walk_size());
}
#endif
/// explicit non-operator conversion to any compatible type /// explicit non-operator conversion to any compatible type
/// ///
/// A compatible type is one which is constructible with an iterator and a /// A compatible type is one which is constructible with an iterator and a
......
...@@ -14,6 +14,12 @@ ...@@ -14,6 +14,12 @@
* limitations under the License. * limitations under the License.
*/ */
#include <folly/Portability.h>
#if FOLLY_HAS_STRING_VIEW
#include <string_view> // @manual
#endif
#include <memory> #include <memory>
#include <folly/portability/GTest.h> #include <folly/portability/GTest.h>
......
...@@ -20,6 +20,7 @@ ...@@ -20,6 +20,7 @@
#include <folly/Range.h> #include <folly/Range.h>
#include <array> #include <array>
#include <deque>
#include <iterator> #include <iterator>
#include <limits> #include <limits>
#include <random> #include <random>
...@@ -1559,3 +1560,64 @@ TEST(Range, MutableStringPieceExplicitConversionOperator) { ...@@ -1559,3 +1560,64 @@ TEST(Range, MutableStringPieceExplicitConversionOperator) {
EXPECT_EQ("hello", piecem.to<fake_string_view>(tag{})); EXPECT_EQ("hello", piecem.to<fake_string_view>(tag{}));
EXPECT_EQ("hello", piecec.to<fake_string_view>(tag{})); EXPECT_EQ("hello", piecec.to<fake_string_view>(tag{}));
} }
#if FOLLY_HAS_STRING_VIEW
namespace {
std::size_t stringViewSize(std::string_view s) {
return s.size();
}
std::size_t stringPieceSize(StringPiece s) {
return s.size();
}
struct TrickyTarget {
TrickyTarget(char const*, char const*) : which{1} {}
TrickyTarget(char const*, std::size_t) : which{2} {}
TrickyTarget(std::string_view) : which{3} {}
int which;
};
struct TrickierTarget {
TrickierTarget(std::deque<char>::const_iterator, std::size_t) : which{1} {}
TrickierTarget(std::string_view) : which{2} {}
int which;
};
} // namespace
TEST(StringPiece, StringViewConversion) {
StringPiece piece("foo");
std::string str("bar");
MutableStringPiece mut(str.data(), str.size());
std::string_view view("baz");
EXPECT_EQ(stringViewSize(piece), 3);
EXPECT_EQ(stringViewSize(str), 3);
EXPECT_EQ(stringViewSize(mut), 3);
EXPECT_EQ(stringPieceSize(mut), 3);
EXPECT_EQ(stringPieceSize(str), 3);
EXPECT_EQ(stringPieceSize(view), 3);
view = mut;
piece = view;
EXPECT_EQ(piece[2], 'r');
piece = "quux";
view = piece;
EXPECT_EQ(view.size(), 4);
TrickyTarget tt1(piece);
EXPECT_EQ(tt1.which, 3);
TrickyTarget tt2(view);
EXPECT_EQ(tt2.which, 3);
std::deque<char> deq;
deq.push_back('a');
deq.push_back('b');
deq.push_back('c');
Range<std::deque<char>::const_iterator> deqRange{deq.begin(), deq.end()};
TrickierTarget tt3(deqRange);
EXPECT_EQ(tt3.which, 1);
}
#endif
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment