Skip to content

Commit

Permalink
Implement ranges::ref_view (#1132)
Browse files Browse the repository at this point in the history
Co-authored-by: Casey Carter <Casey@Carter.net>
  • Loading branch information
miscco and CaseyCarter authored Aug 22, 2020
1 parent a293fad commit aafee76
Show file tree
Hide file tree
Showing 5 changed files with 260 additions and 0 deletions.
59 changes: 59 additions & 0 deletions stl/inc/ranges
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,65 @@ namespace ranges {
template <class _Rng>
concept viewable_range = range<_Rng>
&& (borrowed_range<_Rng> || view<remove_cvref_t<_Rng>>);

// CLASS TEMPLATE ranges::ref_view
// clang-format off
template <range _Rng>
requires is_object_v<_Rng>
class ref_view : public view_interface<ref_view<_Rng>> {
// clang-format on
private:
_Rng* _Range = nullptr;

static void _Rvalue_poison(_Rng&);
static void _Rvalue_poison(_Rng&&) = delete;

public:
constexpr ref_view() noexcept = default;

// clang-format off
template <_Not_same_as<ref_view> _OtherRng>
constexpr ref_view(_OtherRng&& _Other) noexcept(
noexcept(static_cast<_Rng&>(_STD forward<_OtherRng>(_Other)))) // strengthened
requires convertible_to<_OtherRng, _Rng&> && requires {
_Rvalue_poison(static_cast<_OtherRng&&>(_Other));
} : _Range{_STD addressof(static_cast<_Rng&>(_STD forward<_OtherRng>(_Other)))} {}
// clang-format on

_NODISCARD constexpr _Rng& base() const noexcept /* strengthened */ {
return *_Range;
}

_NODISCARD constexpr iterator_t<_Rng> begin() const
noexcept(noexcept(_RANGES begin(*_Range))) /* strengthened */ {
return _RANGES begin(*_Range);
}

_NODISCARD constexpr sentinel_t<_Rng> end() const noexcept(noexcept(_RANGES end(*_Range))) /* strengthened */ {
return _RANGES end(*_Range);
}

_NODISCARD constexpr bool empty() const noexcept(noexcept(_RANGES empty(*_Range))) /* strengthened */
requires _Can_empty<_Rng> {
return _RANGES empty(*_Range);
}

_NODISCARD constexpr auto size() const noexcept(noexcept(_RANGES size(*_Range))) /* strengthened */
requires sized_range<_Rng> {
return _RANGES size(*_Range);
}

_NODISCARD constexpr auto data() const noexcept(noexcept(_RANGES data(*_Range))) /* strengthened */
requires contiguous_range<_Rng> {
return _RANGES data(*_Range);
}
};

template <class _Rng>
ref_view(_Rng&) -> ref_view<_Rng>;

template <class _Rng>
inline constexpr bool enable_borrowed_range<ref_view<_Rng>> = true;
} // namespace ranges

_STD_END
Expand Down
55 changes: 55 additions & 0 deletions tests/std/include/range_algorithm_support.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -968,6 +968,56 @@ struct with_output_ranges {
}
};

template <class Continuation, class Element>
struct with_input_or_output_ranges {
template <class... Args>
static constexpr void call() {
using namespace test;
using test::range;

// For all ranges, IsCommon implies Eq.
// For single-pass ranges, Eq is uninteresting without IsCommon (there's only one valid iterator
// value at a time, and no reason to compare it with itself for equality).
Continuation::template call<Args...,
range<input, Element, Sized::no, CanDifference::no, Common::no, CanCompare::no, ProxyRef::no>>();
Continuation::template call<Args...,
range<input, Element, Sized::no, CanDifference::no, Common::no, CanCompare::no, ProxyRef::yes>>();
Continuation::template call<Args...,
range<input, Element, Sized::no, CanDifference::no, Common::yes, CanCompare::yes, ProxyRef::no>>();
Continuation::template call<Args...,
range<input, Element, Sized::no, CanDifference::no, Common::yes, CanCompare::yes, ProxyRef::yes>>();

Continuation::template call<Args...,
range<input, Element, Sized::no, CanDifference::yes, Common::no, CanCompare::no, ProxyRef::no>>();
Continuation::template call<Args...,
range<input, Element, Sized::no, CanDifference::yes, Common::no, CanCompare::no, ProxyRef::yes>>();
Continuation::template call<Args...,
range<input, Element, Sized::no, CanDifference::yes, Common::yes, CanCompare::yes, ProxyRef::no>>();
Continuation::template call<Args...,
range<input, Element, Sized::no, CanDifference::yes, Common::yes, CanCompare::yes, ProxyRef::yes>>();

Continuation::template call<Args...,
range<input, Element, Sized::yes, CanDifference::no, Common::no, CanCompare::no, ProxyRef::no>>();
Continuation::template call<Args...,
range<input, Element, Sized::yes, CanDifference::no, Common::no, CanCompare::no, ProxyRef::yes>>();
Continuation::template call<Args...,
range<input, Element, Sized::yes, CanDifference::no, Common::yes, CanCompare::yes, ProxyRef::no>>();
Continuation::template call<Args...,
range<input, Element, Sized::yes, CanDifference::no, Common::yes, CanCompare::yes, ProxyRef::yes>>();

Continuation::template call<Args...,
range<input, Element, Sized::yes, CanDifference::yes, Common::no, CanCompare::no, ProxyRef::no>>();
Continuation::template call<Args...,
range<input, Element, Sized::yes, CanDifference::yes, Common::no, CanCompare::no, ProxyRef::yes>>();
Continuation::template call<Args...,
range<input, Element, Sized::yes, CanDifference::yes, Common::yes, CanCompare::yes, ProxyRef::no>>();
Continuation::template call<Args...,
range<input, Element, Sized::yes, CanDifference::yes, Common::yes, CanCompare::yes, ProxyRef::yes>>();

with_output_ranges<Continuation, Element>::template call<Args...>();
}
};

template <class Continuation, class Element>
struct with_input_iterators {
template <class... Args>
Expand Down Expand Up @@ -1018,6 +1068,11 @@ constexpr void test_in() {
with_input_ranges<Instantiator, Element>::call();
}

template <class Instantiator, class Element>
constexpr void test_inout() {
with_input_or_output_ranges<Instantiator, Element>::call();
}

template <class Instantiator, class Element>
constexpr void test_fwd() {
with_forward_ranges<Instantiator, Element>::call();
Expand Down
1 change: 1 addition & 0 deletions tests/std/test.lst
Original file line number Diff line number Diff line change
Expand Up @@ -312,6 +312,7 @@ tests\P0896R4_ranges_alg_unique_copy
tests\P0896R4_ranges_algorithm_machinery
tests\P0896R4_ranges_iterator_machinery
tests\P0896R4_ranges_range_machinery
tests\P0896R4_ranges_ref_view
tests\P0896R4_ranges_subrange
tests\P0896R4_ranges_test_machinery
tests\P0896R4_ranges_to_address
Expand Down
4 changes: 4 additions & 0 deletions tests/std/tests/P0896R4_ranges_ref_view/env.lst
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
# Copyright (c) Microsoft Corporation.
# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception

RUNALL_INCLUDE ..\concepts_matrix.lst
141 changes: 141 additions & 0 deletions tests/std/tests/P0896R4_ranges_ref_view/test.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,141 @@
// Copyright (c) Microsoft Corporation.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception

#include <algorithm>
#include <cassert>
#include <concepts>
#include <memory>
#include <ranges>
#include <span>
#include <utility>

#include <range_algorithm_support.hpp>
using namespace std;

// clang-format off
template <class Range>
concept can_empty = requires(Range& r) { ranges::empty(r); };
template <class Range>
concept can_data = requires(Range& r) { ranges::data(r); };
template <class Range>
concept can_size = requires(Range& r) { ranges::size(r); };
// clang-format on

struct instantiator {
template <ranges::range R>
static constexpr void call() {
using ranges::ref_view, ranges::begin, ranges::end, ranges::forward_range;
int input[3] = {0, 1, 2};

{ // traits
STATIC_ASSERT(ranges::input_range<R> || ranges::output_range<R, const int&>);
STATIC_ASSERT(ranges::enable_borrowed_range<ref_view<R>>);
}

{ // constructors and assignment operators
STATIC_ASSERT(!constructible_from<ref_view<R>, R>);

ref_view<R> default_constructed{};
STATIC_ASSERT(is_nothrow_default_constructible_v<ref_view<R>>);

R wrapped_input{input};
ref_view<R> same_range{wrapped_input};
STATIC_ASSERT(is_nothrow_constructible_v<ref_view<R>, R&>);

auto copy_constructed = same_range;
if constexpr (forward_range<R>) {
assert(copy_constructed.begin().peek() == begin(input));
}
assert(copy_constructed.end().peek() == end(input));

default_constructed = copy_constructed;
if constexpr (forward_range<R>) {
assert(default_constructed.begin().peek() == begin(input));
}
assert(default_constructed.end().peek() == end(input));

[[maybe_unused]] auto move_constructed = std::move(default_constructed);
if constexpr (forward_range<R>) {
assert(move_constructed.begin().peek() == begin(input));
}
assert(move_constructed.end().peek() == end(input));

same_range = std::move(copy_constructed);
if constexpr (forward_range<R>) {
assert(same_range.begin().peek() == begin(input));
}
assert(same_range.end().peek() == end(input));
}

{ // access
R wrapped_input{input};
ref_view<R> test_view{wrapped_input};
same_as<R> auto& base_range = as_const(test_view).base();
assert(addressof(base_range) == addressof(wrapped_input));

STATIC_ASSERT(noexcept(as_const(test_view).base()));
}

{ // iterators
R wrapped_input{input};
ref_view<R> test_view{wrapped_input};
const same_as<ranges::iterator_t<R>> auto first = as_const(test_view).begin();
assert(first.peek() == input);
STATIC_ASSERT(noexcept(as_const(test_view).begin()) == noexcept(wrapped_input.begin()));

const same_as<ranges::sentinel_t<R>> auto last = as_const(test_view).end();
assert(last.peek() == end(input));
STATIC_ASSERT(noexcept(as_const(test_view).end()) == noexcept(wrapped_input.end()));
}

{ // state
STATIC_ASSERT(can_size<ref_view<R>> == ranges::sized_range<R>);
if constexpr (ranges::sized_range<R>) {
R wrapped_input{input};
ref_view<R> test_view{wrapped_input};

const same_as<ranges::range_size_t<R>> auto ref_size = as_const(test_view).size();
assert(ref_size == size(wrapped_input));

STATIC_ASSERT(noexcept(as_const(test_view).size()) == noexcept(wrapped_input.size()));
}

STATIC_ASSERT(can_data<ref_view<R>> == ranges::contiguous_range<R>);
if constexpr (ranges::contiguous_range<R>) {
R wrapped_input{input};
ref_view<R> test_view{wrapped_input};

const same_as<int*> auto ref_data = as_const(test_view).data();
assert(ref_data == input);

STATIC_ASSERT(noexcept(as_const(test_view).data()) == noexcept(wrapped_input.data()));
}

STATIC_ASSERT(can_empty<ref_view<R>> == can_empty<R>);
if constexpr (can_empty<R>) {
R wrapped_input{input};
ref_view<R> test_view{wrapped_input};

const same_as<bool> auto ref_empty = as_const(test_view).empty();
assert(!ref_empty);

STATIC_ASSERT(noexcept(as_const(test_view).empty()) == noexcept(ranges::empty(wrapped_input)));

R empty_range{};
ref_view<R> empty_view{empty_range};
assert(empty_view.empty());
}
}

{ // CTAD
span<const int, 3> spanInput{input};
ref_view span_view{spanInput};
STATIC_ASSERT(same_as<decltype(span_view), ref_view<span<const int, 3>>>);
}
}
};

int main() {
STATIC_ASSERT((test_inout<instantiator, int>(), true));
test_inout<instantiator, int>();
}

0 comments on commit aafee76

Please sign in to comment.