From 22da9e41ddb75663e8fd08cbb22da0cedf171017 Mon Sep 17 00:00:00 2001 From: Patrick Roberts Date: Fri, 7 Feb 2025 14:28:17 -0600 Subject: [PATCH] Add virtual polymorphic view and iterator classes --- CMakeLists.txt | 8 +- CMakePresets.json | 1 - include/beman/any_view/any_view.hpp | 69 ++++++- include/beman/any_view/concepts.hpp | 48 +---- include/beman/any_view/detail/concepts.hpp | 31 +++ .../any_view/detail/intrusive_small_ptr.hpp | 47 +++-- include/beman/any_view/detail/iterator.hpp | 188 +++++++++++++++--- .../any_view/detail/iterator_adaptor.hpp | 137 +++++++++++++ .../any_view/detail/iterator_interface.hpp | 55 +++++ .../beman/any_view/detail/view_adaptor.hpp | 81 ++++++++ .../beman/any_view/detail/view_interface.hpp | 33 +++ 11 files changed, 593 insertions(+), 105 deletions(-) create mode 100644 include/beman/any_view/detail/iterator_adaptor.hpp create mode 100644 include/beman/any_view/detail/iterator_interface.hpp create mode 100644 include/beman/any_view/detail/view_adaptor.hpp create mode 100644 include/beman/any_view/detail/view_interface.hpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 2727ae9..baeec62 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -70,12 +70,8 @@ include(GNUInstallDirs) add_library(beman.any_view INTERFACE) add_library(beman::any_view ALIAS beman.any_view) set_target_properties(beman.any_view PROPERTIES CXX_EXTENSIONS OFF) -target_compile_features(beman.any_view INTERFACE cxx_std_20) -# TODO: implement non-template friend function and remove this suppression -target_compile_options( - beman.any_view - INTERFACE $<$:-Wno-non-template-friend> -) +# TODO: discuss removal of std::unreachable() to revert to C++20 +target_compile_features(beman.any_view INTERFACE cxx_std_23) target_include_directories( beman.any_view INTERFACE diff --git a/CMakePresets.json b/CMakePresets.json index fd69b21..ef417b3 100644 --- a/CMakePresets.json +++ b/CMakePresets.json @@ -7,7 +7,6 @@ "generator": "Ninja", "binaryDir": "${sourceDir}/build/${presetName}", "cacheVariables": { - "CMAKE_CXX_STANDARD": "20", "CMAKE_EXPORT_COMPILE_COMMANDS": true } }, diff --git a/include/beman/any_view/any_view.hpp b/include/beman/any_view/any_view.hpp index c526d48..042e5a1 100644 --- a/include/beman/any_view/any_view.hpp +++ b/include/beman/any_view/any_view.hpp @@ -5,7 +5,10 @@ #include #include +#include #include +#include +#include #if BEMAN_ANY_VIEW_USE_FLAGS() @@ -54,9 +57,28 @@ class any_view : public std::ranges::view_interface; + // inplace storage sufficient for a vtable pointer and a std::vector + detail::intrusive_small_ptr view_ptr; + + template + using adaptor_type = + detail::view_adaptor>; + + template + static constexpr auto get_noexcept() { + return std::is_nothrow_constructible_v, std::views::all_t>; + } + + template + static consteval auto get_in_place_adaptor_type() { + return std::in_place_type>; + } + public: template RangeT> - constexpr any_view(RangeT&&); + constexpr any_view(RangeT&& range) noexcept(get_noexcept()) + : view_ptr(get_in_place_adaptor_type(), std::views::all(std::forward(range))) {} constexpr any_view(const any_view&) requires copyable @@ -71,15 +93,24 @@ class any_view : public std::ranges::view_interface any_view& = default; [[nodiscard]] constexpr auto begin() -> iterator - requires(not simple); + requires(not simple) + { + return view_ptr->begin(); + } [[nodiscard]] constexpr auto begin() const -> iterator - requires simple; + requires simple + { + return view_ptr->begin(); + } [[nodiscard]] constexpr auto end() const { return std::default_sentinel; } [[nodiscard]] constexpr auto size() const -> size_type - requires sized; + requires sized + { + return view_ptr->size(); + } }; #elif BEMAN_ANY_VIEW_USE_TRAITS() @@ -120,15 +151,24 @@ class any_view : public std::ranges::view_interface any_view& = default; [[nodiscard]] constexpr auto begin() -> iterator - requires(not simple); + requires(not simple) + { + return view_ptr->begin(); + } [[nodiscard]] constexpr auto begin() const -> iterator - requires simple; + requires simple + { + return view_ptr->begin(); + } [[nodiscard]] constexpr auto end() const { return std::default_sentinel; } [[nodiscard]] constexpr auto size() const -> size_type - requires sized; + requires sized + { + return view_ptr->size(); + } }; #elif BEMAN_ANY_VIEW_USE_NAMED() @@ -168,15 +208,24 @@ class any_view : public std::ranges::view_interface constexpr auto operator=(any_view&&) noexcept -> any_view& = default; [[nodiscard]] constexpr auto begin() -> iterator - requires(not simple); + requires(not simple) + { + return view_ptr->begin(); + } [[nodiscard]] constexpr auto begin() const -> iterator - requires simple; + requires simple + { + return view_ptr->begin(); + } [[nodiscard]] constexpr auto end() const { return std::default_sentinel; } [[nodiscard]] constexpr auto size() const -> size_type - requires sized; + requires sized + { + return view_ptr->size(); + } }; #endif diff --git a/include/beman/any_view/concepts.hpp b/include/beman/any_view/concepts.hpp index 5e6215a..859330f 100644 --- a/include/beman/any_view/concepts.hpp +++ b/include/beman/any_view/concepts.hpp @@ -3,42 +3,20 @@ #ifndef BEMAN_ANY_VIEW_CONCEPTS_HPP #define BEMAN_ANY_VIEW_CONCEPTS_HPP +#include #include -#include namespace beman::any_view { namespace detail { -template -concept input_iterator_compatible_with = - std::input_or_output_iterator and - (not std::derived_from or std::input_iterator); - -template -concept forward_iterator_compatible_with = - input_iterator_compatible_with and - (not std::derived_from or std::forward_iterator); - -template -concept bidirectional_iterator_compatible_with = - forward_iterator_compatible_with and - (not std::derived_from or std::bidirectional_iterator); - -template -concept random_access_iterator_compatible_with = - bidirectional_iterator_compatible_with and - (not std::derived_from or std::random_access_iterator); - -template -concept contiguous_iterator_compatible_with = - random_access_iterator_compatible_with and - (not std::derived_from or std::contiguous_iterator); - -template -concept contiguous_reference_convertible_to = - std::convertible_to and - (not std::derived_from or - std::convertible_to (*)[], std::remove_reference_t (*)[]>); +template +concept iterator_compatible_with = + contiguous_iterator_compatible_with> and + contiguous_reference_convertible_to, + reference_type_t, + iterator_concept_t> and + std::convertible_to, rvalue_reference_type_t> and + std::convertible_to, difference_type_t>; template concept sized_range_compatible_with = not RangeTraitsT::sized or std::ranges::sized_range; @@ -48,13 +26,7 @@ concept borrowed_range_compatible_with = not RangeTraitsT::borrowed or std::rang template concept range_compatible_with = - std::ranges::range and - contiguous_iterator_compatible_with, iterator_concept_t> and - contiguous_reference_convertible_to, - reference_type_t, - iterator_concept_t> and - std::convertible_to, rvalue_reference_type_t> and - std::convertible_to, difference_type_t> and + std::ranges::range and iterator_compatible_with, RangeTraitsT> and sized_range_compatible_with and borrowed_range_compatible_with; template diff --git a/include/beman/any_view/detail/concepts.hpp b/include/beman/any_view/detail/concepts.hpp index 194cb4c..cb5b9fc 100644 --- a/include/beman/any_view/detail/concepts.hpp +++ b/include/beman/any_view/detail/concepts.hpp @@ -14,6 +14,37 @@ concept simple_range = std::ranges::range and std::ranges::range, std::ranges::iterator_t> and std::same_as, std::ranges::sentinel_t>; +template +concept input_iterator_compatible_with = + std::input_or_output_iterator and + (not std::derived_from or std::input_iterator); + +template +concept forward_iterator_compatible_with = + input_iterator_compatible_with and + (not std::derived_from or std::forward_iterator); + +template +concept bidirectional_iterator_compatible_with = + forward_iterator_compatible_with and + (not std::derived_from or std::bidirectional_iterator); + +template +concept random_access_iterator_compatible_with = + bidirectional_iterator_compatible_with and + (not std::derived_from or std::random_access_iterator); + +template +concept contiguous_iterator_compatible_with = + random_access_iterator_compatible_with and + (not std::derived_from or std::contiguous_iterator); + +template +concept contiguous_reference_convertible_to = + std::convertible_to and + (not std::derived_from or + std::convertible_to (*)[], std::remove_reference_t (*)[]>); + } // namespace beman::any_view::detail #endif // BEMAN_ANY_VIEW_DETAIL_CONCEPTS_HPP diff --git a/include/beman/any_view/detail/intrusive_small_ptr.hpp b/include/beman/any_view/detail/intrusive_small_ptr.hpp index b235318..2bb2087 100644 --- a/include/beman/any_view/detail/intrusive_small_ptr.hpp +++ b/include/beman/any_view/detail/intrusive_small_ptr.hpp @@ -23,9 +23,6 @@ concept interface_copyable = interface_movable and requires(const In { instance.copy() } -> std::same_as; }; -template -using value_type_t = T::value_type; - enum class index_type : bool { is_inplace, is_pointer, @@ -33,22 +30,23 @@ enum class index_type : bool { template class intrusive_small_ptr { - struct alignas(AlignV) inplace_type { - std::byte data[SizeV]; + struct inplace_type { + alignas(AlignV) std::byte data[SizeV]; }; using pointer_type = InterfaceT*; union { - // mutable inplace storage allows "shallow const" semantics consistent with a pointer type - mutable inplace_type inplace; - pointer_type pointer; + inplace_type inplace; + pointer_type pointer; }; index_type index; - [[nodiscard]] auto get_inplace_ptr() const -> pointer_type { - return static_cast(static_cast(&inplace)); + [[nodiscard]] auto get_inplace() noexcept { return static_cast(static_cast(&inplace)); } + + [[nodiscard]] auto get_inplace() const noexcept { + return static_cast(static_cast(&inplace)); } public: @@ -56,10 +54,11 @@ class intrusive_small_ptr { template AdaptorT, class... ArgsT> requires std::constructible_from - constexpr intrusive_small_ptr([[maybe_unused]] std::in_place_type_t tag, ArgsT&&... args) { + constexpr intrusive_small_ptr([[maybe_unused]] std::in_place_type_t tag, + ArgsT&&... args) noexcept(std::is_nothrow_constructible_v) { if constexpr (sizeof(AdaptorT) <= sizeof(inplace_type) and alignof(AdaptorT) <= alignof(inplace_type) and // SBO requires nothrow move construction - std::is_nothrow_move_constructible_v>) { + std::is_nothrow_constructible_v) { // placement new is not allowed in a constant expression if (not std::is_constant_evaluated()) { ::new (&inplace) AdaptorT(std::forward(args)...); @@ -76,7 +75,7 @@ class intrusive_small_ptr { requires interface_copyable : index(other.index) { if (index == index_type::is_inplace) { - other.get_inplace_ptr()->copy_to(&inplace); + other.get_inplace()->copy_to(&inplace); } else { pointer = other.pointer->copy(); } @@ -84,7 +83,7 @@ class intrusive_small_ptr { constexpr intrusive_small_ptr(intrusive_small_ptr&& other) noexcept : index(other.index) { if (index == index_type::is_inplace) { - other.get_inplace_ptr()->move_to(&inplace); + other.get_inplace()->move_to(&inplace); } else { pointer = std::exchange(other.pointer, nullptr); } @@ -113,21 +112,31 @@ class intrusive_small_ptr { return index == index_type::is_inplace or pointer != nullptr; } - [[nodiscard]] constexpr auto get() const noexcept -> InterfaceT* { + [[nodiscard]] constexpr auto get() noexcept -> InterfaceT* { + if (index == index_type::is_inplace) { + return get_inplace(); + } + + return pointer; + } + + [[nodiscard]] constexpr auto get() const noexcept -> const InterfaceT* { if (index == index_type::is_inplace) { - return get_inplace_ptr(); + return get_inplace(); } return pointer; } - [[nodiscard]] constexpr auto operator*() const noexcept -> InterfaceT& { return *get(); } + [[nodiscard]] constexpr auto operator*() noexcept -> InterfaceT& { return *get(); } + [[nodiscard]] constexpr auto operator*() const noexcept -> const InterfaceT& { return *get(); } - [[nodiscard]] constexpr auto operator->() const noexcept -> InterfaceT* { return get(); } + [[nodiscard]] constexpr auto operator->() noexcept -> InterfaceT* { return get(); } + [[nodiscard]] constexpr auto operator->() const noexcept -> const InterfaceT* { return get(); } constexpr ~intrusive_small_ptr() noexcept { if (index == index_type::is_inplace) { - get_inplace_ptr()->~InterfaceT(); + get_inplace()->~InterfaceT(); } else { delete pointer; } diff --git a/include/beman/any_view/detail/iterator.hpp b/include/beman/any_view/detail/iterator.hpp index 66f431a..7f25558 100644 --- a/include/beman/any_view/detail/iterator.hpp +++ b/include/beman/any_view/detail/iterator.hpp @@ -3,6 +3,10 @@ #ifndef BEMAN_ANY_VIEW_DETAIL_ITERATOR_HPP #define BEMAN_ANY_VIEW_DETAIL_ITERATOR_HPP +#include +#include +#include +#include #include #include @@ -17,44 +21,166 @@ class iterator { using rvalue_reference = RValueRefT; using pointer = std::add_pointer_t; - public: - using iterator_concept = IterConceptT; - using element_type = ElementT; - using difference_type = DiffT; - - [[nodiscard]] constexpr auto operator*() const -> reference; - - friend constexpr auto iter_move(const iterator&) -> rvalue_reference; - - [[nodiscard]] constexpr auto operator->() const -> pointer; - - constexpr auto operator++() -> iterator&; - - [[nodiscard]] constexpr auto operator++(int) -> iterator; + struct range_traits { + using iterator_concept = IterConceptT; + using reference_type = RefT; + using rvalue_reference_type = RValueRefT; + using difference_type = DiffT; + }; - [[nodiscard]] constexpr auto operator==(const iterator&) const -> bool; + static constexpr bool forward = std::derived_from; + static constexpr bool bidirectional = std::derived_from; + static constexpr bool random_access = std::derived_from; + static constexpr bool contiguous = std::derived_from; - constexpr auto operator--() -> iterator&; + using interface_type = iterator_interface; + // inplace storage sufficient for a vtable pointer and two pointers + intrusive_small_ptr iterator_ptr; - [[nodiscard]] constexpr auto operator--(int) -> iterator; + template + using adaptor_type = + detail::iterator_adaptor; - [[nodiscard]] constexpr auto operator<=>(const iterator&) const -> std::partial_ordering; + template + static constexpr auto get_noexcept() { + return std::is_nothrow_constructible_v, IteratorT, SentinelT>; + } - [[nodiscard]] constexpr auto operator-(const iterator&) const -> difference_type; + template + static constexpr auto get_in_place_adaptor_type() { + return std::in_place_type>; + } - constexpr auto operator+=(difference_type) -> iterator&; - - [[nodiscard]] constexpr auto operator+(difference_type) const -> iterator; - - friend constexpr auto operator+(difference_type, const iterator&) -> iterator; - - constexpr auto operator-=(difference_type) -> iterator&; - - [[nodiscard]] constexpr auto operator-(difference_type) const -> iterator; - - [[nodiscard]] constexpr auto operator[](difference_type) const -> reference; + public: + using iterator_concept = IterConceptT; + using element_type = ElementT; + using difference_type = DiffT; - [[nodiscard]] constexpr auto operator==(std::default_sentinel_t) const -> bool; + template IteratorT, std::sentinel_for SentinelT> + constexpr iterator(IteratorT iterator, SentinelT sentinel) noexcept(get_noexcept()) + : iterator_ptr(get_in_place_adaptor_type(), std::move(iterator), std::move(sentinel)) {} + + constexpr iterator() noexcept + requires forward + : iterator(pointer(nullptr), pointer(nullptr)) {} + + constexpr iterator(const iterator&) + requires forward + = default; + + constexpr iterator(iterator&&) noexcept = default; + + constexpr auto operator=(const iterator&) -> iterator& + requires forward + = default; + + constexpr auto operator=(iterator&&) noexcept -> iterator& = default; + + [[nodiscard]] constexpr auto operator*() const -> reference { return **iterator_ptr; } + + friend constexpr auto iter_move(const iterator& other) -> rvalue_reference { + return other.iterator_ptr->iter_move(); + } + + [[nodiscard]] constexpr auto operator->() const -> pointer + requires contiguous + { + return std::to_address(*iterator_ptr); + } + + constexpr auto operator++() -> iterator& { + ++*iterator_ptr; + return *this; + } + + constexpr auto operator++(int) -> void { ++*this; } + + [[nodiscard]] constexpr auto operator++(int) -> iterator + requires forward + { + const auto other = *this; + ++*this; + return other; + } + + [[nodiscard]] constexpr auto operator==(const iterator& other) const -> bool + requires forward + { + return *iterator_ptr == *other.iterator_ptr; + } + + constexpr auto operator--() -> iterator& + requires bidirectional + { + --*iterator_ptr; + return *this; + } + + [[nodiscard]] constexpr auto operator--(int) -> iterator + requires bidirectional + { + const auto other = *this; + --*this; + return other; + } + + [[nodiscard]] constexpr auto operator<=>(const iterator& other) const -> std::partial_ordering + requires random_access + { + return *iterator_ptr <=> *other.iterator_ptr; + } + + [[nodiscard]] constexpr auto operator-(const iterator& other) const -> difference_type + requires random_access + { + return *iterator_ptr - *other.iterator_ptr; + } + + constexpr auto operator+=(difference_type offset) -> iterator& + requires random_access + { + *iterator_ptr += offset; + return *this; + } + + [[nodiscard]] constexpr auto operator+(difference_type offset) const -> iterator + requires random_access + { + auto other = *this; + other += offset; + return other; + } + + [[nodiscard]] friend constexpr auto operator+(difference_type offset, const iterator& other) -> iterator + requires random_access + { + return other + offset; + } + + constexpr auto operator-=(difference_type offset) -> iterator& + requires random_access + { + *iterator_ptr -= offset; + return *this; + } + + [[nodiscard]] constexpr auto operator-(difference_type offset) const -> iterator + requires random_access + { + auto other = *this; + other -= offset; + return other; + } + + [[nodiscard]] constexpr auto operator[](difference_type offset) const -> reference + requires random_access + { + return *(*this + offset); + } + + [[nodiscard]] constexpr auto operator==(std::default_sentinel_t sentinel) const -> bool { + return *iterator_ptr == sentinel; + } }; } // namespace beman::any_view::detail diff --git a/include/beman/any_view/detail/iterator_adaptor.hpp b/include/beman/any_view/detail/iterator_adaptor.hpp new file mode 100644 index 0000000..71d6741 --- /dev/null +++ b/include/beman/any_view/detail/iterator_adaptor.hpp @@ -0,0 +1,137 @@ +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +#ifndef BEMAN_ANY_VIEW_DETAIL_ITERATOR_ADAPTOR_HPP +#define BEMAN_ANY_VIEW_DETAIL_ITERATOR_ADAPTOR_HPP + +#include + +#include +#include +#include + +namespace beman::any_view::detail { + +template SentinelT> +class iterator_adaptor : public iterator_interface { + [[no_unique_address]] IteratorT iterator; + [[no_unique_address]] SentinelT sentinel; + + using iterator_interface = detail::iterator_interface; + using pointer = std::add_pointer_t; + + static constexpr auto get_noexcept() { + return std::is_nothrow_move_constructible_v and std::is_nothrow_move_constructible_v; + } + + static constexpr bool forward = std::derived_from; + static constexpr bool bidirectional = std::derived_from; + static constexpr bool random_access = std::derived_from; + static constexpr bool contiguous = std::derived_from; + + public: + constexpr iterator_adaptor(IteratorT&& iterator, SentinelT&& sentinel) noexcept(get_noexcept()) + : iterator(std::move(iterator)), sentinel(std::move(sentinel)) {} + + auto copy_to(void* destination) const -> void override { + if constexpr (std::copy_constructible and std::copy_constructible) { + ::new (destination) iterator_adaptor(*this); + } else { + std::unreachable(); + } + } + + auto move_to(void* destination) noexcept -> void override { + if constexpr (std::is_nothrow_move_constructible_v and + std::is_nothrow_move_constructible_v) { + ::new (destination) iterator_adaptor(std::move(*this)); + } else { + std::unreachable(); + } + } + + [[nodiscard]] constexpr auto copy() const -> iterator_adaptor* override { + if constexpr (std::copy_constructible and std::copy_constructible) { + return new iterator_adaptor(*this); + } else { + std::unreachable(); + } + } + + [[nodiscard]] constexpr auto operator*() const -> RefT override { return *iterator; } + + [[nodiscard]] constexpr auto iter_move() const -> RValueRefT override { return std::ranges::iter_move(iterator); } + + [[nodiscard]] constexpr auto operator->() const -> pointer override { + if constexpr (contiguous) { + return std::to_address(iterator); + } else { + std::unreachable(); + } + } + + constexpr auto operator++() -> void override { ++iterator; } + + [[nodiscard]] constexpr auto operator==(const iterator_interface& other) const -> bool override { + if constexpr (forward) { + if (type() == other.type()) { + return iterator == static_cast(other).iterator; + } + } + + return false; + } + + constexpr auto operator--() -> void override { + if constexpr (bidirectional) { + --iterator; + } else { + std::unreachable(); + } + } + + [[nodiscard]] constexpr auto operator<=>(const iterator_interface& other) const -> std::partial_ordering override { + if constexpr (random_access) { + if (type() == other.type()) { + return iterator <=> static_cast(other).iterator; + } + } + + return std::partial_ordering::unordered; + } + + [[nodiscard]] constexpr auto operator-(const iterator_interface& other) const -> DiffT override { + if constexpr (random_access) { + if (type() == other.type()) { + return iterator - static_cast(other).iterator; + } + } + + std::unreachable(); + } + + constexpr auto operator+=(DiffT offset) -> void override { + if constexpr (random_access) { + iterator += offset; + } else { + std::unreachable(); + } + } + + [[nodiscard]] constexpr auto operator==(std::default_sentinel_t) const -> bool override { + return iterator == sentinel; + } + + [[nodiscard]] constexpr auto type() const noexcept -> const std::type_info& override { + return typeid(iterator_adaptor); + } +}; + +} // namespace beman::any_view::detail + +#endif // BEMAN_ANY_VIEW_DETAIL_ITERATOR_ADAPTOR_HPP diff --git a/include/beman/any_view/detail/iterator_interface.hpp b/include/beman/any_view/detail/iterator_interface.hpp new file mode 100644 index 0000000..1ee125f --- /dev/null +++ b/include/beman/any_view/detail/iterator_interface.hpp @@ -0,0 +1,55 @@ +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +#ifndef BEMAN_ANY_VIEW_DETAIL_ITERATOR_INTERFACE_HPP +#define BEMAN_ANY_VIEW_DETAIL_ITERATOR_INTERFACE_HPP + +#include +#include +#include +#include + +namespace beman::any_view::detail { + +template +class iterator_interface { + using pointer = std::add_pointer_t; + + public: + // in-place construction + virtual auto copy_to(void* destination) const -> void = 0; + virtual auto move_to(void* destination) noexcept -> void = 0; + + // out-of-place construction + [[nodiscard]] virtual constexpr auto copy() const -> iterator_interface* = 0; + + // iterator methods + [[nodiscard]] virtual constexpr auto operator*() const -> RefT = 0; + + [[nodiscard]] virtual constexpr auto iter_move() const -> RValueRefT = 0; + + [[nodiscard]] virtual constexpr auto operator->() const -> pointer = 0; + + virtual constexpr auto operator++() -> void = 0; + + [[nodiscard]] virtual constexpr auto operator==(const iterator_interface& other) const -> bool = 0; + + virtual constexpr auto operator--() -> void = 0; + + [[nodiscard]] virtual constexpr auto + operator<=>(const iterator_interface& other) const -> std::partial_ordering = 0; + + [[nodiscard]] virtual constexpr auto operator-(const iterator_interface& other) const -> DiffT = 0; + + virtual constexpr auto operator+=(DiffT offset) -> void = 0; + + [[nodiscard]] virtual constexpr auto operator==(std::default_sentinel_t sentinel) const -> bool = 0; + + // type query + [[nodiscard]] virtual constexpr auto type() const noexcept -> const std::type_info& = 0; + + virtual constexpr ~iterator_interface() noexcept = default; +}; + +} // namespace beman::any_view::detail + +#endif // BEMAN_ANY_VIEW_DETAIL_ITERATOR_INTERFACE_HPP diff --git a/include/beman/any_view/detail/view_adaptor.hpp b/include/beman/any_view/detail/view_adaptor.hpp new file mode 100644 index 0000000..69ae8e5 --- /dev/null +++ b/include/beman/any_view/detail/view_adaptor.hpp @@ -0,0 +1,81 @@ +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +#ifndef BEMAN_ANY_VIEW_DETAIL_VIEW_ADAPTOR_HPP +#define BEMAN_ANY_VIEW_DETAIL_VIEW_ADAPTOR_HPP + +#include +#include + +#include +#include + +namespace beman::any_view::detail { + +template +class view_adaptor : public view_interface { + using iterator = detail::iterator; + using size_type = std::make_unsigned_t; + + [[no_unique_address]] ViewT view; + + static constexpr auto get_noexcept() { return std::is_nothrow_move_constructible_v; } + + public: + constexpr view_adaptor(ViewT&& view) noexcept(get_noexcept()) : view(std::move(view)) {} + + auto copy_to(void* destination) const -> void override { + if constexpr (std::copy_constructible) { + ::new (destination) view_adaptor(*this); + } else { + std::unreachable(); + } + } + + auto move_to(void* destination) noexcept -> void override { + if constexpr (std::is_nothrow_move_constructible_v) { + ::new (destination) view_adaptor(std::move(*this)); + } else { + std::unreachable(); + } + } + + [[nodiscard]] constexpr auto copy() const -> view_adaptor* override { + if constexpr (std::copy_constructible) { + return new view_adaptor(*this); + } else { + std::unreachable(); + } + } + + [[nodiscard]] constexpr auto begin() -> iterator override { + if constexpr (contiguous_reference_convertible_to, RefT, IterConceptT>) { + return iterator{std::ranges::begin(view), std::ranges::end(view)}; + } else { + std::unreachable(); + } + } + + [[nodiscard]] constexpr auto begin() const -> iterator override { + if constexpr (std::ranges::range) { + if constexpr (contiguous_reference_convertible_to, + RefT, + IterConceptT>) { + return iterator{std::ranges::begin(view), std::ranges::end(view)}; + } + } + + std::unreachable(); + } + + [[nodiscard]] constexpr auto size() const -> size_type override { + if constexpr (std::ranges::sized_range) { + return std::ranges::size(view); + } else { + std::unreachable(); + } + } +}; + +} // namespace beman::any_view::detail + +#endif // BEMAN_ANY_VIEW_DETAIL_VIEW_ADAPTOR_HPP diff --git a/include/beman/any_view/detail/view_interface.hpp b/include/beman/any_view/detail/view_interface.hpp new file mode 100644 index 0000000..70ee2d5 --- /dev/null +++ b/include/beman/any_view/detail/view_interface.hpp @@ -0,0 +1,33 @@ +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +#ifndef BEMAN_ANY_VIEW_DETAIL_VIEW_INTERFACE_HPP +#define BEMAN_ANY_VIEW_DETAIL_VIEW_INTERFACE_HPP + +#include + +namespace beman::any_view::detail { + +template +class view_interface { + using iterator = detail::iterator; + using size_type = std::make_unsigned_t; + + public: + // in-place construction + virtual auto copy_to(void* destination) const -> void = 0; + virtual auto move_to(void* destination) noexcept -> void = 0; + + // out-of-place construction + [[nodiscard]] virtual constexpr auto copy() const -> view_interface* = 0; + + // view methods + [[nodiscard]] virtual constexpr auto begin() -> iterator = 0; + [[nodiscard]] virtual constexpr auto begin() const -> iterator = 0; + [[nodiscard]] virtual constexpr auto size() const -> size_type = 0; + + virtual constexpr ~view_interface() noexcept = default; +}; + +} // namespace beman::any_view::detail + +#endif // BEMAN_ANY_VIEW_DETAIL_VIEW_INTERFACE_HPP