From 377b2db537f4f0d766f2a66d7a27033f62b19e19 Mon Sep 17 00:00:00 2001 From: "Jordan Maples [MSFT]" <49793787+JordanMaples@users.noreply.github.com> Date: Fri, 7 Feb 2020 14:09:57 -0800 Subject: [PATCH] almost parity with std::span --- include/gsl/span | 107 ++++++++++++++++++++++++++++++++----------- tests/span_tests.cpp | 16 ++++++- 2 files changed, 94 insertions(+), 29 deletions(-) diff --git a/include/gsl/span b/include/gsl/span index b922576..6cfc91c 100644 --- a/include/gsl/span +++ b/include/gsl/span @@ -111,8 +111,7 @@ namespace details template struct is_allowed_extent_conversion - : public std::integral_constant + : public std::integral_constant { }; @@ -301,7 +300,9 @@ namespace details if (n < 0) Expects(current_ - begin_ >= -n); } + // clang-format off GSL_SUPPRESS(bounds.1) // NO-FORMAT: attribute + // clang-format on constexpr pointer _Unwrapped() const noexcept { // after seeking *this to a high water mark, or using one of the // _Verify_xxx functions above, unwrap this span_iterator to a raw @@ -316,7 +317,9 @@ namespace details #else static constexpr bool _Unwrap_when_unverified = false; #endif + // clang-format off GSL_SUPPRESS(con.3) // NO-FORMAT: attribute // TODO: false positive + // clang-format on constexpr void _Seek_to(const pointer p) noexcept { // adjust the position of *this to previously verified location p // after _Unwrapped @@ -392,7 +395,9 @@ public: using value_type = std::remove_cv_t; using size_type = std::size_t; using pointer = element_type*; + using const_pointer = const element_type*; using reference = element_type&; + using const_reference = const element_type&; using difference_type = std::ptrdiff_t; using iterator = details::span_iterator; @@ -410,38 +415,48 @@ public: // [span.cons], span constructors, copy, assignment, and destructor template " SFINAE, - // since "std::enable_if_t" is ill-formed when Extent is greater than 0. + // "Dependent" is needed to make "std::enable_if_t" SFINAE, since "std::enable_if_t" is ill-formed when Extent is greater than 0. class = std::enable_if_t<(Dependent || Extent == 0 || Extent == dynamic_extent)>> constexpr span() noexcept : storage_(nullptr, details::extent_type<0>()) {} - constexpr span(pointer ptr, size_type count) noexcept : storage_(ptr, count) {} + constexpr span(pointer ptr, size_type count) noexcept : storage_(ptr, count) + { + if (Extent != dynamic_extent) Expects(count == Extent); + } constexpr span(pointer firstElem, pointer lastElem) noexcept : storage_(firstElem, static_cast(std::distance(firstElem, lastElem))) - {} + { + if (Extent != dynamic_extent) { Expects(lastElem - firstElem == Extent); } + } template constexpr span(element_type (&arr)[N]) noexcept : storage_(KnownNotNull{std::addressof(arr[0])}, details::extent_type()) {} - template 0)>> - constexpr span(std::array, N>& arr) noexcept + template = 0> + constexpr span(std::array& arr) noexcept : storage_(KnownNotNull{arr.data()}, details::extent_type()) {} - constexpr span(std::array, 0>&) noexcept + template ::value), + int> = 0> + constexpr span(const std::array& arr) noexcept + : storage_(KnownNotNull{arr.data()}, details::extent_type()) + {} + + constexpr span(std::array&) noexcept : storage_(static_cast(nullptr), details::extent_type<0>()) {} - template 0)>> - constexpr span(const std::array, N>& arr) noexcept - : storage_(KnownNotNull{arr.data()}, details::extent_type()) - {} - - constexpr span(const std::array, 0>&) noexcept + constexpr span(const std::array&) noexcept : storage_(static_cast(nullptr), details::extent_type<0>()) {} @@ -451,8 +466,8 @@ public: class = std::enable_if_t< !details::is_span::value && !details::is_std_array::value && std::is_convertible::value && - std::is_convertible().data())>::value>> + std::is_convertible::value && + std::is_convertible().data()), pointer>::value>> constexpr span(Container& cont) noexcept : span(cont.data(), cont.size()) {} @@ -460,10 +475,8 @@ public: class = std::enable_if_t< std::is_const::value && !details::is_span::value && std::is_convertible::value && - std::is_convertible().data())>::value>> - constexpr span(const Container& cont) noexcept - : span(cont.data(), cont.size()) + std::is_convertible().data()), pointer>::value>> + constexpr span(const Container& cont) noexcept : span(cont.data(), cont.size()) {} constexpr span(const span& other) noexcept = default; @@ -489,16 +502,20 @@ public: } template + // clang-format off GSL_SUPPRESS(bounds.1) // NO-FORMAT: attribute - constexpr span last() const noexcept + // clang-format on + constexpr span last() const noexcept { Expects(size() >= Count); return {data() + (size() - Count), Count}; } template + // clang-format off GSL_SUPPRESS(bounds.1) // NO-FORMAT: attribute - constexpr auto subspan() const noexcept -> + // clang-format on + constexpr auto subspan() const noexcept -> typename details::calculate_subspan_type::type { Expects((size() >= Offset) && (Count == dynamic_extent || (Count <= size() - Offset))); @@ -537,7 +554,9 @@ public: constexpr bool empty() const noexcept { return size() == 0; } // [span.elem], span element access + // clang-format off GSL_SUPPRESS(bounds.1) // NO-FORMAT: attribute + // clang-format on constexpr reference operator[](size_type idx) const noexcept { Expects(idx < size()); @@ -602,7 +621,9 @@ public: constexpr pointer _Unchecked_begin() const noexcept { return data(); } constexpr pointer _Unchecked_end() const noexcept { + // clang-format off GSL_SUPPRESS(bounds.1) // NO-FORMAT: attribute + // clang-format on return data() + size(); } #endif // _MSC_VER @@ -662,7 +683,9 @@ private: return tmp.subspan(offset, count); } + // clang-format off GSL_SUPPRESS(bounds.1) // NO-FORMAT: attribute + // clang-format on span make_subspan(size_type offset, size_type count, subspan_selector) const { @@ -675,6 +698,20 @@ private: } }; +#if (defined(__cpp_deduction_guides) && (__cpp_deduction_guides >= 201611L)) + +// Deduction Guides +template +span(Type (&)[Extent])->span; + +template +span(std::array&)->span; + +template +span(const std::array&)->span; + +#endif // ( defined(__cpp_deduction_guides) && (__cpp_deduction_guides >= 201611L) ) + #if defined(GSL_USE_STATIC_CONSTEXPR_WORKAROUND) template constexpr const typename span::size_type span::extent; @@ -742,16 +779,32 @@ template span::value> as_bytes(span s) noexcept { + // clang-format off GSL_SUPPRESS(type.1) // NO-FORMAT: attribute + // clang-format on return {reinterpret_cast(s.data()), s.size_bytes()}; } template ::value>> + std::enable_if_t::value, int> = 0> span::value> +as_writable_bytes(span s) noexcept +{ + // clang-format off + GSL_SUPPRESS(type.1) // NO-FORMAT: attribute + // clang-format on + return {reinterpret_cast(s.data()), s.size_bytes()}; +} + +template ::value, int> = 0> +[[deprecated( + "use as_writable_bytes")]] span::value> as_writeable_bytes(span s) noexcept { + // clang-format off GSL_SUPPRESS(type.1) // NO-FORMAT: attribute + // clang-format on return {reinterpret_cast(s.data()), s.size_bytes()}; } @@ -759,8 +812,7 @@ as_writeable_bytes(span s) noexcept // make_span() - Utility functions for creating spans // template -constexpr span make_span(ElementType* ptr, - typename span::size_type count) +constexpr span make_span(ElementType* ptr, typename span::size_type count) { return span(ptr, count); } @@ -812,8 +864,7 @@ constexpr ElementType& at(span s, index i) // [span.obs] Free observer functions template -constexpr std::ptrdiff_t -ssize(const span& s) noexcept +constexpr std::ptrdiff_t ssize(const span& s) noexcept { return static_cast(s.size()); } diff --git a/tests/span_tests.cpp b/tests/span_tests.cpp index 790cee0..8c5ac0d 100644 --- a/tests/span_tests.cpp +++ b/tests/span_tests.cpp @@ -1551,11 +1551,15 @@ TEST(span_test, from_array_constructor) // even when done dynamically { span s = arr; + /* + // this now results in a compile-time error, rather than runtime. + // There is no suitable conversion from dynamic span to fixed span. auto f = [&]() { const span s2 = s; static_cast(s2); }; EXPECT_DEATH(f(), deathstring); + */ } // but doing so explicitly is ok @@ -1570,12 +1574,19 @@ TEST(span_test, from_array_constructor) static_cast(s1); } - // ...or dynamically + /* + // this is not a legal operation in std::span, so we are no longer supporting it + // conversion from span to span via call to `first` + // then convert from span to span + // The dynamic to fixed extents are not supported in the standard + // to make this work, span would need to be span. { + // NB: implicit conversion to span from span span s1 = s4.first(1); static_cast(s1); } + */ // initialization or assignment to static span that requires size INCREASE is not ok. int arr2[2] = {1, 2}; @@ -1597,12 +1608,15 @@ TEST(span_test, from_array_constructor) EXPECT_DEATH(f(), deathstring); } + /* + // This no longer compiles. There is no suitable conversion from dynamic span to a fixed size span. // this should fail - we are trying to assign a small dynamic span to a fixed_size larger one span av = arr2; auto f = [&]() { const span _s4 = av; static_cast(_s4); }; EXPECT_DEATH(f(), deathstring); + */ } TEST(span_test, interop_with_std_regex)