diff --git a/include/gsl_util.h b/include/gsl_util.h index a3d5fbe..92a795b 100644 --- a/include/gsl_util.h +++ b/include/gsl_util.h @@ -118,7 +118,6 @@ inline T narrow(U u) { T t = narrow_cast(u); if (static_cast(t) != u) throw narrowing_error(); -#pragma warning(suppress : 4127) // suppress warning from MSVC compiler about constant in if-test if (!details::is_same_signedness::value && ((t < T{}) != (u < U{}))) throw narrowing_error(); return t; diff --git a/include/span.h b/include/span.h index f40a7ab..27c479d 100644 --- a/include/span.h +++ b/include/span.h @@ -79,12 +79,12 @@ namespace gsl { -template -class span; - // [views.constants], constants constexpr const std::ptrdiff_t dynamic_extent = -1; +template +class span; + // implementation details namespace details { @@ -99,20 +99,35 @@ namespace details }; template - struct is_span : is_span_oracle> + struct is_span : public is_span_oracle> + { + }; + + template + struct is_std_array_oracle : std::false_type + { + }; + + template + struct is_std_array_oracle> : std::true_type + { + }; + + template + struct is_std_array : public is_std_array_oracle> { }; template struct is_allowed_pointer_conversion - : std::bool_constant::value && std::is_pointer::value && + : public std::integral_constant::value && std::is_pointer::value && std::is_convertible::value> { }; template struct is_allowed_integral_conversion - : std::bool_constant::value && std::is_integral::value && + : public std::integral_constant::value && std::is_integral::value && sizeof(From) == sizeof(To) && alignof(From) == alignof(To) && std::is_convertible::value> { @@ -120,13 +135,13 @@ namespace details template struct is_allowed_extent_conversion - : std::bool_constant + : public std::integral_constant { }; template struct is_allowed_element_type_conversion - : std::bool_constant>::value || + : public std::integral_constant>::value || is_allowed_pointer_conversion::value || is_allowed_integral_conversion::value> { @@ -134,12 +149,12 @@ namespace details template struct is_allowed_element_type_conversion - : std::bool_constant::value> + : public std::integral_constant::value> { }; template - struct is_allowed_element_type_conversion : std::true_type + struct is_allowed_element_type_conversion : public std::true_type { }; @@ -269,12 +284,12 @@ namespace details void swap(const_span_iterator& rhs) noexcept { std::swap(index_, rhs.index_); - std::swap(m_span, rhs.m_span); + std::swap(span_, rhs.span_); } private: const Span* span_; - ptrdiff_t index_; + std::ptrdiff_t index_; }; template @@ -323,12 +338,12 @@ namespace details constexpr span_iterator operator+(difference_type n) const noexcept { - return base_type::operator+(n); + return {base_type::operator+(n)}; } constexpr span_iterator& operator+=(difference_type n) noexcept { - return base_type::operator+=(n); + return {base_type::operator+=(n)}; } constexpr span_iterator operator-(difference_type n) const noexcept @@ -376,6 +391,10 @@ namespace details } void swap(span_iterator& rhs) noexcept { base_type::swap(rhs); } + private: + constexpr span_iterator(const base_type& base) : base_type(base) + { + } }; template @@ -408,6 +427,47 @@ namespace details return rhs - n; } + template + class extent_type + { + public: + using index_type = std::ptrdiff_t; + + static_assert(Ext >= 0, "A fixed-size span must be >= 0 in size."); + + constexpr extent_type() noexcept {} + + template + constexpr extent_type(extent_type ext) noexcept + { + static_assert(Other == Ext || Other == dynamic_extent, + "Mismatch between fixed-size extent and size of initializing data."); + Expects(ext.size() == Ext); + } + + constexpr extent_type(index_type size) { Expects(size == Ext); } + + constexpr inline index_type size() const noexcept { return Ext; } + }; + + template <> + class extent_type + { + public: + using index_type = std::ptrdiff_t; + + template + explicit constexpr extent_type(extent_type ext) : size_(ext.size()) + { + } + + explicit constexpr extent_type(index_type size) : size_(size) { Expects(size >= 0); } + + constexpr inline index_type size() const noexcept { return size_; } + + private: + index_type size_; + }; } // namespace details // [span], class template span @@ -429,9 +489,9 @@ public: constexpr static const index_type extent = Extent; // [span.cons], span constructors, copy, assignment, and destructor - constexpr span() noexcept : storage_(nullptr, extent_type<0>()) {} + constexpr span() noexcept : storage_(nullptr, details::extent_type<0>()) {} - constexpr span(nullptr_t) noexcept : span() {} + constexpr span(std::nullptr_t) noexcept : span() {} constexpr span(pointer ptr, index_type count) : storage_(ptr, count) {} @@ -441,24 +501,19 @@ public: } template - constexpr span(element_type (&arr)[N]) noexcept : storage_(&arr[0], extent_type()) + constexpr span(element_type (&arr)[N]) noexcept : storage_(&arr[0], details::extent_type()) + { + } + + template > + constexpr span(std::array& arr) noexcept + : storage_(&arr[0], details::extent_type()) { } template - constexpr span(std::array& arr) noexcept : storage_(&arr[0], extent_type()) - { - } - - template ::value>> - constexpr span(std::array, N>& arr) noexcept - : storage_(&arr[0], extent_type()) - { - } - - template ::value>> constexpr span(const std::array, N>& arr) noexcept - : storage_(&arr[0], extent_type()) + : storage_(&arr[0], details::extent_type()) { } @@ -467,8 +522,9 @@ public: template ::value && - std::is_convertible::value && - std::is_convertible::value && + std::is_convertible::value && + std::is_convertible().data())>::value>> constexpr span(Container& cont) : span(cont.data(), cont.size()) { @@ -477,8 +533,8 @@ public: template ::value && !details::is_span::value && - std::is_convertible::value && - std::is_convertible::value && + std::is_convertible().data())>::value>> constexpr span(const Container& cont) : span(cont.data(), cont.size()) { @@ -493,7 +549,7 @@ public: details::is_allowed_extent_conversion::value && details::is_allowed_element_type_conversion::value>> constexpr span(const span& other) - : storage_(reinterpret_cast(other.data()), extent_type(other.size())) + : storage_(reinterpret_cast(other.data()), details::extent_type(other.size())) { } @@ -503,7 +559,7 @@ public: details::is_allowed_extent_conversion::value && details::is_allowed_element_type_conversion::value>> constexpr span(span&& other) - : storage_(reinterpret_cast(other.data()), extent_type(other.size())) + : storage_(reinterpret_cast(other.data()), details::extent_type(other.size())) { } @@ -512,25 +568,25 @@ public: constexpr span& operator=(span&& other) noexcept = default; // [span.sub], span subviews - template + template constexpr span first() const { Expects(Count >= 0 && Count <= size()); return {data(), Count}; } - template + template constexpr span last() const { Expects(Count >= 0 && Count <= size()); return {data() + (size() - Count), Count}; } - template + template constexpr span subspan() const { - Expects((Offset == 0 || Offset > 0 && Offset <= size()) && - (Count == dynamic_extent || Count >= 0 && Offset + Count <= size())); + Expects((Offset == 0 || (Offset > 0 && Offset <= size())) && + (Count == dynamic_extent || (Count >= 0 && Offset + Count <= size()))); return {data() + Offset, Count == dynamic_extent ? size() - Offset : Count}; } @@ -549,8 +605,8 @@ public: constexpr span subspan(index_type offset, index_type count = dynamic_extent) const { - Expects((offset == 0 || offset > 0 && offset <= size()) && - (count == dynamic_extent || count >= 0 && offset + count <= size())); + Expects((offset == 0 || (offset > 0 && offset <= size())) && + (count == dynamic_extent || (count >= 0 && offset + count <= size()))); return {data() + offset, count == dynamic_extent ? size() - offset : count}; } @@ -584,47 +640,6 @@ public: const_reverse_iterator crend() const noexcept { return reverse_iterator{{this, 0}}; } private: - template - class extent_type; - - template - class extent_type - { - public: - static_assert(Extent >= 0, "A fixed-size span must be >= 0 in size."); - - constexpr extent_type() noexcept {} - - template - constexpr extent_type(extent_type ext) noexcept - { - static_assert(Other == Extent || Other == dynamic_extent, - "Mismatch between fixed-size extent and size of initializing data."); - Expects(ext.size() == Extent); - } - - constexpr extent_type(index_type size) { Expects(size == Extent); } - - constexpr inline index_type size() const noexcept { return Extent; } - }; - - template <> - class extent_type - { - public: - template - explicit constexpr extent_type(extent_type ext) : size_(ext.size()) - { - } - - explicit constexpr extent_type(index_type size) : size_(size) { Expects(size >= 0); } - - constexpr inline index_type size() const noexcept { return size_; } - - private: - index_type size_; - }; - // this implementation detail class lets us take advantage of the // empty base class optimization to pay for only storage of a single // pointer in the case of fixed-size spans @@ -644,41 +659,41 @@ private: pointer data_; }; - storage_type> storage_; + storage_type> storage_; }; // [span.comparison], span comparison operators -template -constexpr bool operator==(const span& l, const span& r) +template +constexpr bool operator==(const span& l, const span& r) { return std::equal(l.begin(), l.end(), r.begin(), r.end()); } -template +template constexpr bool operator!=(const span& l, const span& r) { return !(l == r); } -template +template constexpr bool operator<(const span& l, const span& r) { return std::lexicographical_compare(l.begin(), l.end(), r.begin(), r.end()); } -template +template constexpr bool operator<=(const span& l, const span& r) { return !(l > r); } -template +template constexpr bool operator>(const span& l, const span& r) { return r < l; } -template +template constexpr bool operator>=(const span& l, const span& r) { return !(l < r); @@ -692,11 +707,11 @@ namespace details // we should use a narrow_cast<> to go to size_t, but older compilers may not see it as // constexpr // and so will fail compilation of the template - template + template struct calculate_byte_size : std::integral_constant(sizeof(ElementType) * - static_cast(Extent))> + static_cast(sizeof(ElementType) * + static_cast(Extent))> { }; @@ -708,14 +723,14 @@ namespace details } // [span.objectrep], views of object representation -template +template span::value> as_bytes(span s) noexcept { return {reinterpret_cast(s.data()), s.size_bytes()}; } -template ::value>> span::value> as_writeable_bytes(span s) noexcept diff --git a/include/string_span.h b/include/string_span.h index bef8288..723f650 100644 --- a/include/string_span.h +++ b/include/string_span.h @@ -234,40 +234,39 @@ template class basic_string_span { public: - using value_type = CharT; - using const_value_type = std::add_const_t; - using pointer = std::add_pointer_t; - using reference = std::add_lvalue_reference_t; - using const_reference = std::add_lvalue_reference_t; - using impl_type = span; + using element_type = CharT; + using pointer = std::add_pointer_t; + using reference = std::add_lvalue_reference_t; + using const_reference = std::add_lvalue_reference_t>; + using impl_type = span; - using size_type = ptrdiff_t; + using index_type = typename impl_type::index_type; using iterator = typename impl_type::iterator; using const_iterator = typename impl_type::const_iterator; using reverse_iterator = typename impl_type::reverse_iterator; using const_reverse_iterator = typename impl_type::const_reverse_iterator; // default (empty) - constexpr basic_string_span() = default; + constexpr basic_string_span() noexcept = default; // copy - constexpr basic_string_span(const basic_string_span& other) = default; + constexpr basic_string_span(const basic_string_span& other) noexcept = default; // move #ifndef GSL_MSVC_NO_DEFAULT_MOVE_CTOR - constexpr basic_string_span(basic_string_span&& other) = default; + constexpr basic_string_span(basic_string_span&& other) noexcept = default; #else constexpr basic_string_span(basic_string_span&& other) : span_(std::move(other.span_)) {} #endif // assign - constexpr basic_string_span& operator=(const basic_string_span& other) = default; + constexpr basic_string_span& operator=(const basic_string_span& other) noexcept = default; // move assign #ifndef GSL_MSVC_NO_DEFAULT_MOVE_CTOR - constexpr basic_string_span& operator=(basic_string_span&& other) = default; + constexpr basic_string_span& operator=(basic_string_span&& other) noexcept = default; #else - constexpr basic_string_span& operator=(basic_string_span&& other) + constexpr basic_string_span& operator=(basic_string_span&& other) noexcept { span_ = std::move(other.span_); return *this; @@ -277,47 +276,55 @@ public: // from nullptr constexpr basic_string_span(std::nullptr_t ptr) noexcept : span_(ptr) {} - // from nullptr and length - constexpr basic_string_span(std::nullptr_t ptr, size_type length) noexcept : span_(ptr, length) - { - } + constexpr basic_string_span(pointer ptr, index_type length) : span_(ptr, length) {} + constexpr basic_string_span(pointer firstElem, pointer lastElem) : span_(firstElem, lastElem) {} // From static arrays - if 0-terminated, remove 0 from the view - - // from static arrays and string literals + // All other containers allow 0s within the length, so we do not remove them template - constexpr basic_string_span(value_type (&arr)[N]) noexcept : span_(remove_z(arr)) + constexpr basic_string_span(element_type (&arr)[N]) : span_(remove_z(arr)) + { + } + + template > + constexpr basic_string_span(std::array& arr) noexcept + : span_(arr) {} + + template > + constexpr basic_string_span(const std::array& arr) noexcept + : span_(arr) {} + + // Container signature should work for basic_string after C++17 version exists + template + constexpr basic_string_span(std::basic_string& str) + : span_(&str[0], str.length()) { } - // Those allow 0s within the length, so we do not remove them - - // from raw data and length - constexpr basic_string_span(pointer ptr, size_type length) noexcept : span_(ptr, length) {} - - // from string - constexpr basic_string_span(std::string& s) noexcept - : span_(const_cast(s.data()), narrow_cast(s.length())) + template + constexpr basic_string_span(const std::basic_string& str) + : span_(&str[0], str.length()) { } - // from containers. Containers must have .size() and .data() function signatures - template ::value && !details::is_span::value && - std::is_convertible::value && - std::is_convertible().data())>::value>> - constexpr basic_string_span(Cont& cont) : span_(cont.data(), cont.size()) + // from containers. Containers must have a pointer type and data() function signatures + template ::value && + !details::is_span::value && + std::is_convertible::value && + std::is_convertible().data())>::value>> + constexpr basic_string_span(Container& cont) : span_(cont) { } - // disallow creation from temporary containers and strings - template ::value && !details::is_span::value && - std::is_convertible::value && - std::is_convertible().data())>::value>> - constexpr basic_string_span(const Cont& cont) : span_(cont.data(), cont.size()) + template ::value && + !details::is_span::value && + std::is_convertible::value && + std::is_convertible().data())>::value>> + constexpr basic_string_span(const Container& cont) : span_(cont) { } @@ -326,109 +333,97 @@ public: template , impl_type>::value>> - constexpr basic_string_span(span other) noexcept : span_(other) + constexpr basic_string_span(const span& other) : span_(other) { } #else // from span - constexpr basic_string_span(span other) noexcept : span_(other) {} + constexpr basic_string_span(span other) : span_(other) {} template , value_type>::value>> - constexpr basic_string_span(span, Extent> other) noexcept + !std::is_same, value_type>::value>> + constexpr basic_string_span(const span, Extent>& other) : span_(other) { } #endif // from string_span - template ::impl_type, impl_type>::value>> - constexpr basic_string_span(basic_string_span other) noexcept + template ::impl_type, impl_type>::value>> + constexpr basic_string_span(basic_string_span other) : span_(other.data(), other.length()) { } - constexpr bool empty() const noexcept { return length() == 0; } - // first Count elements - template - constexpr basic_string_span first() const noexcept + template + constexpr basic_string_span first() const { return {span_.template first()}; } - constexpr basic_string_span first(size_type count) const noexcept + constexpr basic_string_span first(index_type count) const { return {span_.first(count)}; } // last Count elements - template - constexpr basic_string_span last() const noexcept + template + constexpr basic_string_span last() const { return {span_.template last()}; } - constexpr basic_string_span last(size_type count) const noexcept + constexpr basic_string_span last(index_type count) const { return {span_.last(count)}; } - // create a subview of Count elements starting from Offset - template - constexpr basic_string_span subspan() const noexcept + template + constexpr basic_string_span subspan() const { return {span_.template subspan()}; } - constexpr basic_string_span - subspan(size_type offset, size_type count = dynamic_extent) const noexcept + constexpr basic_string_span + subspan(index_type offset, index_type count = dynamic_extent) const { return {span_.subspan(offset, count)}; } - constexpr reference operator[](size_type idx) const noexcept { return span_[idx]; } + constexpr reference operator[](index_type idx) const { return span_[idx]; } + constexpr reference operator()(index_type idx) const { return span_[idx]; } - constexpr pointer data() const noexcept { return span_.data(); } + constexpr pointer data() const { return span_.data(); } - // length of the span in elements - constexpr size_type length() const noexcept { return span_.size(); } - - // length of the span in elements - constexpr size_type size() const noexcept { return span_.size(); } - - // length of the span in bytes - constexpr size_type size_bytes() const noexcept { return span_.size_bytes(); } - - // length of the span in bytes - constexpr size_type length_bytes() const noexcept { return span_.length_bytes(); } + constexpr index_type length() const noexcept { return span_.size(); } + constexpr index_type size() const noexcept { return span_.size(); } + constexpr index_type size_bytes() const noexcept { return span_.size_bytes(); } + constexpr index_type length_bytes() const noexcept { return span_.length_bytes(); } + constexpr bool empty() const noexcept { return size() == 0; } constexpr iterator begin() const noexcept { return span_.begin(); } - constexpr iterator end() const noexcept { return span_.end(); } - + constexpr const_iterator cbegin() const noexcept { return span_.cbegin(); } - constexpr const_iterator cend() const noexcept { return span_.cend(); } - + constexpr reverse_iterator rbegin() const noexcept { return span_.rbegin(); } - constexpr reverse_iterator rend() const noexcept { return span_.rend(); } constexpr const_reverse_iterator crbegin() const noexcept { return span_.crbegin(); } - constexpr const_reverse_iterator crend() const noexcept { return span_.crend(); } private: - static impl_type remove_z(pointer const& sz, std::ptrdiff_t max) noexcept + static impl_type remove_z(pointer const& sz, std::ptrdiff_t max) { - return {sz, details::length_func()(sz, max)}; + return {sz, details::length_func()(sz, max)}; } template - static impl_type remove_z(value_type (&sz)[N]) noexcept + static impl_type remove_z(element_type (&sz)[N]) { return remove_z(&sz[0], narrow_cast(N)); } @@ -453,7 +448,7 @@ using cwstring_span = basic_string_span; // #ifndef GSL_MSVC_HAS_TYPE_DEDUCTION_BUG -template +template std::basic_string::type> to_string(basic_string_span view) { @@ -559,15 +554,13 @@ using czstring_span = basic_zstring_span; template using cwzstring_span = basic_zstring_span; -} // namespace GSL - // operator == -template , Extent>>::value>> -bool operator==(gsl::basic_string_span one, const T& other) noexcept +template ::value || std::is_convertible< + T, gsl::basic_string_span>>::value>> +bool operator==(const gsl::basic_string_span& one, const T& other) noexcept { - gsl::basic_string_span, Extent> tmp(other); + gsl::basic_string_span> tmp(other); #ifdef GSL_MSVC_NO_CPP14_STD_EQUAL return (one.size() == tmp.size()) && std::equal(one.begin(), one.end(), tmp.begin()); #else @@ -576,13 +569,13 @@ bool operator==(gsl::basic_string_span one, const T& other) noexc } template < - typename CharT, std::ptrdiff_t Extent = gsl::dynamic_extent, typename T, - typename Dummy = std::enable_if_t< - std::is_convertible, Extent>>::value && - !gsl::details::is_basic_string_span::value>> -bool operator==(const T& one, gsl::basic_string_span other) noexcept + class CharT, std::ptrdiff_t Extent, class T, + class = std::enable_if_t::value && + std::is_convertible>>::value + >> +bool operator==(const T& one, const gsl::basic_string_span& other) noexcept { - gsl::basic_string_span, Extent> tmp(one); + gsl::basic_string_span> tmp(one); #ifdef GSL_MSVC_NO_CPP14_STD_EQUAL return (tmp.size() == other.size()) && std::equal(tmp.begin(), tmp.end(), other.begin()); #else @@ -590,40 +583,6 @@ bool operator==(const T& one, gsl::basic_string_span other) noexc #endif } -#ifndef _MSC_VER - -// VS treats temp and const containers as convertible to basic_string_span, -// so the cases below are already covered by the previous operators - -template < - typename CharT, std::ptrdiff_t Extent = gsl::dynamic_extent, typename T, - typename DataType = typename T::value_type, - typename Dummy = std::enable_if_t< - !gsl::details::is_span::value && !gsl::details::is_basic_string_span::value && - std::is_convertible::value && - std::is_same().size(), *std::declval().data())>, - DataType>::value>> -bool operator==(gsl::basic_string_span one, const T& other) noexcept -{ - gsl::basic_string_span, Extent> tmp(other); - return std::equal(one.begin(), one.end(), tmp.begin(), tmp.end()); -} - -template < - typename CharT, std::ptrdiff_t Extent = gsl::dynamic_extent, typename T, - typename DataType = typename T::value_type, - typename Dummy = std::enable_if_t< - !gsl::details::is_span::value && !gsl::details::is_basic_string_span::value && - std::is_convertible::value && - std::is_same().size(), *std::declval().data())>, - DataType>::value>> -bool operator==(const T& one, gsl::basic_string_span other) noexcept -{ - gsl::basic_string_span, Extent> tmp(one); - return std::equal(tmp.begin(), tmp.end(), other.begin(), other.end()); -} -#endif - // operator != template other) noexc return !(one == other); } -#ifndef _MSC_VER - -// VS treats temp and const containers as convertible to basic_string_span, -// so the cases below are already covered by the previous operators - -template < - typename CharT, std::ptrdiff_t Extent = gsl::dynamic_extent, typename T, - typename DataType = typename T::value_type, - typename Dummy = std::enable_if_t< - !gsl::details::is_span::value && !gsl::details::is_basic_string_span::value && - std::is_convertible::value && - std::is_same().size(), *std::declval().data())>, - DataType>::value>> -bool operator!=(gsl::basic_string_span one, const T& other) noexcept -{ - return !(one == other); -} - -template < - typename CharT, std::ptrdiff_t Extent = gsl::dynamic_extent, typename T, - typename DataType = typename T::value_type, - typename Dummy = std::enable_if_t< - !gsl::details::is_span::value && !gsl::details::is_basic_string_span::value && - std::is_convertible::value && - std::is_same().size(), *std::declval().data())>, - DataType>::value>> -bool operator!=(const T& one, gsl::basic_string_span other) noexcept -{ - return !(one == other); -} -#endif - // operator< template =(const T& one, gsl::basic_string_span other) noexc return !(one < other); } #endif +} // namespace GSL #ifdef _MSC_VER diff --git a/tests/string_span_tests.cpp b/tests/string_span_tests.cpp index f380be3..876886a 100644 --- a/tests/string_span_tests.cpp +++ b/tests/string_span_tests.cpp @@ -40,14 +40,14 @@ SUITE(string_span_tests) { std::string s = "Hello there world"; cstring_span<> v = s; - CHECK(v.length() == static_cast::size_type>(s.length())); + CHECK(v.length() == static_cast::index_type>(s.length())); } TEST(TestConstructFromStdVector) { std::vector vec(5, 'h'); string_span<> v {vec}; - CHECK(v.length() == static_cast::size_type>(vec.size())); + CHECK(v.length() == static_cast::index_type>(vec.size())); } TEST(TestStackArrayConstruction) @@ -109,7 +109,7 @@ SUITE(string_span_tests) char stack_string[] = "Hello"; cstring_span<> v = ensure_z(stack_string); auto s2 = gsl::to_string(v); - CHECK(static_cast::size_type>(s2.length()) == v.length()); + CHECK(static_cast::index_type>(s2.length()) == v.length()); CHECK(s2.length() == 5); }