/** * @file libimp/span.h * @author mutouyun (orz@orzz.org) * @brief Describes an object that can refer to a contiguous sequence of objects * @date 2022-10-16 */ #pragma once #include #include #include #include #include #include #include #include #include #include #include "libimp/def.h" #include "libimp/detect_plat.h" #if defined(LIBIMP_CPP_20) && defined(__cpp_lib_span) #include #define LIBIMP_CPP_LIB_SPAN_ #endif // __cpp_lib_span LIBIMP_NAMESPACE_BEG_ namespace detail { /// @brief Helper trait for span. template using array_convertible = std::is_convertible; template using is_array_convertible = typename std::enable_if::value>::type; template using compatible_ref = array_convertible::type, T>; template using iter_reference_t = decltype(*std::declval()); template using is_compatible_iter = typename std::enable_if>::value>::type; template using is_inconvertible = typename std::enable_if::value>::type; template using is_sized_sentinel_for = typename std::enable_if() - std::declval()), std::ptrdiff_t>::value>::type; /// @brief Obtain the address represented by p /// without forming a reference to the object pointed to by p. /// @see https://en.cppreference.com/w/cpp/memory/to_address template constexpr T *to_address(T *ptr) noexcept { static_assert(!std::is_function::value, "ptr shouldn't a function pointer"); return ptr; } template constexpr auto to_address(T const &ptr) noexcept(noexcept(ptr.operator->())) -> decltype(ptr.operator->()) { return to_address(ptr.operator->()); } } // namespace detail /** * @brief A simple implementation of span. * @see https://en.cppreference.com/w/cpp/container/span */ template class span { public: using element_type = T; using value_type = typename std::remove_cv::type; using size_type = std::size_t; using difference_type = std::ptrdiff_t; using pointer = element_type *; using const_pointer = typename std::remove_const::type const *; using reference = element_type &; using const_reference = typename std::remove_const::type const &; using iterator = pointer; using reverse_iterator = std::reverse_iterator; private: pointer ptr_ {nullptr}; size_type extent_ {0}; public: constexpr span() noexcept = default; constexpr span(span const &) noexcept = default; #if (LIBIMP_CC_MSVC > LIBIMP_CC_MSVC_2015) constexpr #endif span & operator=(span const &) noexcept = default; template > constexpr span(It first, size_type count) noexcept : ptr_ (detail::to_address(first)) , extent_(count) {} template , typename = detail::is_sized_sentinel_for, typename = detail::is_inconvertible> constexpr span(It first, End last) noexcept(noexcept(last - first)) : ptr_ (detail::to_address(first)) , extent_(static_cast(last - first)) {} template > constexpr span(U (&arr)[E]) noexcept : span(static_cast(arr), E) {} template > constexpr span(std::array &arr) noexcept : span(static_cast(arr.data()), E) {} template ::type, element_type>> constexpr span(std::array const &arr) noexcept : span(static_cast(arr.data()), E) {} template > constexpr span(span const &s) noexcept : ptr_ (s.data()) , extent_(s.size()) {} #ifdef LIBIMP_CPP_LIB_SPAN_ template > constexpr span(std::span const &s) noexcept : ptr_ (s.data()) , extent_(s.size()) {} #endif // LIBIMP_CPP_LIB_SPAN_ constexpr size_type size() const noexcept { return extent_; } constexpr size_type size_bytes() const noexcept { return size() * sizeof(element_type); } constexpr bool empty() const noexcept { return size() == 0; } constexpr pointer data() const noexcept { return this->ptr_; } constexpr reference front() const noexcept { return *data(); } constexpr reference back() const noexcept { return *(data() + (size() - 1)); } constexpr reference operator[](size_type idx) const noexcept { return *(data() + idx); } constexpr iterator begin() const noexcept { return iterator(data()); } constexpr iterator end() const noexcept { return iterator(data() + this->size()); } constexpr reverse_iterator rbegin() const noexcept { return reverse_iterator(this->end()); } constexpr reverse_iterator rend() const noexcept { return reverse_iterator(this->begin()); } constexpr span first(size_type count) const noexcept { return span(begin(), count); } constexpr span last(size_type count) const noexcept { return span(end() - count, count); } constexpr span subspan(size_type offset, size_type count = (std::numeric_limits::max)()) const noexcept { return (offset >= size()) ? span() : span(begin() + offset, (std::min)(size() - offset, count)); } }; /// @brief Support for span equals comparison. template () == std::declval())> bool operator==(span a, span b) noexcept { if (a.size() != b.size()) { return false; } for (std::size_t i = 0; i < a.size(); ++i) { if (a[i] != b[i]) return false; } return true; } /// @brief Constructs an object of type T and wraps it in a span. /// Before C++17, template argument deduction for class templates was not supported. /// @see https://en.cppreference.com/w/cpp/language/template_argument_deduction template auto make_span(T *arr, std::size_t count) noexcept -> span { return {arr, count}; } template auto make_span(T (&arr)[E]) noexcept -> span { return {arr}; } template auto make_span(std::array &arr) noexcept -> span { return {arr}; } template auto make_span(std::array const &arr) noexcept -> span::type> { return {arr}; } template auto make_span(std::vector &arr) noexcept -> span { return {arr.data(), arr.size()}; } template auto make_span(std::vector const &arr) noexcept -> span::type> { return {arr.data(), arr.size()}; } template auto make_span(std::initializer_list list) noexcept -> span::type> { return {list.begin(), list.end()}; } inline auto make_span(std::string &str) noexcept -> span { return {const_cast(str.data()), str.size()}; } inline auto make_span(std::string const &str) noexcept -> span { return {str.data(), str.size()}; } LIBIMP_NAMESPACE_END_