Skip to content

Commit

Permalink
Bug 1653335 - Add/fix constructors and deduction guides for Span. r=f…
Browse files Browse the repository at this point in the history
…roydnj

Differential Revision: https://phabricator.services.mozilla.com/D83813
  • Loading branch information
sigiesec committed Aug 7, 2020
1 parent dea5b54 commit 48dd75b
Show file tree
Hide file tree
Showing 3 changed files with 71 additions and 12 deletions.
4 changes: 4 additions & 0 deletions mfbt/Range.h
Original file line number Diff line number Diff line change
Expand Up @@ -68,11 +68,15 @@ template <class T>
Span<T> MakeSpan(Range<T>& aRange) {
return aRange;
}
template <typename T>
Span(Range<T>&) -> Span<T>;

template <class T>
Span<const T> MakeSpan(const Range<T>& aRange) {
return aRange;
}
template <typename T>
Span(const Range<T>&) -> Span<const T>;

} // namespace mozilla

Expand Down
72 changes: 60 additions & 12 deletions mfbt/Span.h
Original file line number Diff line number Diff line change
Expand Up @@ -438,10 +438,17 @@ class Span {
// a zero-terminated string. A Span<const char> or Span<const char16_t> can be
// obtained for const char* or const char16_t pointing to a zero-terminated
// string using the MakeStringSpan() function.
Span(char* aStr) = delete;
Span(const char* aStr) = delete;
Span(char16_t* aStr) = delete;
Span(const char16_t* aStr) = delete;
// (This must be a template because otherwise it will prevent the previous
// array constructor to match because an array decays to a pointer. This only
// exists to point to the above explanation, since there's no other
// constructor that would match.)
template <
typename T,
typename = std::enable_if_t<
std::is_pointer_v<T> &&
(std::is_same_v<std::remove_const_t<std::decay_t<T>>, char> ||
std::is_same_v<std::remove_const_t<std::decay_t<T>>, char16_t>)>>
Span(T& aStr) = delete;

/**
* Constructor for std::array.
Expand Down Expand Up @@ -490,13 +497,15 @@ class Span {
*/
template <
class Container,
class = std::enable_if_t<
!span_details::is_span<Container>::value &&
!span_details::is_std_array<Container>::value &&
std::is_convertible_v<typename Container::pointer, pointer> &&
std::is_convertible_v<typename Container::pointer,
decltype(std::declval<Container>().data())>>>
constexpr MOZ_IMPLICIT Span(Container& cont)
class Dummy = std::enable_if_t<
!std::is_const_v<Container> &&
!span_details::is_span<Container>::value &&
!span_details::is_std_array<Container>::value &&
std::is_convertible_v<typename Container::pointer, pointer> &&
std::is_convertible_v<typename Container::pointer,
decltype(std::declval<Container>().data())>,
Container>>
constexpr MOZ_IMPLICIT Span(Container& cont, Dummy* = nullptr)
: Span(cont.data(), ReleaseAssertedCast<index_type>(cont.size())) {}

/**
Expand All @@ -513,6 +522,39 @@ class Span {
constexpr MOZ_IMPLICIT Span(const Container& cont)
: Span(cont.data(), ReleaseAssertedCast<index_type>(cont.size())) {}

// NB: the SFINAE here uses .Elements() as a incomplete/imperfect proxy for
// the requirement on Container to be a contiguous sequence container.
/**
* Constructor for contiguous Mozilla containers.
*/
template <
class Container,
class = std::enable_if_t<
!std::is_const_v<Container> &&
!span_details::is_span<Container>::value &&
!span_details::is_std_array<Container>::value &&
std::is_convertible_v<typename Container::elem_type*, pointer> &&
std::is_convertible_v<
typename Container::elem_type*,
decltype(std::declval<Container>().Elements())>>>
constexpr MOZ_IMPLICIT Span(Container& cont, void* = nullptr)
: Span(cont.Elements(), ReleaseAssertedCast<index_type>(cont.Length())) {}

/**
* Constructor for contiguous Mozilla containers (const version).
*/
template <
class Container,
class = std::enable_if_t<
std::is_const_v<element_type> &&
!span_details::is_span<Container>::value &&
std::is_convertible_v<typename Container::elem_type*, pointer> &&
std::is_convertible_v<
typename Container::elem_type*,
decltype(std::declval<Container>().Elements())>>>
constexpr MOZ_IMPLICIT Span(const Container& cont, void* = nullptr)
: Span(cont.Elements(), ReleaseAssertedCast<index_type>(cont.Length())) {}

/**
* Constructor from other Span.
*/
Expand Down Expand Up @@ -780,14 +822,20 @@ Span(span_details::span_iterator<Span<T, OtherExtent>, IsConst> aBegin,
-> Span<std::conditional_t<IsConst, std::add_const_t<T>, T>>;

template <typename T, size_t Extent>
Span(T (&aArr)[Extent]) -> Span<T, Extent>;
Span(T (&)[Extent]) -> Span<T, Extent>;

template <class Container>
Span(Container&) -> Span<typename Container::value_type>;

template <class Container>
Span(const Container&) -> Span<const typename Container::value_type>;

template <typename T, size_t Extent>
Span(mozilla::Array<T, Extent>&) -> Span<T, Extent>;

template <typename T, size_t Extent>
Span(const mozilla::Array<T, Extent>&) -> Span<const T, Extent>;

// [Span.comparison], Span comparison operators
template <class ElementType, size_t FirstExtent, size_t SecondExtent>
inline constexpr bool operator==(const Span<ElementType, FirstExtent>& l,
Expand Down
7 changes: 7 additions & 0 deletions mfbt/tests/gtest/TestSpan.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,13 @@ static_assert(std::is_convertible_v<nsTArray<const int>, Span<const int>>,
static_assert(!std::is_convertible_v<nsTArray<const int>, Span<int>>,
"nsTArray should not drop const in conversion");

static_assert(std::is_convertible_v<const std::vector<int>, Span<const int>>,
"const std::vector should convert into const");
static_assert(std::is_convertible_v<std::vector<int>, Span<const int>>,
"std::vector should convert into const");
static_assert(!std::is_convertible_v<const std::vector<int>, Span<int>>,
"std::vector should not drop const in conversion");

/**
* Rust slice-compatible nullptr replacement value.
*/
Expand Down

0 comments on commit 48dd75b

Please sign in to comment.