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

[span] Make span standard_layout #877

Merged
merged 10 commits into from
Jun 24, 2020
104 changes: 57 additions & 47 deletions stl/inc/span
Original file line number Diff line number Diff line change
Expand Up @@ -226,29 +226,38 @@ struct pointer_traits<_Span_iterator<_Ty>> {
};

// STRUCT TEMPLATE _Span_extent_type
template <size_t _Extent>
template <class _Ty, size_t _Extent>
struct _Span_extent_type {
StephanTLavavej marked this conversation as resolved.
Show resolved Hide resolved
using pointer = _Ty*;

constexpr _Span_extent_type() noexcept = default;
StephanTLavavej marked this conversation as resolved.
Show resolved Hide resolved

constexpr explicit _Span_extent_type(size_t) noexcept {}
constexpr explicit _Span_extent_type(size_t, const pointer _Data) noexcept : _Mydata{_Data} {}
StephanTLavavej marked this conversation as resolved.
Show resolved Hide resolved

_NODISCARD constexpr size_t size() const noexcept {
return _Extent;
}

protected:
pointer _Mydata{nullptr};
};

template <>
struct _Span_extent_type<dynamic_extent> {
template <class _Ty>
struct _Span_extent_type<_Ty, dynamic_extent> {
using pointer = _Ty*;

constexpr _Span_extent_type() noexcept = default;

constexpr explicit _Span_extent_type(const size_t _Size) noexcept : _Mysize(_Size) {}
constexpr explicit _Span_extent_type(const size_t _Size, const pointer _Data) noexcept
: _Mysize(_Size), _Mydata{_Data} {}
miscco marked this conversation as resolved.
Show resolved Hide resolved

_NODISCARD constexpr size_t size() const noexcept {
return _Mysize;
}

private:
protected:
size_t _Mysize{0};
pointer _Mydata{nullptr};
miscco marked this conversation as resolved.
Show resolved Hide resolved
};

template <class _Ty, size_t _Size>
Expand Down Expand Up @@ -346,7 +355,7 @@ inline constexpr bool _Is_span_compatible_range = conjunction_v<
// [views.span]
// CLASS TEMPLATE span
template <class _Ty, size_t _Extent = dynamic_extent>
class span : public _Span_extent_type<_Extent> {
class span : public _Span_extent_type<_Ty, _Extent> {
miscco marked this conversation as resolved.
Show resolved Hide resolved
public:
using element_type = _Ty;
using value_type = remove_cv_t<_Ty>;
Expand All @@ -369,7 +378,7 @@ public:

template <_Span_compatible_iterator<element_type> _It>
constexpr explicit(_Extent != dynamic_extent) span(_It _First, size_type _Count) noexcept // strengthened
: _Mybase(_Count), _Mydata(_STD to_address(_Get_unwrapped_n(_First, _Count))) {
: _Mybase(_Count, _STD to_address(_Get_unwrapped_n(_First, _Count))) {
#if _CONTAINER_DEBUG_LEVEL > 0
if constexpr (_Extent != dynamic_extent) {
_STL_VERIFY(_Count == _Extent,
Expand All @@ -381,7 +390,7 @@ public:
template <_Span_compatible_iterator<element_type> _It, _Span_compatible_sentinel<_It> _Sentinel>
constexpr explicit(_Extent != dynamic_extent) span(_It _First, _Sentinel _Last)
noexcept(noexcept(_Last - _First)) // strengthened
: _Mybase(static_cast<size_type>(_Last - _First)), _Mydata(_STD to_address(_First)) {
: _Mybase(static_cast<size_type>(_Last - _First), _STD to_address(_First)) {
_Adl_verify_range(_First, _Last);
#if _CONTAINER_DEBUG_LEVEL > 0
if constexpr (_Extent != dynamic_extent) {
Expand All @@ -393,21 +402,21 @@ public:

template <size_t _Size>
requires (_Extent == dynamic_extent || _Extent == _Size)
constexpr span(type_identity_t<element_type> (&_Arr)[_Size]) noexcept : _Mybase(_Size), _Mydata(_Arr) {}
constexpr span(type_identity_t<element_type> (&_Arr)[_Size]) noexcept : _Mybase(_Size, _Arr) {}

template <class _OtherTy, size_t _Size>
requires (_Extent == dynamic_extent || _Extent == _Size)
&& is_convertible_v<_OtherTy (*)[], element_type (*)[]>
constexpr span(array<_OtherTy, _Size>& _Arr) noexcept : _Mybase(_Size), _Mydata(_Arr.data()) {}
constexpr span(array<_OtherTy, _Size>& _Arr) noexcept : _Mybase(_Size, _Arr.data()) {}

template <class _OtherTy, size_t _Size>
requires (_Extent == dynamic_extent || _Extent == _Size)
&& is_convertible_v<const _OtherTy (*)[], element_type (*)[]>
constexpr span(const array<_OtherTy, _Size>& _Arr) noexcept : _Mybase(_Size), _Mydata(_Arr.data()) {}
constexpr span(const array<_OtherTy, _Size>& _Arr) noexcept : _Mybase(_Size, _Arr.data()) {}

template <_Span_compatible_range<element_type> _Rng>
constexpr explicit(_Extent != dynamic_extent) span(_Rng&& _Range)
: _Mybase(static_cast<size_type>(_RANGES size(_Range))), _Mydata(_RANGES data(_Range)) {
: _Mybase(static_cast<size_type>(_RANGES size(_Range)), _RANGES data(_Range)) {
#if _CONTAINER_DEBUG_LEVEL > 0
if constexpr (_Extent != dynamic_extent) {
_STL_VERIFY(_RANGES size(_Range) == _Extent,
Expand All @@ -421,7 +430,7 @@ public:
&& is_convertible_v<_OtherTy (*)[], element_type (*)[]>
constexpr explicit(_Extent != dynamic_extent && _OtherExtent == dynamic_extent)
span(const span<_OtherTy, _OtherExtent>& _Other) noexcept
: _Mybase(_Other.size()), _Mydata(_Other.data()) {
: _Mybase(_Other.size(), _Other.data()) {
#if _CONTAINER_DEBUG_LEVEL > 0
if constexpr (_Extent != dynamic_extent) {
_STL_VERIFY(_Other.size() == _Extent,
Expand All @@ -437,7 +446,7 @@ public:
constexpr span() noexcept {}

constexpr explicit(_Extent != dynamic_extent) span(pointer _Ptr, size_type _Count) noexcept // strengthened
: _Mybase(_Count), _Mydata(_Ptr) {
: _Mybase(_Count, _Ptr) {
#if _CONTAINER_DEBUG_LEVEL > 0
if constexpr (_Extent != dynamic_extent) {
_STL_VERIFY(_Count == _Extent,
Expand All @@ -447,7 +456,7 @@ public:
}

constexpr explicit(_Extent != dynamic_extent) span(pointer _First, pointer _Last) noexcept // strengthened
: _Mybase(static_cast<size_type>(_Last - _First)), _Mydata(_First) {
: _Mybase(static_cast<size_type>(_Last - _First), _First) {
_Adl_verify_range(_First, _Last);
#if _CONTAINER_DEBUG_LEVEL > 0
if constexpr (_Extent != dynamic_extent) {
Expand All @@ -458,23 +467,23 @@ public:
}

template <size_t _Size, enable_if_t<_Extent == dynamic_extent || _Extent == _Size, int> = 0>
constexpr span(element_type (&_Arr)[_Size]) noexcept : _Mybase(_Size), _Mydata(_Arr) {}
constexpr span(element_type (&_Arr)[_Size]) noexcept : _Mybase(_Size, _Arr) {}

template <class _OtherTy, size_t _Size,
enable_if_t<conjunction_v<bool_constant<_Extent == dynamic_extent || _Extent == _Size>,
is_convertible<_OtherTy (*)[], element_type (*)[]>>,
int> = 0>
constexpr span(array<_OtherTy, _Size>& _Arr) noexcept : _Mybase(_Size), _Mydata(_Arr.data()) {}
constexpr span(array<_OtherTy, _Size>& _Arr) noexcept : _Mybase(_Size, _Arr.data()) {}

template <class _OtherTy, size_t _Size,
enable_if_t<conjunction_v<bool_constant<_Extent == dynamic_extent || _Extent == _Size>,
is_convertible<const _OtherTy (*)[], element_type (*)[]>>,
int> = 0>
constexpr span(const array<_OtherTy, _Size>& _Arr) noexcept : _Mybase(_Size), _Mydata(_Arr.data()) {}
constexpr span(const array<_OtherTy, _Size>& _Arr) noexcept : _Mybase(_Size, _Arr.data()) {}

template <class _Rng, enable_if_t<_Is_span_compatible_range<_Rng, element_type>, int> = 0>
constexpr explicit(_Extent != dynamic_extent) span(_Rng& _Range)
: _Mybase(static_cast<size_type>(_STD size(_Range))), _Mydata(_STD data(_Range)) {
: _Mybase(static_cast<size_type>(_STD size(_Range)), _STD data(_Range)) {
#if _CONTAINER_DEBUG_LEVEL > 0
if constexpr (_Extent != dynamic_extent) {
_STL_VERIFY(_STD size(_Range) == _Extent,
Expand All @@ -485,7 +494,7 @@ public:

template <class _Rng, enable_if_t<_Is_span_compatible_range<const _Rng, element_type>, int> = 0>
constexpr explicit(_Extent != dynamic_extent) span(const _Rng& _Range)
: _Mybase(static_cast<size_type>(_STD size(_Range))), _Mydata(_STD data(_Range)) {
: _Mybase(static_cast<size_type>(_STD size(_Range)), _STD data(_Range)) {
#if _CONTAINER_DEBUG_LEVEL > 0
if constexpr (_Extent != dynamic_extent) {
_STL_VERIFY(_STD size(_Range) == _Extent,
Expand All @@ -501,7 +510,7 @@ public:
int> = 0>
constexpr explicit(_Extent != dynamic_extent && _OtherExtent == dynamic_extent)
span(const span<_OtherTy, _OtherExtent>& _Other) noexcept
: _Mybase(_Other.size()), _Mydata(_Other.data()) {
: _Mybase(_Other.size(), _Other.data()) {
#if _CONTAINER_DEBUG_LEVEL > 0
if constexpr (_Extent != dynamic_extent) {
_STL_VERIFY(_Other.size() == _Extent,
Expand All @@ -520,7 +529,7 @@ public:
}
#if _CONTAINER_DEBUG_LEVEL > 0
else {
_STL_VERIFY(_Count <= this->size(), "Count out of range in span::first()");
_STL_VERIFY(_Count <= size(), "Count out of range in span::first()");
}
#endif // _CONTAINER_DEBUG_LEVEL > 0
return span<element_type, _Count>{_Mydata, _Count};
Expand All @@ -529,7 +538,7 @@ public:
_NODISCARD constexpr auto first(const size_type _Count) const noexcept
/* strengthened */ {
#if _CONTAINER_DEBUG_LEVEL > 0
_STL_VERIFY(_Count <= this->size(), "Count out of range in span::first(count)");
_STL_VERIFY(_Count <= size(), "Count out of range in span::first(count)");
#endif // _CONTAINER_DEBUG_LEVEL > 0
return span<element_type, dynamic_extent>{_Mydata, _Count};
}
Expand All @@ -541,17 +550,17 @@ public:
}
#if _CONTAINER_DEBUG_LEVEL > 0
else {
_STL_VERIFY(_Count <= this->size(), "Count out of range in span::last()");
_STL_VERIFY(_Count <= size(), "Count out of range in span::last()");
}
#endif // _CONTAINER_DEBUG_LEVEL > 0
return span<element_type, _Count>{_Mydata + (this->size() - _Count), _Count};
return span<element_type, _Count>{_Mydata + (size() - _Count), _Count};
}

_NODISCARD constexpr auto last(const size_type _Count) const noexcept /* strengthened */ {
#if _CONTAINER_DEBUG_LEVEL > 0
_STL_VERIFY(_Count <= this->size(), "Count out of range in span::last(count)");
_STL_VERIFY(_Count <= size(), "Count out of range in span::last(count)");
#endif // _CONTAINER_DEBUG_LEVEL > 0
return span<element_type, dynamic_extent>{_Mydata + (this->size() - _Count), _Count};
return span<element_type, dynamic_extent>{_Mydata + (size() - _Count), _Count};
}

template <size_t _Offset, size_t _Count = dynamic_extent>
Expand All @@ -563,62 +572,64 @@ public:
}
#if _CONTAINER_DEBUG_LEVEL > 0
else {
_STL_VERIFY(_Offset <= this->size(), "Offset out of range in span::subspan()");
_STL_VERIFY(_Offset <= size(), "Offset out of range in span::subspan()");

if constexpr (_Count != dynamic_extent) {
_STL_VERIFY(_Count <= this->size() - _Offset, "Count out of range in span::subspan()");
_STL_VERIFY(_Count <= size() - _Offset, "Count out of range in span::subspan()");
}
}
#endif // _CONTAINER_DEBUG_LEVEL > 0
using _ReturnType = span<element_type,
_Count != dynamic_extent ? _Count : (_Extent != dynamic_extent ? _Extent - _Offset : dynamic_extent)>;
return _ReturnType{_Mydata + _Offset, _Count == dynamic_extent ? this->size() - _Offset : _Count};
return _ReturnType{_Mydata + _Offset, _Count == dynamic_extent ? size() - _Offset : _Count};
}

_NODISCARD constexpr auto subspan(const size_type _Offset, const size_type _Count = dynamic_extent) const noexcept
/* strengthened */ {
#if _CONTAINER_DEBUG_LEVEL > 0
_STL_VERIFY(_Offset <= this->size(), "Offset out of range in span::subspan(offset, count)");
_STL_VERIFY(_Count == dynamic_extent || _Count <= this->size() - _Offset,
_STL_VERIFY(_Offset <= size(), "Offset out of range in span::subspan(offset, count)");
_STL_VERIFY(_Count == dynamic_extent || _Count <= size() - _Offset,
"Count out of range in span::subspan(offset, count)");
#endif // _CONTAINER_DEBUG_LEVEL > 0
using _ReturnType = span<element_type, dynamic_extent>;
return _ReturnType{_Mydata + _Offset, _Count == dynamic_extent ? this->size() - _Offset : _Count};
return _ReturnType{_Mydata + _Offset, _Count == dynamic_extent ? size() - _Offset : _Count};
}

// [span.obs] Observers
using _Span_extent_type<_Ty, _Extent>::size;
StephanTLavavej marked this conversation as resolved.
Show resolved Hide resolved

_NODISCARD constexpr size_type size_bytes() const noexcept {
#if _CONTAINER_DEBUG_LEVEL > 0
_STL_VERIFY(this->size() <= dynamic_extent / sizeof(element_type),
_STL_VERIFY(size() <= dynamic_extent / sizeof(element_type),
"size of span in bytes exceeds std::numeric_limits<size_t>::max()");
#endif // _CONTAINER_DEBUG_LEVEL > 0
return this->size() * sizeof(element_type);
return size() * sizeof(element_type);
}

_NODISCARD constexpr bool empty() const noexcept {
return this->size() == 0;
return size() == 0;
}

// [span.elem] Element access
_NODISCARD constexpr reference operator[](const size_type _Off) const noexcept /* strengthened */ {
#if _CONTAINER_DEBUG_LEVEL > 0
_STL_VERIFY(_Off < this->size(), "span index out of range");
_STL_VERIFY(_Off < size(), "span index out of range");
#endif // _CONTAINER_DEBUG_LEVEL > 0
return _Mydata[_Off];
}

_NODISCARD constexpr reference front() const noexcept /* strengthened */ {
#if _CONTAINER_DEBUG_LEVEL > 0
_STL_VERIFY(this->size() > 0, "front of empty span");
_STL_VERIFY(size() > 0, "front of empty span");
#endif // _CONTAINER_DEBUG_LEVEL > 0
return _Mydata[0];
}

_NODISCARD constexpr reference back() const noexcept /* strengthened */ {
#if _CONTAINER_DEBUG_LEVEL > 0
_STL_VERIFY(this->size() > 0, "back of empty span");
_STL_VERIFY(size() > 0, "back of empty span");
#endif // _CONTAINER_DEBUG_LEVEL > 0
return _Mydata[this->size() - 1];
return _Mydata[size() - 1];
}

_NODISCARD constexpr pointer data() const noexcept {
Expand All @@ -628,14 +639,14 @@ public:
// [span.iterators] Iterator support
_NODISCARD constexpr iterator begin() const noexcept {
#if _ITERATOR_DEBUG_LEVEL >= 1
return {_Mydata, _Mydata, _Mydata + this->size()};
return {_Mydata, _Mydata, _Mydata + size()};
#else // ^^^ _ITERATOR_DEBUG_LEVEL >= 1 ^^^ // vvv _ITERATOR_DEBUG_LEVEL == 0 vvv
return {_Mydata};
#endif // _ITERATOR_DEBUG_LEVEL
}

_NODISCARD constexpr iterator end() const noexcept {
const auto _End = _Mydata + this->size();
const auto _End = _Mydata + size();
StephanTLavavej marked this conversation as resolved.
Show resolved Hide resolved
#if _ITERATOR_DEBUG_LEVEL >= 1
return {_End, _Mydata, _End};
#else // ^^^ _ITERATOR_DEBUG_LEVEL >= 1 ^^^ // vvv _ITERATOR_DEBUG_LEVEL == 0 vvv
Expand All @@ -656,13 +667,12 @@ public:
}

_NODISCARD constexpr pointer _Unchecked_end() const noexcept {
return _Mydata + this->size();
return _Mydata + size();
}

private:
using _Mybase = _Span_extent_type<_Extent>;

pointer _Mydata{nullptr};
using _Mybase = _Span_extent_type<_Ty, _Extent>;
using _Mybase::_Mydata;
};

// DEDUCTION GUIDES
Expand Down
16 changes: 16 additions & 0 deletions tests/std/tests/P0122R7_span/test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,22 @@ static_assert(is_trivially_copyable_v<span<const int>::iterator>);
static_assert(is_trivially_copyable_v<span<const int, 3>>);
static_assert(is_trivially_copyable_v<span<const int, 3>::iterator>);

// For portability, our implementation provides an additional guarantee beyond the Standard
// that span has standard layout
CaseyCarter marked this conversation as resolved.
Show resolved Hide resolved
static_assert(is_standard_layout_v<span<int>>);
static_assert(is_standard_layout_v<span<int>::iterator>);
static_assert(is_standard_layout_v<span<int, 3>>);
static_assert(is_standard_layout_v<span<int, 3>::iterator>);
static_assert(is_standard_layout_v<span<const int>>);
static_assert(is_standard_layout_v<span<const int>::iterator>);
static_assert(is_standard_layout_v<span<const int, 3>>);
static_assert(is_standard_layout_v<span<const int, 3>::iterator>);

// For performance, our implementation provides an additional guarantee beyond the Standard
// that fixed size span has only the size of a pointer.
StephanTLavavej marked this conversation as resolved.
Show resolved Hide resolved
static_assert(sizeof(span<int, 4>) == sizeof(void*));
static_assert(sizeof(span<int>) == sizeof(void*) + sizeof(size_t));

struct Base {};
struct Derived : Base {};
static_assert(is_convertible_v<Derived*, Base*>);
Expand Down