From f4745a73411c468f5e26867a110bec9bb74d5683 Mon Sep 17 00:00:00 2001 From: Michael Schellenberger Costa Date: Tue, 19 Nov 2019 23:00:06 +0100 Subject: [PATCH 01/11] [span] This implements the contents of as defined in N4832 --- stl/CMakeLists.txt | 1 + stl/inc/__msvc_all_public_headers.hpp | 1 + stl/inc/span | 743 ++++++++++++++++++++++++++ stl/inc/yvals_core.h | 7 +- 4 files changed, 751 insertions(+), 1 deletion(-) create mode 100644 stl/inc/span diff --git a/stl/CMakeLists.txt b/stl/CMakeLists.txt index 2ac71037b32..5092acfdbfc 100644 --- a/stl/CMakeLists.txt +++ b/stl/CMakeLists.txt @@ -171,6 +171,7 @@ set(HEADERS ${CMAKE_CURRENT_LIST_DIR}/inc/scoped_allocator ${CMAKE_CURRENT_LIST_DIR}/inc/set ${CMAKE_CURRENT_LIST_DIR}/inc/shared_mutex + ${CMAKE_CURRENT_LIST_DIR}/inc/span ${CMAKE_CURRENT_LIST_DIR}/inc/sstream ${CMAKE_CURRENT_LIST_DIR}/inc/stack ${CMAKE_CURRENT_LIST_DIR}/inc/stdexcept diff --git a/stl/inc/__msvc_all_public_headers.hpp b/stl/inc/__msvc_all_public_headers.hpp index 17767871349..d06a531594a 100644 --- a/stl/inc/__msvc_all_public_headers.hpp +++ b/stl/inc/__msvc_all_public_headers.hpp @@ -81,6 +81,7 @@ #include #include #include +#include #include #include #include diff --git a/stl/inc/span b/stl/inc/span new file mode 100644 index 00000000000..f628310bd17 --- /dev/null +++ b/stl/inc/span @@ -0,0 +1,743 @@ +// span standard header + +// Copyright (c) Microsoft Corporation. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +#pragma once +#ifndef _SPAN_ +#define _SPAN_ +#include +#if _STL_COMPILER_PREPROCESSOR + +#if !_HAS_CXX20 +#pragma message("The contents of are available only with C++20 or later.") +#else // ^^^ !_HAS_CXX20 / _HAS_CXX20 vvv +#include +#include +#include + +#pragma pack(push, _CRT_PACKING) +#pragma warning(push, _STL_WARNING_LEVEL) +#pragma warning(disable : _STL_DISABLED_WARNINGS) +_STL_DISABLE_CLANG_WARNINGS +#pragma push_macro("new") +#undef new + +_STD_BEGIN + +inline constexpr size_t dynamic_extent = static_cast(-1); + +// STRUCT TEMPLATE _Span_iterator +template +struct _Span_iterator { +#ifdef __cpp_lib_concepts + using iterator_concept = contiguous_iterator_tag; +#endif // __cpp_lib_concepts + using iterator_category = random_access_iterator_tag; + using value_type = remove_cv_t<_Ty>; + using difference_type = ptrdiff_t; + using pointer = _Ty*; + using reference = _Ty&; + + _NODISCARD constexpr operator _Span_iterator() const noexcept { +#if _ITERATOR_DEBUG_LEVEL >= 1 + return {_Myptr, _Mybegin, _Myend}; +#else // ^^^ _ITERATOR_DEBUG_LEVEL >= 1 ^^^ // vvv _ITERATOR_DEBUG_LEVEL == 0 vvv + return {_Myptr}; +#endif // _ITERATOR_DEBUG_LEVEL + } + + _NODISCARD constexpr reference operator*() const noexcept { +#if _ITERATOR_DEBUG_LEVEL >= 1 + _STL_VERIFY(_Mybegin, "cannot dereference value-initialized span iterator"); + _STL_VERIFY(_Myptr < _Myend, "cannot dereference end span iterator"); +#endif // _ITERATOR_DEBUG_LEVEL >= 1 + return *_Myptr; + } + + _NODISCARD constexpr pointer operator->() const noexcept { +#if _ITERATOR_DEBUG_LEVEL >= 1 + _STL_VERIFY(_Mybegin, "cannot dereference value-initialized span iterator"); + _STL_VERIFY(_Myptr < _Myend, "cannot dereference end span iterator"); +#endif // _ITERATOR_DEBUG_LEVEL >= 1 + return _Myptr; + } + + constexpr _Span_iterator& operator++() noexcept { +#if _ITERATOR_DEBUG_LEVEL >= 1 + _STL_VERIFY(_Mybegin, "cannot increment value-initialized span iterator"); + _STL_VERIFY(_Myptr < _Myend, "cannot increment span iterator past end"); +#endif // _ITERATOR_DEBUG_LEVEL >= 1 + ++_Myptr; + return *this; + } + + constexpr _Span_iterator operator++(int) noexcept { + _Span_iterator _Tmp{*this}; + ++*this; + return _Tmp; + } + + constexpr _Span_iterator& operator--() noexcept { +#if _ITERATOR_DEBUG_LEVEL >= 1 + _STL_VERIFY(_Mybegin, "cannot decrement value-initialized span iterator"); + _STL_VERIFY(_Mybegin < _Myptr, "cannot decrement span iterator before begin"); +#endif // _ITERATOR_DEBUG_LEVEL >= 1 + --_Myptr; + return *this; + } + + constexpr _Span_iterator operator--(int) noexcept { + _Span_iterator _Tmp{*this}; + --*this; + return _Tmp; + } + + constexpr void _Verify_offset([[maybe_unused]] const difference_type _Off) const noexcept { +#if _ITERATOR_DEBUG_LEVEL >= 1 + if (_Off != 0) { + _STL_VERIFY(_Mybegin, "cannot seek value-initialized span iterator"); + } + + if (_Off < 0) { + _STL_VERIFY(_Myptr - _Mybegin >= -_Off, "cannot seek span iterator before begin"); + } + + if (_Off > 0) { + _STL_VERIFY(_Myend - _Myptr >= _Off, "cannot seek span iterator after end"); + } +#endif // _ITERATOR_DEBUG_LEVEL >= 1 + } + + constexpr _Span_iterator& operator+=(const difference_type _Off) noexcept { + _Verify_offset(_Off); + _Myptr += _Off; + return *this; + } + + _NODISCARD constexpr _Span_iterator operator+(const difference_type _Off) const noexcept { + _Span_iterator _Tmp{*this}; + _Tmp += _Off; + return _Tmp; + } + + constexpr _Span_iterator& operator-=(const difference_type _Off) noexcept { + _Verify_offset(-_Off); + _Myptr -= _Off; + return *this; + } + + _NODISCARD constexpr _Span_iterator operator-(const difference_type _Off) const noexcept { + _Span_iterator _Tmp{*this}; + _Tmp -= _Off; + return _Tmp; + } + + _NODISCARD constexpr difference_type operator-(const _Span_iterator& _Right) const noexcept { +#if _ITERATOR_DEBUG_LEVEL >= 1 + _STL_VERIFY( + _Mybegin == _Right._Mybegin && _Myend == _Right._Myend, "cannot subtract incompatible span iterators"); +#endif // _ITERATOR_DEBUG_LEVEL >= 1 + return _Myptr - _Right._Myptr; + } + + _NODISCARD constexpr reference operator[](const difference_type _Off) const noexcept { + return *(*this + _Off); + } + + _NODISCARD constexpr bool operator==(const _Span_iterator& _Right) const noexcept { +#if _ITERATOR_DEBUG_LEVEL >= 1 + _STL_VERIFY(_Mybegin == _Right._Mybegin && _Myend == _Right._Myend, + "cannot compare incompatible span iterators for equality"); +#endif // _ITERATOR_DEBUG_LEVEL >= 1 + return _Myptr == _Right._Myptr; + } + + _NODISCARD constexpr bool operator!=(const _Span_iterator& _Right) const noexcept { + return !(*this == _Right); + } + + _NODISCARD constexpr bool operator<(const _Span_iterator& _Right) const noexcept { +#if _ITERATOR_DEBUG_LEVEL >= 1 + _STL_VERIFY( + _Mybegin == _Right._Mybegin && _Myend == _Right._Myend, "cannot compare incompatible span iterators"); +#endif // _ITERATOR_DEBUG_LEVEL >= 1 + return _Myptr < _Right._Myptr; + } + + _NODISCARD constexpr bool operator>(const _Span_iterator& _Right) const noexcept { + return _Right < *this; + } + + _NODISCARD constexpr bool operator<=(const _Span_iterator& _Right) const noexcept { + return !(_Right < *this); + } + + _NODISCARD constexpr bool operator>=(const _Span_iterator& _Right) const noexcept { + return !(*this < _Right); + } + +#if _ITERATOR_DEBUG_LEVEL >= 1 + friend constexpr void _Verify_range(const _Span_iterator& _First, const _Span_iterator& _Last) noexcept { + _STL_VERIFY(_First._Mybegin == _Last._Mybegin && _First._Myend == _Last._Myend, + "span iterators from different views do not form a range"); + _STL_VERIFY(_First._Myptr <= _Last._Myptr, "span iterator range transposed"); + } +#endif // _ITERATOR_DEBUG_LEVEL >= 1 + + using _Prevent_inheriting_unwrap = _Span_iterator; + + _NODISCARD constexpr pointer _Unwrapped() const noexcept { + return _Myptr; + } + + static constexpr bool _Unwrap_when_unverified = _ITERATOR_DEBUG_LEVEL == 0; + + constexpr void _Seek_to(const pointer _It) noexcept { + _Myptr = _It; + } + + pointer _Myptr = nullptr; +#if _ITERATOR_DEBUG_LEVEL >= 1 + pointer _Mybegin = nullptr; + pointer _Myend = nullptr; +#endif // _ITERATOR_DEBUG_LEVEL >= 1 +}; + +template +_NODISCARD _Span_iterator<_Ty> operator+(const ptrdiff_t _Off, _Span_iterator<_Ty> _Next) noexcept { + return _Next += _Off; +} + +template +struct pointer_traits<_Span_iterator<_Ty>> { + using pointer = _Span_iterator<_Ty>; + using element_type = _Ty; + using difference_type = ptrdiff_t; + + _NODISCARD static constexpr element_type* to_address(const pointer _Iter) noexcept { + return _Iter._Unwrapped(); + } +}; + +// STRUCT TEMPLATE _Span_extent_type +template +struct _Span_extent_type { + constexpr _Span_extent_type() noexcept = default; + + constexpr explicit _Span_extent_type([[maybe_unused]] const size_t _Size) noexcept { +#if _CONTAINER_DEBUG_LEVEL > 0 + _STL_VERIFY(_Size == _Extent, "Mismatch between static extent and size of initializing data"); +#endif // _CONTAINER_DEBUG_LEVEL > 0 + } + + template + constexpr explicit _Span_extent_type(const _Span_extent_type<_OtherExtent> _Ext) noexcept { + static_assert(_OtherExtent == _Extent || _OtherExtent == dynamic_extent, + "Mismatch between static extent and size of initializing data"); +#if _CONTAINER_DEBUG_LEVEL > 0 + if constexpr (_OtherExtent == dynamic_extent) { + _STL_VERIFY(_Ext.size() == _Extent, "Mismatch between static extent and size of initializing data"); + } +#endif // _CONTAINER_DEBUG_LEVEL > 0 + } + + _NODISCARD constexpr size_t size() const noexcept { + return _Extent; + } +}; + +template <> +struct _Span_extent_type { + constexpr _Span_extent_type() noexcept = default; + + constexpr explicit _Span_extent_type(const size_t _Size) noexcept : _Mysize(_Size) {} + + template + constexpr explicit _Span_extent_type(const _Span_extent_type<_OtherExtent> _Ext) noexcept : _Mysize(_Ext.size()) {} + + _NODISCARD constexpr size_t size() const noexcept { + return _Mysize; + } + +private: + size_t _Mysize{0}; +}; + +template +class array; + +template +class span; + +#ifdef __cpp_lib_concepts +namespace ranges { + template + inline constexpr bool enable_safe_range> = true; +} // namespace ranges + +// VARIABLE TEMPLATE _Is_span_v +template +inline constexpr bool _Is_span_v = false; + +template +inline constexpr bool _Is_span_v> = true; + +// VARIABLE TEMPLATE _Is_std_array_v +template +inline constexpr bool _Is_std_array_v = false; + +template +inline constexpr bool _Is_std_array_v> = true; + +// clang-format off +template +concept _Is_span_compatible_iterator = contiguous_iterator<_It> + && is_convertible_v>(*)[], _Ty(*)[]>; + +template +concept _Is_span_compatible_sentinel = sized_sentinel_for<_Sentinel, _It> + && !is_convertible_v<_Sentinel, size_t>; + +template +concept _Is_span_compatible_range = + !is_array_v> + && !_Is_span_v> + && !_Is_std_array_v> + && _RANGES contiguous_range<_Rng> + && _RANGES sized_range<_Rng> + && (_RANGES safe_range<_Rng> || is_const_v<_Ty>) + && is_convertible_v>(*)[], _Ty(*)[]>; +// clang-format on +#else // ^^^ __cpp_lib_concepts / !__cpp_lib_concepts vvv + +// STRUCT TEMPLATE _Is_span +template +struct _Is_span : false_type {}; + +template +struct _Is_span> : true_type {}; + +// STRUCT TEMPLATE _Is_std_array +template +struct _Is_std_array : false_type {}; + +template +struct _Is_std_array> : true_type {}; + +// STRUCT TEMPLATE _Is_span_convertible_range +template +struct _Is_span_convertible_range + : bool_constant()))> (*)[], _Ty (*)[]>> {}; + +// STRUCT TEMPLATE _Has_container_interface +template +struct _Has_container_interface : false_type {}; + +template +struct _Has_container_interface<_Rng, + void_t())), decltype(_STD size(_STD declval<_Rng&>()))>> : true_type {}; + +// STRUCT TEMPLATE _Is_span_compatible_range +// clang-format off +template +struct _Is_span_compatible_range : bool_constant>, + negation<_Is_span>>, + negation<_Is_std_array>>, + _Has_container_interface<_Rng>, + _Is_span_convertible_range<_Rng, _Ty>>> {}; +// clang-format on + +#endif // !__cpp_lib_concepts + +// [views.span] +// CLASS TEMPLATE span +template +class span : public _Span_extent_type<_Extent> { +public: + using element_type = _Ty; + using value_type = remove_cv_t<_Ty>; + using size_type = size_t; + using difference_type = ptrdiff_t; + using pointer = _Ty*; + using const_pointer = const _Ty*; + using reference = _Ty&; + using const_reference = const _Ty&; + using iterator = _Span_iterator<_Ty>; + using const_iterator = _Span_iterator; + using reverse_iterator = _STD reverse_iterator; + using const_reverse_iterator = _STD reverse_iterator; + + static constexpr size_type extent = _Extent; + + // [span.cons] Constructors, copy, and assignment +#ifdef __cpp_lib_concepts + + // clang-format off + constexpr span() noexcept requires (_Extent == 0 || _Extent == dynamic_extent) = default; + + template <_Is_span_compatible_iterator _It> + constexpr span(_It _First, size_type _Count) noexcept // strengthened + : _Mybase(_Count), _Mydata(_STD to_address(_First)) { +#if _CONTAINER_DEBUG_LEVEL > 0 + if constexpr (_Extent != dynamic_extent) { + _STL_VERIFY(_Count == _Extent, "span construction from invalid range as count != extent"); + } +#endif // _CONTAINER_DEBUG_LEVEL > 0 + } + + template <_Is_span_compatible_iterator _It, _Is_span_compatible_sentinel<_It> _Sentinel> + constexpr span(_It _First, _Sentinel _Last) noexcept(noexcept(_Last - _First)) // strengthened + : _Mybase(static_cast(_Last - _First)), _Mydata(_STD to_address(_First)) { + _Adl_verify_range(_First, _Last); +#if _CONTAINER_DEBUG_LEVEL > 0 + if constexpr (_Extent != dynamic_extent) { + _STL_VERIFY(_Last - _First == _Extent, "span construction from invalid range as last - first != extent"); + } +#endif // _CONTAINER_DEBUG_LEVEL > 0 + } + + template + requires (_Extent == dynamic_extent || _Extent == _Size) + constexpr span(element_type (&_Arr)[_Size]) noexcept : _Mybase(_Size), _Mydata(_Arr) {} + + template + requires (_Extent == dynamic_extent || _Extent == _Size) + constexpr span(array& _Arr) noexcept : _Mybase(_Size), _Mydata(_Arr.data()) {} + + template + requires (_Extent == dynamic_extent || _Extent == _Size) && is_convertible_v + constexpr span(const array& _Arr) noexcept : _Mybase(_Size), _Mydata(_Arr.data()) {} + + template + requires (_Extent == dynamic_extent) && _Is_span_compatible_range<_Rng, element_type> + constexpr span(_Rng&& _Range) + : _Mybase(static_cast(_RANGES size(_Range))), _Mydata(_RANGES data(_Range)) {} + + template + requires (_Extent == dynamic_extent || _Extent == _OtherExtent) + && is_convertible_v<_OtherTy (*)[], element_type (*)[]> + constexpr span(const span<_OtherTy, _OtherExtent>& _Other) noexcept + : _Mybase(_Other.size()), _Mydata(_Other.data()) {} + // clang-format on + +#else // ^^^ __cpp_lib_concepts / !__cpp_lib_concepts vvv + + template = 0> + constexpr span() noexcept {} + + constexpr span(pointer _Ptr, size_type _Count) noexcept // strengthened + : _Mybase(_Count), _Mydata(_Ptr) { +#if _CONTAINER_DEBUG_LEVEL > 0 + if constexpr (_Extent != dynamic_extent) { + _STL_VERIFY(_Count == _Extent, "span construction from invalid range as count != extent"); + } +#endif // _CONTAINER_DEBUG_LEVEL > 0 + } + + constexpr span(pointer _First, pointer _Last) noexcept // strengthened + : _Mybase(static_cast(_Last - _First)), _Mydata(_First) { + _Adl_verify_range(_First, _Last); +#if _CONTAINER_DEBUG_LEVEL > 0 + if constexpr (_Extent != dynamic_extent) { + _STL_VERIFY(_Last - _First == _Extent, "span construction from invalid range as last - first != extent"); + } +#endif // _CONTAINER_DEBUG_LEVEL > 0 + } + + template = 0> + constexpr span(element_type (&_Arr)[_Size]) noexcept : _Mybase(_Size), _Mydata(_Arr) {} + + template = 0> + constexpr span(array& _Arr) noexcept : _Mybase(_Size), _Mydata(_Arr.data()) {} + + template , + is_convertible>, + int> = 0> + constexpr span(const array& _Arr) noexcept : _Mybase(_Size), _Mydata(_Arr.data()) {} + + template , + _Is_span_compatible_range<_Rng, element_type>>, + int> = 0> + constexpr span(_Rng& _Range) : _Mybase(static_cast(_STD size(_Range))), _Mydata(_STD data(_Range)) {} + + template , + _Is_span_compatible_range>, + int> = 0> + constexpr span(const _Rng& _Range) + : _Mybase(static_cast(_STD size(_Range))), _Mydata(_STD data(_Range)) {} + + template , + is_convertible<_OtherTy (*)[], element_type (*)[]>>, + int> = 0> + constexpr span(const span<_OtherTy, _OtherExtent>& _Other) noexcept + : _Mybase(_Other.size()), _Mydata(_Other.data()) {} + +#endif // !__cpp_lib_concepts + + // [span.sub] Subviews + template + _NODISCARD constexpr span first() const noexcept /* strengthened */ { + if constexpr (_Extent != dynamic_extent) { + static_assert(_Count <= _Extent, "Count out of range in span::first()"); + } +#if _CONTAINER_DEBUG_LEVEL > 0 + else { + _STL_VERIFY(_Count <= this->size(), "Count out of range in span::first()"); + } +#endif // _CONTAINER_DEBUG_LEVEL > 0 + return {_Mydata, _Count}; + } + + _NODISCARD constexpr span 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)"); +#endif // _CONTAINER_DEBUG_LEVEL > 0 + return {_Mydata, _Count}; + } + + template + _NODISCARD constexpr span last() const noexcept /* strengthened */ { + if constexpr (_Extent != dynamic_extent) { + static_assert(_Count <= _Extent, "Count out of range in span::last()"); + } +#if _CONTAINER_DEBUG_LEVEL > 0 + else { + _STL_VERIFY(_Count <= this->size(), "Count out of range in span::last()"); + } +#endif // _CONTAINER_DEBUG_LEVEL > 0 + return {_Mydata + (this->size() - _Count), _Count}; + } + + _NODISCARD constexpr span 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)"); +#endif // _CONTAINER_DEBUG_LEVEL > 0 + return {_Mydata + (this->size() - _Count), _Count}; + } + + template + _NODISCARD constexpr span + subspan() const noexcept /* strengthened */ { + if constexpr (_Extent != dynamic_extent) { + static_assert(_Offset <= _Extent, "Offset out of range in span::subspan()"); + static_assert( + _Count == dynamic_extent || _Count <= _Extent - _Offset, "Count out of range in span::subspan()"); + } +#if _CONTAINER_DEBUG_LEVEL > 0 + else { + _STL_VERIFY(_Offset <= this->size(), "Offset out of range in span::subspan()"); + _STL_VERIFY( + _Count == dynamic_extent || _Count <= this->size() - _Offset, "Count out of range in span::subspan()"); + } +#endif // _CONTAINER_DEBUG_LEVEL > 0 + return {_Mydata + _Offset, _Count == dynamic_extent ? this->size() - _Offset : _Count}; + } + + _NODISCARD constexpr span 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, + "Count out of range in span::subspan(offset, count)"); +#endif // _CONTAINER_DEBUG_LEVEL > 0 + return {_Mydata + _Offset, _Count == dynamic_extent ? this->size() - _Offset : _Count}; + } + + // [span.obs] Observers + _NODISCARD constexpr size_type size_bytes() const noexcept { +#if _CONTAINER_DEBUG_LEVEL > 0 + _STL_VERIFY(this->size() <= dynamic_extent / sizeof(element_type), + "size of span in bytes exceeds std::numeric_limits::max()"); +#endif // _CONTAINER_DEBUG_LEVEL > 0 + return this->size() * sizeof(element_type); + } + + _NODISCARD constexpr bool empty() const noexcept { + return this->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"); +#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"); +#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"); +#endif // _CONTAINER_DEBUG_LEVEL > 0 + return _Mydata[this->size() - 1]; + } + + _NODISCARD constexpr pointer data() const noexcept { + return _Mydata; + } + + // [span.iterators] Iterator support + _NODISCARD constexpr iterator begin() const noexcept { +#if _ITERATOR_DEBUG_LEVEL >= 1 + return {_Mydata, _Mydata, _Mydata + this->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(); +#if _ITERATOR_DEBUG_LEVEL >= 1 + return {_End, _Mydata, _End}; +#else // ^^^ _ITERATOR_DEBUG_LEVEL >= 1 ^^^ // vvv _ITERATOR_DEBUG_LEVEL == 0 vvv + return {_End}; +#endif // _ITERATOR_DEBUG_LEVEL + } + + _NODISCARD constexpr const_iterator cbegin() const noexcept { +#if _ITERATOR_DEBUG_LEVEL >= 1 + return {_Mydata, _Mydata, _Mydata + this->size()}; +#else // ^^^ _ITERATOR_DEBUG_LEVEL >= 1 ^^^ // vvv _ITERATOR_DEBUG_LEVEL == 0 vvv + return {_Mydata}; +#endif // _ITERATOR_DEBUG_LEVEL + } + + _NODISCARD constexpr const_iterator cend() const noexcept { + const auto _End = _Mydata + this->size(); +#if _ITERATOR_DEBUG_LEVEL >= 1 + return {_End, _Mydata, _End}; +#else // ^^^ _ITERATOR_DEBUG_LEVEL >= 1 ^^^ // vvv _ITERATOR_DEBUG_LEVEL == 0 vvv + return {_End}; +#endif // _ITERATOR_DEBUG_LEVEL + } + + _NODISCARD constexpr reverse_iterator rbegin() const noexcept { + return reverse_iterator{end()}; + } + + _NODISCARD constexpr reverse_iterator rend() const noexcept { + return reverse_iterator{begin()}; + } + + _NODISCARD constexpr const_reverse_iterator crbegin() const noexcept { + return const_reverse_iterator{cend()}; + } + + _NODISCARD constexpr const_reverse_iterator crend() const noexcept { + return const_reverse_iterator{cbegin()}; + } + + _NODISCARD constexpr pointer _Unchecked_begin() const noexcept { + return _Mydata; + } + + _NODISCARD constexpr pointer _Unchecked_end() const noexcept { + return _Mydata + this->size(); + } + +private: + using _Mybase = _Span_extent_type<_Extent>; + + pointer _Mydata{nullptr}; +}; + +// DEDUCTION GUIDES +template +span(_Ty (&)[_Extent])->span<_Ty, _Extent>; + +template +span(array<_Ty, _Size>&)->span<_Ty, _Size>; + +template +span(const array<_Ty, _Size>&)->span; + +#ifdef __cpp_lib_concepts + +template +span(_It, _End)->span>>; + +template <_RANGES contiguous_range _Rng> +span(_Rng &&)->span>>; + +#else // ^^^ __cpp_lib_concepts / !__cpp_lib_concepts vvv + +template +span(_Rng&)->span; + +template +span(const _Rng&)->span; + +#endif // !__cpp_lib_concepts + +#if _HAS_STD_BYTE +// [span.objectrep] Views of object representation +// FUNCTION TEMPLATE as_bytes +template +_NODISCARD auto as_bytes(span<_Ty, _Extent> _Sp) noexcept + -> span { + return {reinterpret_cast(_Sp.data()), _Sp.size_bytes()}; +} + +// FUNCTION TEMPLATE as_writable_bytes +template , int> = 0> +_NODISCARD auto as_writable_bytes(span<_Ty, _Extent> _Sp) noexcept + -> span { + return {reinterpret_cast(_Sp.data()), _Sp.size_bytes()}; +} +#endif // _HAS_STD_BYTE + +// [span.tuple] Tuple interface +#ifdef __cpp_lib_concepts + +// clang-format off +template + requires (_Extent != dynamic_extent) +struct tuple_size> : integral_constant {}; +// clang-format on + +#else // ^^^ __cpp_lib_concepts / !__cpp_lib_concepts vvv + +template +struct tuple_size> : integral_constant {}; + +template +struct tuple_size>; + +#endif // !__cpp_lib_concepts + +template +struct tuple_element<_Index, span<_Ty, _Extent>> { + static_assert(dynamic_extent != _Extent, "std::span is not tuple-like"); + static_assert(_Index < _Extent, "Index out of bounds for a std::span of this extent"); + using type = _Ty; +}; + +template +_NODISCARD constexpr _Ty& get(span<_Ty, _Extent> _Span) noexcept { + static_assert(dynamic_extent != _Extent, "std::get<> not supported for std::span"); + static_assert(_Index < _Extent, "Index out of bounds for a std::span of this extent"); + return _Span[_Index]; +} + +_STD_END + +#pragma pop_macro("new") +_STL_RESTORE_CLANG_WARNINGS +#pragma warning(pop) +#pragma pack(pop) +#endif // _HAS_CXX20 +#endif // _STL_COMPILER_PREPROCESSOR +#endif // _SPAN_ diff --git a/stl/inc/yvals_core.h b/stl/inc/yvals_core.h index 296bb4ab5e1..c07340013e9 100644 --- a/stl/inc/yvals_core.h +++ b/stl/inc/yvals_core.h @@ -127,6 +127,7 @@ // _HAS_CXX20 directly controls: // P0020R6 atomic, atomic, atomic +// P0122R7 // P0318R1 unwrap_reference, unwrap_ref_decay // P0325R4 to_array() // P0356R5 bind_front() @@ -161,10 +162,12 @@ // P0919R3 Heterogeneous Lookup For Unordered Containers // P0966R1 string::reserve() Should Not Shrink // P1006R1 constexpr For pointer_traits::pointer_to() +// P1024R3 Enhancing span Usability +// P1085R2 Removing span Comparisons // P1209R0 erase_if(), erase() // P1227R2 Signed std::ssize(), Unsigned span::size() -// (partially implemented) // P1357R1 is_bounded_array, is_unbounded_array +// P1394R4 Range Constructor For span // P1456R1 Move-Only Views // P1612R1 Relocating endian To // P1645R1 constexpr For Algorithms @@ -172,6 +175,7 @@ // P1690R1 Refining Heterogeneous Lookup For Unordered Containers // P1754R1 Rename Concepts To standard_case // P1870R1 safe_range +// P1872R0 span Should Have size_type, Not index_type // P1959R0 Removing weak_equality And strong_equality // P????R? directory_entry::clear_cache() @@ -1014,6 +1018,7 @@ #define __cpp_lib_math_constants 201907L #define __cpp_lib_remove_cvref 201711L #define __cpp_lib_shift 201806L +#define __cpp_lib_span 201902L #define __cpp_lib_ssize 201902L #define __cpp_lib_starts_ends_with 201711L #define __cpp_lib_to_address 201711L From 8e2ea45ff861d06e97c219485dd061469086ffd3 Mon Sep 17 00:00:00 2001 From: "Stephan T. Lavavej" Date: Tue, 7 Jan 2020 14:25:27 -0800 Subject: [PATCH 02/11] Fix warning C4127 "conditional expression is constant". --- stl/inc/span | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/stl/inc/span b/stl/inc/span index f628310bd17..5241aa07fb6 100644 --- a/stl/inc/span +++ b/stl/inc/span @@ -533,8 +533,10 @@ public: #if _CONTAINER_DEBUG_LEVEL > 0 else { _STL_VERIFY(_Offset <= this->size(), "Offset out of range in span::subspan()"); - _STL_VERIFY( - _Count == dynamic_extent || _Count <= this->size() - _Offset, "Count out of range in span::subspan()"); + + if constexpr (_Count != dynamic_extent) { + _STL_VERIFY(_Count <= this->size() - _Offset, "Count out of range in span::subspan()"); + } } #endif // _CONTAINER_DEBUG_LEVEL > 0 return {_Mydata + _Offset, _Count == dynamic_extent ? this->size() - _Offset : _Count}; From 9f75aa86ec4b586ff78d4dd354c2f5c40348522f Mon Sep 17 00:00:00 2001 From: "Stephan T. Lavavej" Date: Tue, 7 Jan 2020 17:28:27 -0800 Subject: [PATCH 03/11] Fix span CTAD from built-in arrays. This is a defect in the Standardese, for which I've submitted an LWG issue with an extensive writeup. Basically, the constrained constructor out-competes the non-constrained deduction-guide, resulting in the dynamic_extent default argument being used. Adding type_identity_t prevents the constructor from interfering with CTAD (which is why the std::array constructors aren't affected; they involve value_type which is remove_cv_t). This is specific to concepts; there's Standardese that prefers "more constrained" during overload resolution. The non-concepts implementation of this constructor doesn't need to be changed. --- stl/inc/span | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/stl/inc/span b/stl/inc/span index 5241aa07fb6..807ff3e43c7 100644 --- a/stl/inc/span +++ b/stl/inc/span @@ -400,7 +400,7 @@ public: template requires (_Extent == dynamic_extent || _Extent == _Size) - constexpr span(element_type (&_Arr)[_Size]) noexcept : _Mybase(_Size), _Mydata(_Arr) {} + constexpr span(type_identity_t (&_Arr)[_Size]) noexcept : _Mybase(_Size), _Mydata(_Arr) {} template requires (_Extent == dynamic_extent || _Extent == _Size) From 048be0886414d17b1f83c6052e8ec16c4f991a4a Mon Sep 17 00:00:00 2001 From: "Stephan T. Lavavej" Date: Thu, 9 Jan 2020 14:12:09 -0800 Subject: [PATCH 04/11] Enable span tests in libcxx. --- tests/libcxx/skipped_tests.txt | 45 ++++++++++------------------------ 1 file changed, 13 insertions(+), 32 deletions(-) diff --git a/tests/libcxx/skipped_tests.txt b/tests/libcxx/skipped_tests.txt index e762c2150ab..f50071d081e 100644 --- a/tests/libcxx/skipped_tests.txt +++ b/tests/libcxx/skipped_tests.txt @@ -196,38 +196,6 @@ utilities\tuple\tuple.tuple\tuple.apply\apply_large_arity.pass.cpp # C++20 P0019R8 "atomic_ref" language.support\support.limits\support.limits.general\atomic.version.pass.cpp -# C++20 P0122R7 "" (and subsequent patch papers) -containers\views\types.pass.cpp -containers\views\span.cons\array.pass.cpp -containers\views\span.cons\assign.pass.cpp -containers\views\span.cons\container.pass.cpp -containers\views\span.cons\copy.pass.cpp -containers\views\span.cons\deduct.pass.cpp -containers\views\span.cons\default.pass.cpp -containers\views\span.cons\ptr_len.pass.cpp -containers\views\span.cons\ptr_ptr.pass.cpp -containers\views\span.cons\span.pass.cpp -containers\views\span.cons\stdarray.pass.cpp -containers\views\span.elem\back.pass.cpp -containers\views\span.elem\data.pass.cpp -containers\views\span.elem\front.pass.cpp -containers\views\span.elem\op_idx.pass.cpp -containers\views\span.iterators\begin.pass.cpp -containers\views\span.iterators\end.pass.cpp -containers\views\span.iterators\rbegin.pass.cpp -containers\views\span.iterators\rend.pass.cpp -containers\views\span.objectrep\as_bytes.pass.cpp -containers\views\span.objectrep\as_writable_bytes.pass.cpp -containers\views\span.obs\empty.pass.cpp -containers\views\span.obs\size.pass.cpp -containers\views\span.obs\size_bytes.pass.cpp -containers\views\span.sub\first.pass.cpp -containers\views\span.sub\last.pass.cpp -containers\views\span.sub\subspan.pass.cpp -containers\views\span.tuple\get.pass.cpp -containers\views\span.tuple\tuple_element.pass.cpp -containers\views\span.tuple\tuple_size.pass.cpp - # C++20 P0202R3 "constexpr For And exchange()" algorithms\alg.modifying.operations\alg.copy\copy_backward.pass.cpp algorithms\alg.modifying.operations\alg.copy\copy_if.pass.cpp @@ -303,6 +271,9 @@ algorithms\alg.sorting\alg.sort\is.sorted\is_sorted_comp.pass.cpp algorithms\alg.sorting\alg.sort\is.sorted\is_sorted_until_comp.pass.cpp algorithms\alg.sorting\alg.sort\is.sorted\is_sorted_until.pass.cpp algorithms\alg.sorting\alg.sort\is.sorted\is_sorted.pass.cpp +containers\views\span.sub\first.pass.cpp +containers\views\span.sub\last.pass.cpp +containers\views\span.sub\subspan.pass.cpp utilities\utility\exchange\exchange.pass.cpp # C++20 P0355R7 " Calendars And Time Zones" @@ -608,6 +579,10 @@ utilities\tuple\tuple.tuple\tuple.cnstr\test_lazy_sfinae.pass.cpp # Compiler bug: VSO-406936 "is_constructible and is_constructible> should be true" utilities\meta\meta.unary\meta.unary.prop\is_constructible.pass.cpp +# Compiler bug: DevCom-876860 "conditional operator errors" blocks readable. +containers\views\span.cons\ptr_len.pass.cpp +containers\views\span.cons\ptr_ptr.pass.cpp + # *** CLANG COMPILER BUGS *** # LLVM-33230 "Clang on Windows should define __STDCPP_THREADS__ to be 1" @@ -617,6 +592,12 @@ thread\macro.pass.cpp iterators\iterator.primitives\iterator.traits\pointer.pass.cpp iterators\iterator.primitives\std.iterator.tags\contiguous_iterator_tag.pass.cpp +# Clang 9 doesn't support comparison rewriting; implemented in Clang 10. +containers\views\span.iterators\begin.pass.cpp +containers\views\span.iterators\end.pass.cpp +containers\views\span.iterators\rbegin.pass.cpp +containers\views\span.iterators\rend.pass.cpp + # *** CLANG ISSUES, NOT YET ANALYZED *** # Clang doesn't enable sized deallocation by default. Should we add -fsized-deallocation or do something else? From c9e647c074b5b6e978cd90c374eb48acf5ced5ec Mon Sep 17 00:00:00 2001 From: "Stephan T. Lavavej" Date: Thu, 16 Jan 2020 16:43:12 -0800 Subject: [PATCH 05/11] Simplify _Span_extent_type. span's constructors perform _CONTAINER_DEBUG_LEVEL checking, so the checking in _Span_extent_type was unnecessary and less specific. _Span_extent_type's converting constructor was simply never used. --- stl/inc/span | 20 +------------------- 1 file changed, 1 insertion(+), 19 deletions(-) diff --git a/stl/inc/span b/stl/inc/span index 807ff3e43c7..c3cfa19ecc5 100644 --- a/stl/inc/span +++ b/stl/inc/span @@ -225,22 +225,7 @@ template struct _Span_extent_type { constexpr _Span_extent_type() noexcept = default; - constexpr explicit _Span_extent_type([[maybe_unused]] const size_t _Size) noexcept { -#if _CONTAINER_DEBUG_LEVEL > 0 - _STL_VERIFY(_Size == _Extent, "Mismatch between static extent and size of initializing data"); -#endif // _CONTAINER_DEBUG_LEVEL > 0 - } - - template - constexpr explicit _Span_extent_type(const _Span_extent_type<_OtherExtent> _Ext) noexcept { - static_assert(_OtherExtent == _Extent || _OtherExtent == dynamic_extent, - "Mismatch between static extent and size of initializing data"); -#if _CONTAINER_DEBUG_LEVEL > 0 - if constexpr (_OtherExtent == dynamic_extent) { - _STL_VERIFY(_Ext.size() == _Extent, "Mismatch between static extent and size of initializing data"); - } -#endif // _CONTAINER_DEBUG_LEVEL > 0 - } + constexpr explicit _Span_extent_type(size_t) noexcept {} _NODISCARD constexpr size_t size() const noexcept { return _Extent; @@ -253,9 +238,6 @@ struct _Span_extent_type { constexpr explicit _Span_extent_type(const size_t _Size) noexcept : _Mysize(_Size) {} - template - constexpr explicit _Span_extent_type(const _Span_extent_type<_OtherExtent> _Ext) noexcept : _Mysize(_Ext.size()) {} - _NODISCARD constexpr size_t size() const noexcept { return _Mysize; } From 31a2dbadfb8a0567f7020a9dcf8b0d17a801c4ae Mon Sep 17 00:00:00 2001 From: "Stephan T. Lavavej" Date: Thu, 16 Jan 2020 16:45:30 -0800 Subject: [PATCH 06/11] Add constexpr to std::to_address(const _Ptr&). An LWG issue has been submitted; it's necessary for span testing. --- stl/inc/xutility | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/stl/inc/xutility b/stl/inc/xutility index e6a0e432f80..9d69a0922bb 100644 --- a/stl/inc/xutility +++ b/stl/inc/xutility @@ -140,7 +140,7 @@ _NODISCARD constexpr _Ty* to_address(_Ty* const _Val) noexcept { } template -_NODISCARD auto to_address(const _Ptr& _Val) noexcept { +_NODISCARD constexpr auto to_address(const _Ptr& _Val) noexcept { if constexpr (_Has_to_address_v<_Ptr>) { return pointer_traits<_Ptr>::to_address(_Val); } else { From c28cb004fcd752207eb56094bc9f60aca7fd6f2e Mon Sep 17 00:00:00 2001 From: Michael Schellenberger Costa Date: Fri, 17 Jan 2020 07:52:29 +0100 Subject: [PATCH 07/11] Add implicit bounds checks for the pointer + size constructor --- stl/inc/span | 2 ++ 1 file changed, 2 insertions(+) diff --git a/stl/inc/span b/stl/inc/span index c3cfa19ecc5..37588517a48 100644 --- a/stl/inc/span +++ b/stl/inc/span @@ -363,6 +363,7 @@ public: constexpr span(_It _First, size_type _Count) noexcept // strengthened : _Mybase(_Count), _Mydata(_STD to_address(_First)) { #if _CONTAINER_DEBUG_LEVEL > 0 + (void) (_First + _Count); // Implicit bounds checking for checked iterators if constexpr (_Extent != dynamic_extent) { _STL_VERIFY(_Count == _Extent, "span construction from invalid range as count != extent"); } @@ -412,6 +413,7 @@ public: constexpr span(pointer _Ptr, size_type _Count) noexcept // strengthened : _Mybase(_Count), _Mydata(_Ptr) { #if _CONTAINER_DEBUG_LEVEL > 0 + (void) (_Ptr + _Count); // Implicit bounds checking for checked iterators if constexpr (_Extent != dynamic_extent) { _STL_VERIFY(_Count == _Extent, "span construction from invalid range as count != extent"); } From 5de0f95f36c3ecc2d25a501ff073b06abebdeee5 Mon Sep 17 00:00:00 2001 From: Michael Schellenberger Costa Date: Fri, 17 Jan 2020 16:41:19 +0100 Subject: [PATCH 08/11] Put _Adl_verify_range inside _CONTAINER_DEBUG_LEVEL --- stl/inc/span | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/stl/inc/span b/stl/inc/span index 37588517a48..c438527d6df 100644 --- a/stl/inc/span +++ b/stl/inc/span @@ -373,8 +373,8 @@ public: template <_Is_span_compatible_iterator _It, _Is_span_compatible_sentinel<_It> _Sentinel> constexpr span(_It _First, _Sentinel _Last) noexcept(noexcept(_Last - _First)) // strengthened : _Mybase(static_cast(_Last - _First)), _Mydata(_STD to_address(_First)) { - _Adl_verify_range(_First, _Last); #if _CONTAINER_DEBUG_LEVEL > 0 + _Adl_verify_range(_First, _Last); if constexpr (_Extent != dynamic_extent) { _STL_VERIFY(_Last - _First == _Extent, "span construction from invalid range as last - first != extent"); } @@ -422,8 +422,8 @@ public: constexpr span(pointer _First, pointer _Last) noexcept // strengthened : _Mybase(static_cast(_Last - _First)), _Mydata(_First) { - _Adl_verify_range(_First, _Last); #if _CONTAINER_DEBUG_LEVEL > 0 + _Adl_verify_range(_First, _Last); if constexpr (_Extent != dynamic_extent) { _STL_VERIFY(_Last - _First == _Extent, "span construction from invalid range as last - first != extent"); } From 1ee24339c705b46f4632ec67aa03081bf2a74555 Mon Sep 17 00:00:00 2001 From: Michael Schellenberger Costa Date: Fri, 17 Jan 2020 18:51:02 +0100 Subject: [PATCH 09/11] Pointer are boundless --- stl/inc/span | 1 - 1 file changed, 1 deletion(-) diff --git a/stl/inc/span b/stl/inc/span index c438527d6df..c89393d6999 100644 --- a/stl/inc/span +++ b/stl/inc/span @@ -413,7 +413,6 @@ public: constexpr span(pointer _Ptr, size_type _Count) noexcept // strengthened : _Mybase(_Count), _Mydata(_Ptr) { #if _CONTAINER_DEBUG_LEVEL > 0 - (void) (_Ptr + _Count); // Implicit bounds checking for checked iterators if constexpr (_Extent != dynamic_extent) { _STL_VERIFY(_Count == _Extent, "span construction from invalid range as count != extent"); } From 7adf3e34588d986779a080f8abf367086a6b3e5d Mon Sep 17 00:00:00 2001 From: Michael Schellenberger Costa Date: Fri, 17 Jan 2020 20:55:17 +0100 Subject: [PATCH 10/11] Improve checking Move _Adl_verify_range out of _CONTAINER_DEBUG_LEVEL checks so that it works with checked iterators Use _Get_unwrapped_n to check validity of iterator + size constructor --- stl/inc/span | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/stl/inc/span b/stl/inc/span index c89393d6999..d7030ae97cd 100644 --- a/stl/inc/span +++ b/stl/inc/span @@ -361,9 +361,8 @@ public: template <_Is_span_compatible_iterator _It> constexpr span(_It _First, size_type _Count) noexcept // strengthened - : _Mybase(_Count), _Mydata(_STD to_address(_First)) { + : _Mybase(_Count), _Mydata(_STD to_address(_Get_unwrapped_n(_First, _Count))) { #if _CONTAINER_DEBUG_LEVEL > 0 - (void) (_First + _Count); // Implicit bounds checking for checked iterators if constexpr (_Extent != dynamic_extent) { _STL_VERIFY(_Count == _Extent, "span construction from invalid range as count != extent"); } @@ -373,8 +372,8 @@ public: template <_Is_span_compatible_iterator _It, _Is_span_compatible_sentinel<_It> _Sentinel> constexpr span(_It _First, _Sentinel _Last) noexcept(noexcept(_Last - _First)) // strengthened : _Mybase(static_cast(_Last - _First)), _Mydata(_STD to_address(_First)) { -#if _CONTAINER_DEBUG_LEVEL > 0 _Adl_verify_range(_First, _Last); +#if _CONTAINER_DEBUG_LEVEL > 0 if constexpr (_Extent != dynamic_extent) { _STL_VERIFY(_Last - _First == _Extent, "span construction from invalid range as last - first != extent"); } @@ -421,8 +420,8 @@ public: constexpr span(pointer _First, pointer _Last) noexcept // strengthened : _Mybase(static_cast(_Last - _First)), _Mydata(_First) { -#if _CONTAINER_DEBUG_LEVEL > 0 _Adl_verify_range(_First, _Last); +#if _CONTAINER_DEBUG_LEVEL > 0 if constexpr (_Extent != dynamic_extent) { _STL_VERIFY(_Last - _First == _Extent, "span construction from invalid range as last - first != extent"); } From 0a9cdb16f0f47faa5f72856b77471ea086b63e9c Mon Sep 17 00:00:00 2001 From: "Stephan T. Lavavej" Date: Fri, 17 Jan 2020 16:50:11 -0800 Subject: [PATCH 11/11] Comment constexpr to_address(). --- stl/inc/xutility | 1 + 1 file changed, 1 insertion(+) diff --git a/stl/inc/xutility b/stl/inc/xutility index 9d69a0922bb..18ecf96bf9f 100644 --- a/stl/inc/xutility +++ b/stl/inc/xutility @@ -139,6 +139,7 @@ _NODISCARD constexpr _Ty* to_address(_Ty* const _Val) noexcept { return _Val; } +// constexpr per pending LWG issue, submitted 2020-01-14 template _NODISCARD constexpr auto to_address(const _Ptr& _Val) noexcept { if constexpr (_Has_to_address_v<_Ptr>) {