// https://github.com/tcbrindle/span/blob/master/include/tcb/span.hpp // TCP SPAN @commit cd0c6d0 /* This is an implementation of std::span from P0122R7 http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2018/p0122r7.pdf */ // Copyright Tristan Brindle 2018. // Distributed under the Boost Software License, Version 1.0. // (See accompanying file ../../LICENSE_1_0.txt or copy at // https://www.boost.org/LICENSE_1_0.txt) #ifndef TCB_SPAN_HPP_INCLUDED #define TCB_SPAN_HPP_INCLUDED #include #include #include #include #ifndef TCB_SPAN_NO_EXCEPTIONS // Attempt to discover whether we're being compiled with exception support #if !(defined(__cpp_exceptions) || defined(__EXCEPTIONS) || defined(_CPPUNWIND)) #define TCB_SPAN_NO_EXCEPTIONS #endif #endif #ifndef TCB_SPAN_NO_EXCEPTIONS #include #include #endif // Various feature test macros #ifndef TCB_SPAN_NAMESPACE_NAME #define TCB_SPAN_NAMESPACE_NAME tcb #endif #ifdef TCB_SPAN_STD_COMPLIANT_MODE #define TCB_SPAN_NO_DEPRECATION_WARNINGS #endif #ifndef TCB_SPAN_NO_DEPRECATION_WARNINGS #define TCB_SPAN_DEPRECATED_FOR(msg) [[deprecated(msg)]] #else #define TCB_SPAN_DEPRECATED_FOR(msg) #endif #if __cplusplus >= 201703L || (defined(_MSVC_LANG) && _MSVC_LANG >= 201703L) #define TCB_SPAN_HAVE_CPP17 #endif #if __cplusplus >= 201402L || (defined(_MSVC_LANG) && _MSVC_LANG >= 201402L) #define TCB_SPAN_HAVE_CPP14 #endif namespace TCB_SPAN_NAMESPACE_NAME { // Establish default contract checking behavior #if !defined(TCB_SPAN_THROW_ON_CONTRACT_VIOLATION) && \ !defined(TCB_SPAN_TERMINATE_ON_CONTRACT_VIOLATION) && \ !defined(TCB_SPAN_NO_CONTRACT_CHECKING) #if defined(NDEBUG) || !defined(TCB_SPAN_HAVE_CPP14) #define TCB_SPAN_NO_CONTRACT_CHECKING #else #define TCB_SPAN_TERMINATE_ON_CONTRACT_VIOLATION #endif #endif #if defined(TCB_SPAN_THROW_ON_CONTRACT_VIOLATION) struct contract_violation_error : std::logic_error { explicit contract_violation_error(const char* msg) : std::logic_error(msg) {} }; inline void contract_violation(const char* msg) { throw contract_violation_error(msg); } #elif defined(TCB_SPAN_TERMINATE_ON_CONTRACT_VIOLATION) [[noreturn]] inline void contract_violation(const char* /*unused*/) { std::terminate(); } #endif #if !defined(TCB_SPAN_NO_CONTRACT_CHECKING) #define TCB_SPAN_STRINGIFY(cond) #cond #define TCB_SPAN_EXPECT(cond) \ cond ? (void) 0 : contract_violation("Expected " TCB_SPAN_STRINGIFY(cond)) #else #define TCB_SPAN_EXPECT(cond) #endif #if defined(TCB_SPAN_HAVE_CPP17) || defined(__cpp_inline_variables) #define TCB_SPAN_INLINE_VAR inline #else #define TCB_SPAN_INLINE_VAR #endif #if defined(TCB_SPAN_HAVE_CPP14) || \ (defined(__cpp_constexpr) && __cpp_constexpr >= 201304) #define TCB_SPAN_CONSTEXPR14 constexpr #else #define TCB_SPAN_CONSTEXPR14 #endif #if defined(TCB_SPAN_NO_CONTRACT_CHECKING) #define TCB_SPAN_CONSTEXPR11 constexpr #else #define TCB_SPAN_CONSTEXPR11 TCB_SPAN_CONSTEXPR14 #endif #if defined(TCB_SPAN_HAVE_CPP17) || defined(__cpp_deduction_guides) #define TCB_SPAN_HAVE_DEDUCTION_GUIDES #endif #if defined(TCB_SPAN_HAVE_CPP17) || defined(__cpp_lib_byte) && !(defined(_HAS_STD_BYTE) && !_HAS_STD_BYTE) #define TCB_SPAN_HAVE_STD_BYTE #endif #if defined(TCB_SPAN_HAVE_CPP17) || defined(__cpp_lib_array_constexpr) #define TCB_SPAN_HAVE_CONSTEXPR_STD_ARRAY_ETC #endif #if defined(TCB_SPAN_HAVE_CONSTEXPR_STD_ARRAY_ETC) #define TCB_SPAN_ARRAY_CONSTEXPR constexpr #else #define TCB_SPAN_ARRAY_CONSTEXPR #endif #ifdef TCB_SPAN_HAVE_STD_BYTE using byte = std::byte; #else using byte = unsigned char; #endif TCB_SPAN_INLINE_VAR constexpr std::ptrdiff_t dynamic_extent = -1; template class span; namespace detail { template struct span_storage { constexpr span_storage() noexcept = default; constexpr span_storage(E* aptr, std::ptrdiff_t /*unused*/) noexcept : ptr(aptr) {} E* ptr = nullptr; static constexpr std::ptrdiff_t size = S; }; template struct span_storage { constexpr span_storage() noexcept = default; constexpr span_storage(E* aptr, std::size_t asize) noexcept : ptr(aptr), size(asize) {} E* ptr = nullptr; std::size_t size = 0; }; // Reimplementation of C++17 std::size() and std::data() #if defined(TCB_SPAN_HAVE_CPP17) || \ defined(__cpp_lib_nonmember_container_access) using std::data; using std::size; #else template constexpr auto size(const C& c) -> decltype(c.size()) { return c.size(); } template constexpr std::size_t size(const T (&)[N]) noexcept { return N; } template constexpr auto data(C& c) -> decltype(c.data()) { return c.data(); } template constexpr auto data(const C& c) -> decltype(c.data()) { return c.data(); } template constexpr T* data(T (&array)[N]) noexcept { return array; } template constexpr const E* data(std::initializer_list il) noexcept { return il.begin(); } #endif // TCB_SPAN_HAVE_CPP17 #if defined(TCB_SPAN_HAVE_CPP17) || defined(__cpp_lib_void_t) using std::void_t; #else template using void_t = void; #endif template using uncvref_t = typename std::remove_cv::type>::type; template struct is_span : std::false_type {}; template struct is_span> : std::true_type {}; template struct is_std_array : std::false_type {}; template struct is_std_array> : std::true_type {}; template struct has_size_and_data : std::false_type {}; template struct has_size_and_data())), decltype(detail::data(std::declval()))>> : std::true_type {}; template > struct is_container { static constexpr bool value = !is_span::value && !is_std_array::value && !std::is_array::value && has_size_and_data::value; }; template using remove_pointer_t = typename std::remove_pointer::type; template struct is_container_element_type_compatible : std::false_type {}; template struct is_container_element_type_compatible< T, E, void_t()))>> : std::is_convertible< remove_pointer_t()))> (*)[], E (*)[]> {}; template struct is_complete : std::false_type {}; template struct is_complete : std::true_type {}; } // namespace detail template class span { static_assert(Extent == dynamic_extent || Extent >= 0, "A span must have an extent greater than or equal to zero, " "or a dynamic extent"); static_assert(std::is_object::value, "A span's ElementType must be an object type (not a " "reference type or void)"); static_assert(detail::is_complete::value, "A span's ElementType must be a complete type (not a forward " "declaration)"); static_assert(!std::is_abstract::value, "A span's ElementType cannot be an abstract class type"); using storage_type = detail::span_storage; public: // constants and types using element_type = ElementType; using value_type = typename std::remove_cv::type; using index_type = std::size_t; using difference_type = std::ptrdiff_t; using pointer = ElementType*; using reference = ElementType&; using iterator = pointer; using const_iterator = const ElementType*; using reverse_iterator = std::reverse_iterator; using const_reverse_iterator = std::reverse_iterator; static constexpr index_type extent = static_cast(Extent); // [span.cons], span constructors, copy, assignment, and destructor template ::type = 0> constexpr span() noexcept {} TCB_SPAN_CONSTEXPR11 span(pointer ptr, index_type count) : storage_(ptr, count) { TCB_SPAN_EXPECT(extent == dynamic_extent || count == extent); } TCB_SPAN_CONSTEXPR11 span(pointer first_elem, pointer last_elem) : storage_(first_elem, last_elem - first_elem) { TCB_SPAN_EXPECT(extent == dynamic_extent || last_elem - first_elem == extent); } template < std::size_t N, std::ptrdiff_t E = Extent, typename std::enable_if< (E == dynamic_extent || static_cast(N) == E) && detail::is_container_element_type_compatible< element_type (&)[N], ElementType>::value, int>::type = 0> constexpr span(element_type (&arr)[N]) noexcept : storage_(arr, N) {} template < std::size_t N, std::ptrdiff_t E = Extent, typename std::enable_if< (E == dynamic_extent || static_cast(N) == E) && detail::is_container_element_type_compatible< std::array&, ElementType>::value, int>::type = 0> TCB_SPAN_ARRAY_CONSTEXPR span(std::array& arr) noexcept : storage_(arr.data(), N) {} template < std::size_t N, std::ptrdiff_t E = Extent, typename std::enable_if< (E == dynamic_extent || static_cast(N) == E) && detail::is_container_element_type_compatible< const std::array&, ElementType>::value, int>::type = 0> TCB_SPAN_ARRAY_CONSTEXPR span(const std::array& arr) noexcept : storage_(arr.data(), N) {} template ::value && detail::is_container_element_type_compatible< Container&, ElementType>::value, int>::type = 0> TCB_SPAN_CONSTEXPR11 span(Container& cont) : storage_(detail::data(cont), detail::size(cont)) { TCB_SPAN_EXPECT(extent == dynamic_extent || static_cast(detail::size(cont)) == extent); } template ::value && detail::is_container_element_type_compatible< const Container&, ElementType>::value, int>::type = 0> TCB_SPAN_CONSTEXPR11 span(const Container& cont) : storage_(detail::data(cont), detail::size(cont)) { TCB_SPAN_EXPECT(extent == dynamic_extent || static_cast(detail::size(cont)) == extent); } constexpr span(const span& other) noexcept = default; template ::value, int>::type = 0> constexpr span(const span& other) noexcept : storage_(other.data(), other.size()) {} ~span() noexcept = default; span& operator=(const span& other) noexcept = default; // [span.sub], span subviews template TCB_SPAN_CONSTEXPR11 span first() const { TCB_SPAN_EXPECT(Count >= 0 && Count <= size()); return {data(), Count}; } template TCB_SPAN_CONSTEXPR11 span last() const { TCB_SPAN_EXPECT(Count >= 0 && Count <= size()); return {data() + (size() - Count), Count}; } template using subspan_return_t = span; template TCB_SPAN_CONSTEXPR11 subspan_return_t subspan() const { TCB_SPAN_EXPECT((Offset >= 0 && Offset <= size()) && (Count == dynamic_extent || (Count >= 0 && Offset + Count <= size()))); return {data() + Offset, Count != dynamic_extent ? Count : (Extent != dynamic_extent ? Extent - Offset : size() - Offset)}; } TCB_SPAN_CONSTEXPR11 span first(index_type count) const { TCB_SPAN_EXPECT(count >= 0 && count <= size()); return {data(), count}; } TCB_SPAN_CONSTEXPR11 span last(index_type count) const { TCB_SPAN_EXPECT(count >= 0 && count <= size()); return {data() + (size() - count), count}; } TCB_SPAN_CONSTEXPR11 span subspan(index_type offset, index_type count = static_cast(dynamic_extent)) const { TCB_SPAN_EXPECT((offset >= 0 && offset <= size()) && (count == dynamic_extent || (count >= 0 && offset + count <= size()))); return {data() + offset, count == dynamic_extent ? size() - offset : count}; } // [span.obs], span observers constexpr index_type size() const noexcept { return storage_.size; } constexpr index_type size_bytes() const noexcept { return size() * sizeof(element_type); } constexpr bool empty() const noexcept { return size() == 0; } // [span.elem], span element access TCB_SPAN_CONSTEXPR11 reference operator[](index_type idx) const { TCB_SPAN_EXPECT(idx >= 0 && idx < size()); return *(data() + idx); } /* Extension: not in P0122 */ #ifndef TCB_SPAN_STD_COMPLIANT_MODE TCB_SPAN_CONSTEXPR14 reference at(index_type idx) const { #ifndef TCB_SPAN_NO_EXCEPTIONS if (idx < 0 || idx >= size()) { char msgbuf[64] = { 0, }; std::snprintf(msgbuf, sizeof(msgbuf), "Index %td is out of range for span of size %td", idx, size()); throw std::out_of_range{msgbuf}; } #endif // TCB_SPAN_NO_EXCEPTIONS return this->operator[](idx); } TCB_SPAN_CONSTEXPR11 reference front() const { TCB_SPAN_EXPECT(!empty()); return *data(); } TCB_SPAN_CONSTEXPR11 reference back() const { TCB_SPAN_EXPECT(!empty()); return *(data() + (size() - 1)); } #endif // TCB_SPAN_STD_COMPLIANT_MODE #ifndef TCB_SPAN_NO_FUNCTION_CALL_OPERATOR TCB_SPAN_DEPRECATED_FOR("Use operator[] instead") constexpr reference operator()(index_type idx) const { return this->operator[](idx); } #endif // TCB_SPAN_NO_FUNCTION_CALL_OPERATOR constexpr pointer data() const noexcept { return storage_.ptr; } // [span.iterators], span iterator support constexpr iterator begin() const noexcept { return data(); } constexpr iterator end() const noexcept { return data() + size(); } constexpr const_iterator cbegin() const noexcept { return begin(); } constexpr const_iterator cend() const noexcept { return end(); } TCB_SPAN_ARRAY_CONSTEXPR reverse_iterator rbegin() const noexcept { return reverse_iterator(end()); } TCB_SPAN_ARRAY_CONSTEXPR reverse_iterator rend() const noexcept { return reverse_iterator(begin()); } TCB_SPAN_ARRAY_CONSTEXPR const_reverse_iterator crbegin() const noexcept { return const_reverse_iterator(cend()); } TCB_SPAN_ARRAY_CONSTEXPR const_reverse_iterator crend() const noexcept { return const_reverse_iterator(cbegin()); } private: storage_type storage_{}; }; #ifdef TCB_SPAN_HAVE_DEDUCTION_GUIDES /* Deduction Guides */ template span(T (&)[N])->span; template span(std::array&)->span; template span(const std::array&)->span; template span(Container&)->span; template span(const Container&)->span; #endif // TCB_HAVE_DEDUCTION_GUIDES template constexpr span make_span(span s) noexcept { return s; } #define AS_SIGNED(N) static_cast(N) template constexpr span make_span(T (&arr)[N]) noexcept { return {arr}; } template TCB_SPAN_ARRAY_CONSTEXPR span make_span(std::array& arr) noexcept { return {arr}; } template TCB_SPAN_ARRAY_CONSTEXPR span make_span(const std::array& arr) noexcept { return {arr}; } #undef AS_SIGNED template constexpr span make_span(Container& cont) { return {cont}; } template constexpr span make_span(const Container& cont) { return {cont}; } /* Comparison operators */ // Implementation note: the implementations of == and < are equivalent to // 4-legged std::equal and std::lexicographical_compare respectively template TCB_SPAN_CONSTEXPR14 bool operator==(span lhs, span rhs) { if (lhs.size() != rhs.size()) { return false; } for (std::ptrdiff_t i = 0; i < lhs.size(); i++) { if (lhs[i] != rhs[i]) { return false; } } return true; } template TCB_SPAN_CONSTEXPR14 bool operator!=(span lhs, span rhs) { return !(lhs == rhs); } template TCB_SPAN_CONSTEXPR14 bool operator<(span lhs, span rhs) { // No std::min to avoid dragging in const std::ptrdiff_t size = lhs.size() < rhs.size() ? lhs.size() : rhs.size(); for (std::ptrdiff_t i = 0; i < size; i++) { if (lhs[i] < rhs[i]) { return true; } if (lhs[i] > rhs[i]) { return false; } } return lhs.size() < rhs.size(); } template TCB_SPAN_CONSTEXPR14 bool operator<=(span lhs, span rhs) { return !(rhs < lhs); } template TCB_SPAN_CONSTEXPR14 bool operator>(span lhs, span rhs) { return rhs < lhs; } template TCB_SPAN_CONSTEXPR14 bool operator>=(span lhs, span rhs) { return !(lhs < rhs); } template span(sizeof(ElementType)) * Extent))> as_bytes(span s) noexcept { return {reinterpret_cast(s.data()), s.size_bytes()}; } template < class ElementType, ptrdiff_t Extent, typename std::enable_if::value, int>::type = 0> span(sizeof(ElementType)) * Extent))> as_writable_bytes(span s) noexcept { return {reinterpret_cast(s.data()), s.size_bytes()}; } /* Extension: nonmember subview operations */ #ifndef TCB_SPAN_STD_COMPLIANT_MODE template TCB_SPAN_CONSTEXPR11 auto first(T& t) -> decltype(make_span(t).template first()) { return make_span(t).template first(); } template TCB_SPAN_CONSTEXPR11 auto last(T& t) -> decltype(make_span(t).template last()) { return make_span(t).template last(); } template TCB_SPAN_CONSTEXPR11 auto subspan(T& t) -> decltype(make_span(t).template subspan()) { return make_span(t).template subspan(); } template TCB_SPAN_CONSTEXPR11 auto first(T& t, std::ptrdiff_t count) -> decltype(make_span(t).first(count)) { return make_span(t).first(count); } template TCB_SPAN_CONSTEXPR11 auto last(T& t, std::ptrdiff_t count) -> decltype(make_span(t).last(count)) { return make_span(t).last(count); } template TCB_SPAN_CONSTEXPR11 auto subspan(T& t, std::ptrdiff_t offset, std::ptrdiff_t count = dynamic_extent) -> decltype(make_span(t).subspan(offset, count)) { return make_span(t).subspan(offset, count); } #endif // TCB_SPAN_STD_COMPLIANT_MODE } // namespace TCB_SPAN_NAMESPACE_NAME /* Extension: support for C++17 structured bindings */ #ifndef TCB_SPAN_STD_COMPLIANT_MODE namespace TCB_SPAN_NAMESPACE_NAME { template constexpr auto get(span s) -> decltype(s[N]) { return s[N]; } } // namespace TCB_SPAN_NAMESPACE_NAME namespace std { template class tuple_size> : public integral_constant(S)> {}; template class tuple_size>; // not defined template class tuple_element> { public: using type = E; }; } // end namespace std #endif // TCB_SPAN_STD_COMPLIANT_MODE #endif // TCB_SPAN_HPP_INCLUDED