Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

gsl::span P1976R2 implemenation #887

Merged
merged 9 commits into from
May 21, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
116 changes: 82 additions & 34 deletions include/gsl/span
Original file line number Diff line number Diff line change
Expand Up @@ -343,15 +343,9 @@ namespace details

constexpr extent_type() noexcept = default;

template <size_type Other>
constexpr extent_type(extent_type<Other> ext)
{
static_assert(Other == Ext,
"Mismatch between fixed-size extent and size of initializing data.");
Expects(ext.size() == Ext);
}
constexpr explicit extent_type(extent_type<dynamic_extent>);

constexpr extent_type(size_type size) { Expects(size == Ext); }
constexpr explicit extent_type(size_type size) { Expects(size == Ext); }

constexpr size_type size() const noexcept { return Ext; }
};
Expand All @@ -363,10 +357,10 @@ namespace details
using size_type = std::size_t;

template <size_type Other>
explicit constexpr extent_type(extent_type<Other> ext) : size_(ext.size())
constexpr explicit extent_type(extent_type<Other> ext) : size_(ext.size())
{}

explicit constexpr extent_type(size_type size) : size_(size)
constexpr explicit extent_type(size_type size) : size_(size)
{
Expects(size != dynamic_extent);
}
Expand All @@ -377,6 +371,12 @@ namespace details
size_type size_;
};

template <std::size_t Ext>
constexpr extent_type<Ext>::extent_type(extent_type<dynamic_extent> ext)
{
Expects(ext.size() == Ext);
}

Comment on lines +374 to +379
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm unsure whether this actually needs to be an out-of-line definition. My understanding is that member function bodies aren't instantiated until needed (this is what lets list<T>::sort() require op< from T only if sort() is actually called). So you should be able to define this within the class definition, without fear of prematurely instantiating the primary template for extent_type<dynamic_extent> before the explicit specialization has been seen. But if you're especially concerned about that, I think I would recommend declaring the primary template and then declaring the explicit specialization, so you can definitely define this member function within the class definition.

template <class ElementType, std::size_t Extent, std::size_t Offset, std::size_t Count>
struct calculate_subspan_type
{
Expand Down Expand Up @@ -420,18 +420,28 @@ public:
constexpr span() noexcept : storage_(nullptr, details::extent_type<0>())
{}

constexpr span(pointer ptr, size_type count) noexcept : storage_(ptr, count)
template <std::size_t MyExtent = Extent, std::enable_if_t<MyExtent != gsl::dynamic_extent, int> = 0>
constexpr explicit span(pointer ptr, size_type count) noexcept : storage_(ptr, count)
{
if (Extent != dynamic_extent) Expects(count == Extent);
Expects(count == Extent);
}

constexpr span(pointer firstElem, pointer lastElem) noexcept
template <std::size_t MyExtent = Extent, std::enable_if_t<MyExtent == gsl::dynamic_extent, int> = 0>
constexpr span(pointer ptr, size_type count) noexcept : storage_(ptr, count)
{}

template <std::size_t MyExtent = Extent, std::enable_if_t<MyExtent != gsl::dynamic_extent, int> = 0>
constexpr explicit span(pointer firstElem, pointer lastElem) noexcept
: storage_(firstElem, static_cast<std::size_t>(lastElem - firstElem))
{
if (Extent != dynamic_extent)
{ Expects(lastElem - firstElem == static_cast<difference_type>(Extent)); }
Expects(lastElem - firstElem == static_cast<difference_type>(Extent));
}

template <std::size_t MyExtent = Extent, std::enable_if_t<MyExtent == gsl::dynamic_extent, int> = 0>
constexpr span(pointer firstElem, pointer lastElem) noexcept
: storage_(firstElem, static_cast<std::size_t>(lastElem - firstElem))
{}

template <std::size_t N,
std::enable_if_t<details::is_allowed_extent_conversion<N, Extent>::value, int> = 0>
constexpr span(element_type (&arr)[N]) noexcept
Expand All @@ -456,40 +466,74 @@ public:
: storage_(KnownNotNull{arr.data()}, details::extent_type<N>())
{}

// NB: the SFINAE here uses .data() as an incomplete/imperfect proxy for the requirement
// on Container to be a contiguous sequence container.
template <class Container,
class = std::enable_if_t<
// NB: the SFINAE on these constructors uses .data() as an incomplete/imperfect proxy for the
// requirement on Container to be a contiguous sequence container.
template <std::size_t MyExtent = Extent, class Container,
std::enable_if_t<
MyExtent != dynamic_extent &&
!details::is_span<Container>::value && !details::is_std_array<Container>::value &&
std::is_pointer<decltype(std::declval<Container&>().data())>::value &&
std::is_convertible<
std::remove_pointer_t<decltype(std::declval<Container&>().data())> (*)[],
element_type (*)[]>::value>>
element_type (*)[]>::value, int> = 0>
constexpr explicit span(Container& cont) noexcept : span(cont.data(), cont.size())
{}

template <std::size_t MyExtent = Extent, class Container,
std::enable_if_t<
MyExtent == dynamic_extent &&
!details::is_span<Container>::value && !details::is_std_array<Container>::value &&
std::is_pointer<decltype(std::declval<Container&>().data())>::value &&
std::is_convertible<
std::remove_pointer_t<decltype(std::declval<Container&>().data())> (*)[],
element_type (*)[]>::value, int> = 0>
constexpr span(Container& cont) noexcept : span(cont.data(), cont.size())
{}

template <class Container,
class = std::enable_if_t<
template <std::size_t MyExtent = Extent, class Container,
std::enable_if_t<
MyExtent != dynamic_extent &&
std::is_const<element_type>::value && !details::is_span<Container>::value &&
!details::is_std_array<Container>::value &&
std::is_pointer<decltype(std::declval<const Container&>().data())>::value &&
std::is_convertible<std::remove_pointer_t<
decltype(std::declval<const Container&>().data())> (*)[],
element_type (*)[]>::value>>
decltype(std::declval<const Container&>().data())> (*)[],
element_type (*)[]>::value, int> = 0>
constexpr explicit span(const Container& cont) noexcept : span(cont.data(), cont.size())
{}

template <std::size_t MyExtent = Extent, class Container,
std::enable_if_t<
MyExtent == dynamic_extent &&
std::is_const<element_type>::value && !details::is_span<Container>::value &&
!details::is_std_array<Container>::value &&
std::is_pointer<decltype(std::declval<const Container&>().data())>::value &&
std::is_convertible<std::remove_pointer_t<
decltype(std::declval<const Container&>().data())> (*)[],
element_type (*)[]>::value, int> = 0>
constexpr span(const Container& cont) noexcept : span(cont.data(), cont.size())
{}

constexpr span(const span& other) noexcept = default;

template <
class OtherElementType, std::size_t OtherExtent,
class = std::enable_if_t<
details::is_allowed_extent_conversion<OtherExtent, Extent>::value &&
details::is_allowed_element_type_conversion<OtherElementType, element_type>::value>>
class OtherElementType, std::size_t OtherExtent, std::size_t MyExtent = Extent,
std::enable_if_t<
(MyExtent == dynamic_extent || MyExtent == OtherExtent) &&
details::is_allowed_element_type_conversion<OtherElementType, element_type>::value, int> = 0>
constexpr span(const span<OtherElementType, OtherExtent>& other) noexcept
: storage_(other.data(), details::extent_type<OtherExtent>(other.size()))
{}

template <
class OtherElementType, std::size_t OtherExtent, std::size_t MyExtent = Extent,
std::enable_if_t<
MyExtent != dynamic_extent && OtherExtent == dynamic_extent &&
details::is_allowed_element_type_conversion<OtherElementType, element_type>::value, int> = 0>
constexpr explicit span(const span<OtherElementType, OtherExtent>& other) noexcept
: storage_(other.data(), details::extent_type<OtherExtent>(other.size()))
{}

~span() noexcept = default;
constexpr span& operator=(const span& other) noexcept = default;

Expand All @@ -498,7 +542,7 @@ public:
constexpr span<element_type, Count> first() const noexcept
{
Expects(Count <= size());
return {data(), Count};
return span<element_type, Count>{data(), Count};
}

template <std::size_t Count>
Expand All @@ -508,7 +552,7 @@ public:
constexpr span<element_type, Count> last() const noexcept
{
Expects(Count <= size());
return {data() + (size() - Count), Count};
return span<element_type, Count>{data() + (size() - Count), Count};
}

template <std::size_t Offset, std::size_t Count = dynamic_extent>
Expand All @@ -519,8 +563,8 @@ public:
typename details::calculate_subspan_type<ElementType, Extent, Offset, Count>::type
{
Expects((size() >= Offset) && (Count == dynamic_extent || (Count <= size() - Offset)));

return {data() + Offset, Count == dynamic_extent ? size() - Offset : Count};
using type = typename details::calculate_subspan_type<ElementType, Extent, Offset, Count>::type;
return type{data() + Offset, Count == dynamic_extent ? size() - Offset : Count};
}

constexpr span<element_type, dynamic_extent> first(size_type count) const noexcept
Expand Down Expand Up @@ -728,21 +772,25 @@ template <class ElementType, std::size_t Extent>
span<const byte, details::calculate_byte_size<ElementType, Extent>::value>
as_bytes(span<ElementType, Extent> s) noexcept
{
using type = span<const byte, details::calculate_byte_size<ElementType, Extent>::value>;

// clang-format off
GSL_SUPPRESS(type.1) // NO-FORMAT: attribute
// clang-format on
return {reinterpret_cast<const byte*>(s.data()), s.size_bytes()};
return type{reinterpret_cast<const byte*>(s.data()), s.size_bytes()};
}

template <class ElementType, std::size_t Extent,
std::enable_if_t<!std::is_const<ElementType>::value, int> = 0>
span<byte, details::calculate_byte_size<ElementType, Extent>::value>
as_writable_bytes(span<ElementType, Extent> s) noexcept
{
using type = span<byte, details::calculate_byte_size<ElementType, Extent>::value>;

// clang-format off
GSL_SUPPRESS(type.1) // NO-FORMAT: attribute
// clang-format on
return {reinterpret_cast<byte*>(s.data()), s.size_bytes()};
return type{reinterpret_cast<byte*>(s.data()), s.size_bytes()};
}

} // namespace gsl
Expand Down
2 changes: 1 addition & 1 deletion include/gsl/string_span
Original file line number Diff line number Diff line change
Expand Up @@ -308,7 +308,7 @@ public:
private:
static impl_type remove_z(pointer const& sz, std::size_t max)
{
return {sz, details::string_length(sz, max)};
return impl_type(sz, details::string_length(sz, max));
}

template <std::size_t N>
Expand Down
14 changes: 8 additions & 6 deletions tests/span_compatibility_tests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -846,8 +846,6 @@ static_assert(!std::is_constructible<gsl::span<int, 3>, std::array<int, 500>&>::
"!std::is_constructible<gsl::span<int, 3>, std::array<int, 500>&>");
static_assert(!std::is_constructible<gsl::span<int, 3>, const std::array<int, 3>&>::value,
"!std::is_constructible<gsl::span<int, 3>, const std::array<int, 3>&>");
static_assert(!std::is_constructible<gsl::span<int, 3>, const gsl::span<int>&>::value,
"!std::is_constructible<gsl::span<int, 3>, const gsl::span<int>&>");
static_assert(!std::is_constructible<gsl::span<int, 3>, const gsl::span<int, 500>&>::value,
"!std::is_constructible<gsl::span<int, 3>, const gsl::span<int, 500>&>");
static_assert(!std::is_constructible<gsl::span<int, 3>, const gsl::span<const int>&>::value,
Expand All @@ -866,12 +864,8 @@ static_assert(!std::is_constructible<gsl::span<const int>, std::array<double, 3>
static_assert(!std::is_constructible<gsl::span<const int>, const gsl::span<double, 3>&>::value,
"!std::is_constructible<gsl::span<const int>, const gsl::span<double, 3>&>");

static_assert(!std::is_constructible<gsl::span<const int, 3>, const gsl::span<int>&>::value,
"!std::is_constructible<gsl::span<const int, 3>, const gsl::span<int>&>");
static_assert(!std::is_constructible<gsl::span<const int, 3>, const gsl::span<int, 500>&>::value,
"!std::is_constructible<gsl::span<const int, 3>, const gsl::span<int, 500>&>");
static_assert(!std::is_constructible<gsl::span<const int, 3>, const gsl::span<const int>&>::value,
"!std::is_constructible<gsl::span<const int, 3>, const gsl::span<const int>&>");
static_assert(
!std::is_constructible<gsl::span<const int, 3>, const gsl::span<const int, 500>&>::value,
"!std::is_constructible<gsl::span<const int, 3>, const gsl::span<const int, 500>&>");
Expand Down Expand Up @@ -925,6 +919,14 @@ static_assert(!std::is_constructible<gsl::span<const Derived>, std::array<Base,
static_assert(!std::is_constructible<gsl::span<const Derived>, const std::array<Base, 3>&>::value,
"!std::is_constructible<gsl::span<const Derived>, const std::array<Base, 3>&>");

// Explicit construction enabled in P1976R2
static_assert(std::is_constructible<gsl::span<int, 3>, const gsl::span<int>&>::value,
"std::is_constructible<gsl::span<int, 3>, const gsl::span<int>&>");
static_assert(std::is_constructible<gsl::span<const int, 3>, const gsl::span<int>&>::value,
"std::is_constructible<gsl::span<const int, 3>, const gsl::span<int>&>");
static_assert(std::is_constructible<gsl::span<const int, 3>, const gsl::span<const int>&>::value,
"std::is_constructible<gsl::span<const int, 3>, const gsl::span<const int>&>");

// no throw copy constructor
static_assert(std::is_nothrow_copy_constructible<gsl::span<int>>::value,
"std::is_nothrow_copy_constructible<gsl::span<int>>");
Expand Down
4 changes: 2 additions & 2 deletions tests/span_tests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1144,7 +1144,7 @@ TEST(span_test, from_array_constructor)

// you can convert statically
{
const span<int, 2> s2 = {&arr[0], 2};
const span<int, 2> s2{&arr[0], 2};
static_cast<void>(s2);
}
{
Expand Down Expand Up @@ -1180,7 +1180,7 @@ TEST(span_test, from_array_constructor)
#endif
{
auto f = [&]() {
const span<int, 4> _s4 = {arr2, 2};
const span<int, 4> _s4{arr2, 2};
static_cast<void>(_s4);
};
EXPECT_DEATH(f(), deathstring);
Expand Down