diff --git a/.gitignore b/.gitignore index 29e063c95..e17223e44 100644 --- a/.gitignore +++ b/.gitignore @@ -9,3 +9,5 @@ build*/ .vscode/ .vs/ *.*.swp +.history/ +.devcontainer/ diff --git a/include/stl2/view/const.hpp b/include/stl2/view/const.hpp new file mode 100644 index 000000000..6775bd919 --- /dev/null +++ b/include/stl2/view/const.hpp @@ -0,0 +1,241 @@ +// cmcstl2 - A concept-enabled C++ standard library +// +// Copyright Dvir Yitzchaki 2019 +// +// Use, modification and distribution is subject to the +// Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) +// +// Project home: https://github.com/caseycarter/cmcstl2 +// +#ifndef STL2_VIEW_CONST_HPP +#define STL2_VIEW_CONST_HPP + +#include +#include +#include +#include + +STL2_OPEN_NAMESPACE { + namespace ext { + template + requires view + struct const_view : view_interface> { + template + struct __iterator; + + public: + const_view() = default; + constexpr explicit const_view(R base) : base_(std::move(base)) {} + + constexpr R base() const noexcept { return base_; } + + constexpr __iterator begin() requires(!ext::simple_view) { + return __iterator{*this, __stl2::begin(base_)}; + } + + constexpr __iterator begin() const requires range { + return __iterator{*this, __stl2::begin(base_)}; + } + + constexpr auto end() requires(!ext::simple_view) { + return end_impl(*this); + } + + constexpr auto end() const requires range { + return end_impl(*this); + } + + constexpr auto size() requires(!ext::simple_view && sized_range) { + return __stl2::size(base_); + } + + constexpr auto size() const requires sized_range { + return __stl2::size(base_); + } + + private: + R base_ = R{}; + + template + static constexpr auto end_impl(Self& self) { + if constexpr (common_range) { + return __iterator>{self, __stl2::end(self.base_)}; + } else { + return __stl2::end(self.base_); + } + } + }; + + template + const_view(R&& r)->const_view>; + + template + template + class const_view::__iterator { + using parent_t = __maybe_const; + using base_t = __maybe_const; + + friend __iterator; + + parent_t* parent_ = nullptr; + iterator_t current_{}; + + public: + using iterator_category = iterator_category_t>; + using value_type = range_value_t; + using difference_type = range_difference_t; + + __iterator() = default; + + constexpr __iterator(parent_t& parent, iterator_t current) + : parent_{std::addressof(parent)}, current_{current} {} + + constexpr __iterator(__iterator const& other) + requires is_const&& convertible_to, iterator_t> + : parent_(other.parent_), current_(other.current_) {} + + constexpr iterator_t base() const { return current_; } + + constexpr common_reference_t> operator*() const { + return *current_; + } + + constexpr __iterator& operator++() { + ++current_; + return *this; + } + + constexpr void operator++(int) { (void)++*this; } + + constexpr __iterator operator++(int) requires forward_range { + auto temp = *this; + ++*this; + return temp; + } + + constexpr __iterator& operator--() requires bidirectional_range { + --current_; + return *this; + } + + constexpr __iterator operator--(int) requires bidirectional_range { + auto temp = *this; + --*this; + return temp; + } + + constexpr __iterator& operator+=(difference_type const n) + requires random_access_range { + current_ += n; + return *this; + } + + constexpr __iterator& operator-=(difference_type const n) + requires random_access_range { + current_ -= n; + return *this; + } + + constexpr decltype(auto) operator[](difference_type const n) const + requires random_access_range { + return *(*this + n); + } + + friend constexpr bool operator==(const __iterator& x, const __iterator& y) + requires equality_comparable> { + return x.current_ == y.current_; + } + + friend constexpr bool operator!=(const __iterator& x, + const __iterator& y) requires equality_comparable> { + return !(x == y); + } + + friend constexpr bool operator==(const __iterator& x, + const sentinel_t& y) { + return x.current_ == y; + } + + friend constexpr bool operator==(const sentinel_t& y, + const __iterator& x) { + return x == y; + } + + friend constexpr bool operator!=(const __iterator& x, + const sentinel_t& y) { + return !(x == y); + } + + friend constexpr bool operator!=(const sentinel_t& y, + const __iterator& x) { + return !(x == y); + } + + friend constexpr bool operator<(const __iterator& x, const __iterator& y) + requires random_access_range { + return x.current_ < y.current_; + } + + friend constexpr bool operator>(const __iterator& x, const __iterator& y) + requires random_access_range { + return y < x; + } + + friend constexpr bool operator<=(const __iterator& x, const __iterator& y) + requires random_access_range { + return !(y < x); + } + + friend constexpr bool operator>=(const __iterator& x, const __iterator& y) + requires random_access_range { + return !(x < y); + } + + friend constexpr __iterator operator+(__iterator i, difference_type n) + requires random_access_range { + return i += n; + } + + friend constexpr __iterator operator+(difference_type n, __iterator i) + requires random_access_range { + return i += n; + } + + friend constexpr __iterator operator-(__iterator i, difference_type n) + requires random_access_range { + return i -= n; + } + + friend constexpr difference_type operator-(const __iterator& x, const __iterator& y) + requires random_access_range { + return x.current_ - y.current_; + } + + friend constexpr difference_type operator-(const __iterator& x, const sentinel_t& y) + requires sized_sentinel_for, sentinel_t> { + return x.current_ - y; + } + + friend constexpr difference_type operator-(const sentinel_t& x, const __iterator& y) + requires sized_sentinel_for, sentinel_t> { + return x - y.current_; + } + }; + + } // namespace ext + + namespace views::ext { + struct __as_const_fn : detail::__pipeable<__as_const_fn> { + template + constexpr auto operator()(Rng&& rng) const { + return __stl2::ext::const_view{std::forward(rng)}; + } + }; + + inline constexpr __as_const_fn as_const{}; + } // namespace views::ext +} STL2_CLOSE_NAMESPACE + +#endif // STL2_VIEW_CONST_HPP diff --git a/test/view/CMakeLists.txt b/test/view/CMakeLists.txt index 0e70e01db..7ef20c610 100644 --- a/test/view/CMakeLists.txt +++ b/test/view/CMakeLists.txt @@ -32,3 +32,4 @@ add_stl2_test(view.take view.take take_view.cpp) add_stl2_test(view.take_exactly view.take_exactly take_exactly_view.cpp) add_stl2_test(view.take_while view.take_while take_while_view.cpp) add_stl2_test(view.transform view.transform transform_view.cpp) +add_stl2_test(view.const view.const const_view.cpp) diff --git a/test/view/const_view.cpp b/test/view/const_view.cpp new file mode 100644 index 000000000..0c5191eaa --- /dev/null +++ b/test/view/const_view.cpp @@ -0,0 +1,121 @@ +// cmcstl2 - A concept-enabled C++ standard library +// +// Copyright Dvir Yitzchaki 2019 +// +// Use, modification and distribution is subject to the +// Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) +// +// Project home: https://github.com/caseycarter/cmcstl2 +#include "../simple_test.hpp" +#include "../test_iterators.hpp" +#include +#include +#include +#include +#include + +namespace ranges = __stl2; + +int main() { + using namespace ranges; + + int rgi[] = {1, 2, 3, 4}; + + { + auto rng = rgi | views::ext::as_const; + static_assert(same_as); + static_assert(same_as); + static_assert( + same_as>); + static_assert(view); + static_assert(common_range); + static_assert(sized_range); + static_assert(random_access_range); + CHECK_EQUAL(rng, {1, 2, 3, 4}); + CHECK(&*begin(rng) == &rgi[0]); + CHECK(rng.size() == 4u); + } + + { + auto rng2 = + views::counted(::forward_iterator(rgi), 4) | views::ext::as_const; + static_assert(same_as); + static_assert( + same_as, int const &&>); + static_assert(view); + static_assert(forward_range); + static_assert(!bidirectional_range); + static_assert(!common_range); + static_assert(sized_range); + CHECK_EQUAL(rng2, {1, 2, 3, 4}); + CHECK(&*begin(rng2) == &rgi[0]); + CHECK(rng2.size() == 4u); + } + +#if 0 // Test DISABLED pending view implementations. + { + auto zip = views::zip(rgi, rgi); + auto rng3 = zip | views::ext::as_const; + has_type>(*begin(zip)); + has_type>(iter_move(begin(zip))); + has_type>(*begin(rng3)); + has_type>(iter_move(begin(rng3))); + static_assert(view); + static_assert(random_access_range); + static_assert(common_range); + static_assert(sized_range); + using P = std::pair; + CHECK_EQUAL(rng3, {P{1,1}, P{2,2}, P{3,3}, P{4,4}}); + CHECK(&(*begin(rng3)).first == &rgi[0]); + CHECK(rng3.size() == 4u); + } + + { + auto zip2 = views::zip(rgi, rgi) | views::move; + auto rng4 = zip2 | views::ext::as_const; + has_type>(*begin(zip2)); + has_type>(iter_move(begin(zip2))); + has_type>(*begin(rng4)); + has_type>(iter_move(begin(rng4))); + static_assert(view); + static_assert(random_access_range); + static_assert(common_range); + static_assert(sized_range); + using P = std::pair; + CHECK_EQUAL(rng4, {P{1,1}, P{2,2}, P{3,3}, P{4,4}}); + CHECK(&(*begin(rng4)).first == &rgi[0]); + CHECK(rng4.size() == 4u); + } + + { + auto rng = debug_input_view{rgi} | views::ext::as_const; + static_assert(same_as>); + CHECK_EQUAL(rng, rgi); + } +#endif + + { + auto rng6 = + rgi | views::filter([](auto) { return true; }) | views::ext::as_const; + static_assert(view); + } + + { + auto rng7 = rgi | views::move | views::ext::as_const; + static_assert(same_as); + static_assert( + same_as>); + static_assert(view); + static_assert(common_range); + static_assert(sized_range); + static_assert(!forward_range); + CHECK_EQUAL(rng7, {1, 2, 3, 4}); + CHECK(rng7.size() == 4u); + // no move of const rvalue + CHECK_EQUAL(rgi, {1, 2, 3, 4}); + } + + return ::test_result(); +}