Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add const_view #346

Open
wants to merge 6 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -9,3 +9,5 @@ build*/
.vscode/
.vs/
*.*.swp
.history/
.devcontainer/
276 changes: 276 additions & 0 deletions include/stl2/view/const.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,276 @@
// 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 <stl2/detail/fwd.hpp>
#include <stl2/detail/range/concepts.hpp>
#include <stl2/view/view_interface.hpp>
#include <stl2/view/all.hpp>

STL2_OPEN_NAMESPACE
{

namespace ext
{
template <input_range R>
requires view<R>
class const_view : public view_interface<const_view<R>>
{
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please make this

STL2_OPEN_NAMESPACE {
	namespace ext {
		template<input_range R>
		requires view<R>
		struct const_view : view_interface<const_view<R>> {

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Seems like this project needs a .clang-format file

template <bool>
struct __iterator;

public:
const_view() = default;
constexpr explicit const_view(R base) : base_(std::move(base)) {}

constexpr auto base() const noexcept -> R { return base_; }
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We usually don't use trailing return types.


constexpr __iterator<false> begin() requires (!ext::simple_view<R>)
{
return __iterator<false>{*this, __stl2::begin(base_)};
}
constexpr __iterator<true> begin() const requires range<const R>
{
return __iterator<true>{*this, __stl2::begin(base_)};
};
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Stray semicolon. Also, I think @CaseyCarter's style is to have the opening brace on the same line as the function's head.


constexpr auto end() requires (!ext::simple_view<R>)
{
return end_impl(*this);
}
constexpr auto end() const requires range<const R>
{
return end_impl(*this);
}

constexpr auto size() requires (!ext::simple_view<R> && sized_range<R>)
{
return __stl2::size(base_);
}

constexpr auto size() const requires sized_range<R>
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

s/sized_range<R>/sized_range<const R>

{
return __stl2::size(base_);
}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A non-const overload is necessary for size.


private:
R base_ = R{};

template <class Self>
static constexpr auto end_impl(Self& self)
{
if constexpr (common_range<R>)
{
return __iterator<std::is_const_v<Self>>{
self,
__stl2::end(self.base_)};
}
else
{
return __stl2::end(self.base_);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You'll need to make a custom sentinel type for this. @timsong-cpp pointed out that if a range adaptor has its own iterator, it needs its own sentinel so that we can compare both the adaptor's iterator and the adaptee's iterator against the sentinel.

}
}
};

template <input_range R>
template <bool is_const>
class const_view<R>::__iterator
{
using parent_t = __maybe_const<is_const, const_view>;
using base_t = __maybe_const<is_const, R>;

friend __iterator<!is_const>;

parent_t *parent_ = nullptr;
iterator_t<base_t> current_{};

public:
using iterator_category = iterator_category_t<iterator_t<base_t>>;
using value_type = std::add_const_t<range_value_t<base_t>>;
using difference_type = range_difference_t<base_t>;

__iterator() = default;

constexpr explicit __iterator(parent_t& parent, iterator_t<base_t> current)
: parent_{std::addressof(parent)},
current_{current}
{
}

constexpr __iterator(__iterator<!is_const> const& other) requires is_const && convertible_to<iterator_t<R>, iterator_t<base_t>>
dvirtz marked this conversation as resolved.
Show resolved Hide resolved
: parent_(other.parent_), current_(other.current_)
dvirtz marked this conversation as resolved.
Show resolved Hide resolved
{
}

constexpr iterator_t<base_t> base() const
{
return current_;
}

constexpr decltype(auto) operator*() const
{
return std::as_const(*current_);
}

constexpr __iterator& operator++()
{
++current_;
return *this;
}

constexpr void operator++(int)
{
(void)++*this;
}

constexpr __iterator operator++(int) requires forward_range<base_t>
{
auto temp = *this;
++*this;
return temp;
}

constexpr __iterator& operator--() requires bidirectional_range<base_t>
{
--current_;
return *this;
}

constexpr __iterator operator--(int) requires bidirectional_range<base_t>
{
auto temp = *this;
--*this;
return temp;
}

constexpr __iterator& operator+=(difference_type const n) requires random_access_range<base_t>
{
current_ += n;
return *this;
}

constexpr __iterator& operator-=(difference_type const n) requires random_access_range<base_t>
{
current_ -= n;
return *this;
}

constexpr decltype(auto) operator[](difference_type const n) const
requires random_access_range<base_t>
{
return *(*this + n);
}

friend constexpr bool operator==(const __iterator& x, const __iterator& y) requires equality_comparable<iterator_t<base_t>>
dvirtz marked this conversation as resolved.
Show resolved Hide resolved
{
return x.current_ == y.current_;
}

friend constexpr bool operator!=(const __iterator& x, const __iterator& y) requires equality_comparable<iterator_t<base_t>>
dvirtz marked this conversation as resolved.
Show resolved Hide resolved
{
return !(x == y);
}

friend constexpr bool operator==(const __iterator& x, const sentinel_t<base_t>& y)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Remember to change these overloads when you make the custom sentinel.

{
return x.current_ == y;
}

friend constexpr bool operator==(const sentinel_t<base_t>& y, const __iterator& x)
{
return x == y;
}

friend constexpr bool operator!=(const __iterator& x, const sentinel_t<base_t>& y)
{
return !(x == y);
}

friend constexpr bool operator!=(const sentinel_t<base_t>& y, const __iterator& x)
{
return !(x == y);
}

friend constexpr bool operator<(const __iterator& x, const __iterator& y) requires random_access_range<base_t>
dvirtz marked this conversation as resolved.
Show resolved Hide resolved
{
return x.current_ < y.current_;
}

friend constexpr bool operator>(const __iterator& x, const __iterator& y) requires random_access_range<base_t>
dvirtz marked this conversation as resolved.
Show resolved Hide resolved
{
return y < x;
}

friend constexpr bool operator<=(const __iterator& x, const __iterator& y) requires random_access_range<base_t>
dvirtz marked this conversation as resolved.
Show resolved Hide resolved
{
return !(y < x);
}

friend constexpr bool operator>=(const __iterator& x, const __iterator& y) requires random_access_range<base_t>
dvirtz marked this conversation as resolved.
Show resolved Hide resolved
{
return !(x < y);
}

friend constexpr __iterator operator+(__iterator i, difference_type n) requires random_access_range<base_t>
{
return i += n;
}

friend constexpr __iterator operator+(difference_type n, __iterator i) requires random_access_range<base_t>
{
return i += n;
}

friend constexpr __iterator operator-(__iterator i, difference_type n) requires random_access_range<base_t>
{
return i -= n;
}

friend constexpr difference_type operator-(const __iterator& x, const __iterator& y) requires random_access_range<base_t>
{
return x.current_ - y.current_;
}

friend constexpr difference_type operator-(const __iterator& x, const sentinel_t<base_t>& y) requires sized_sentinel_for<iterator_t<base_t>, sentinel_t<base_t>>
{
return x.current_ - y;
}

friend constexpr difference_type operator-(const sentinel_t<base_t>& x, const __iterator& y) requires sized_sentinel_for<iterator_t<base_t>, sentinel_t<base_t>>
{
return x - y.current_;
}
};

template <input_range R>
dvirtz marked this conversation as resolved.
Show resolved Hide resolved
const_view(R&& r)->const_view<all_view<R>>;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please also move this between the range adaptor and its iterator.


} // namespace ext

namespace views::ext
{
struct __as_const_fn : detail::__pipeable<__as_const_fn>
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Inheritance not needed for this one. Also, please look at some of the other adaptors to see what's usually done. I recommend take or drop.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it's necessary for adaptors with no arguments. same as views::move.

{
template <viewable_range Rng>
constexpr auto operator()(Rng&& rng) const
{
return __stl2::ext::const_view{std::forward<Rng>(rng)};
}
};

inline constexpr __as_const_fn as_const{};
} // namespace views::ext
}
STL2_CLOSE_NAMESPACE

#endif // STL2_VIEW_CONST_HPP
1 change: 1 addition & 0 deletions test/view/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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)
103 changes: 103 additions & 0 deletions test/view/const_view.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
// 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 <stl2/view/const.hpp>
#include <stl2/view/counted.hpp>
#include <stl2/view/filter.hpp>
#include <vector>
#include "../simple_test.hpp"
#include "../test_iterators.hpp"

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<int &, decltype(*begin(rgi))>);
static_assert(same_as<int const &, decltype(*begin(rng))>);
static_assert(same_as<int const &&, range_rvalue_reference_t<decltype(rng)>>);
static_assert(view<decltype(rng)>);
static_assert(common_range<decltype(rng)>);
static_assert(sized_range<decltype(rng)>);
static_assert(random_access_range<decltype(rng)>);
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<int const &, decltype(*begin(rng2))>);
static_assert(same_as<range_rvalue_reference_t<decltype(rng2)>, int const &&>);
static_assert(view<decltype(rng2)>);
static_assert(forward_range<decltype(rng2)>);
static_assert(!bidirectional_range<decltype(rng2)>);
static_assert(!common_range<decltype(rng2)>);
static_assert(sized_range<decltype(rng2)>);
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<common_pair<int &, int &>>(*begin(zip));
has_type<common_pair<int &&, int &&>>(iter_move(begin(zip)));
has_type<common_pair<int const &, int const &>>(*begin(rng3));
has_type<common_pair<int const &&, int const &&>>(iter_move(begin(rng3)));
static_assert(view<decltype(rng3)>);
static_assert(random_access_range<decltype(rng3)>);
static_assert(common_range<decltype(rng3)>);
static_assert(sized_range<decltype(rng3)>);
using P = std::pair<int,int>;
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<common_pair<int &&, int &&>>(*begin(zip2));
has_type<common_pair<int &&, int &&>>(iter_move(begin(zip2)));
has_type<common_pair<int const &&, int const &&>>(*begin(rng4));
has_type<common_pair<int const &&, int const &&>>(iter_move(begin(rng4)));
static_assert(view<decltype(rng4)>);
static_assert(random_access_range<decltype(rng4)>);
static_assert(common_range<decltype(rng4)>);
static_assert(sized_range<decltype(rng4)>);
using P = std::pair<int,int>;
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<int>{rgi} | views::ext::as_const;
static_assert(same_as<int const&, range_reference_t<decltype(rng)>>);
CHECK_EQUAL(rng, rgi);
}
#endif

{
auto r = rgi
| views::filter([](auto) { return true; })
| views::ext::as_const;
static_assert(view<decltype(r)>);
}

return ::test_result();
}