1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149
|
// Copyright (C) 2022 T. Zachary Laine
//
// Distributed under 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)
#ifndef BOOST_STL_INTERFACES_EXAMPLE_REVERSE_VIEW_HPP
#define BOOST_STL_INTERFACES_EXAMPLE_REVERSE_VIEW_HPP
#include "all_view.hpp"
namespace detail {
//[ reverse_view_defn
// We need to treat iterator/sentinel ranges differently from iterator
// ranges (a.k.a. common_ranges). If the iterator and sentinel are
// different types, we need to advance the iterator to the end of the
// range before we can move through the range in reverse.
template<bool CommonRange>
struct set_rev_rng_first
{
template<typename V>
static auto call(V const & v)
{
return boost::stl_interfaces::make_reverse_iterator(v.end());
}
};
template<>
struct set_rev_rng_first<false>
{
template<typename V>
static auto call(V const & v)
{
auto v_f = v.begin();
auto const v_l = v.end();
while (v_f != v_l) {
++v_f;
}
return boost::stl_interfaces::make_reverse_iterator(v_f);
}
};
// This view reverses whatever view you construct it from. Unlike
// all_view, it requires that it be constructed from a view. This is
// enforced through a constraint in C++20 and later, but is left up to the
// user in earlier C++ modes.
#if BOOST_STL_INTERFACES_USE_CONCEPTS
template<std::ranges::view View>
requires std::is_object_v<View>
#else
template<
typename View,
typename Enable = std::enable_if_t<std::is_object<View>::value>>
#endif
struct reverse_view
: boost::stl_interfaces::view_interface<reverse_view<View>>
{
using view_iterator = iterator_t<View>;
using view_sentinel = sentinel_t<View>;
// This would be better off as a constraint in C++20 and later.
static_assert(
std::is_base_of<
std::bidirectional_iterator_tag,
typename std::iterator_traits<
view_iterator>::iterator_category>::value,
"A reversed view must have bidirectional iterators.");
using iterator = boost::stl_interfaces::reverse_iterator<view_iterator>;
constexpr reverse_view() = default;
#if BOOST_STL_INTERFACES_USE_CONCEPTS
template<typename V>
requires std::is_same_v<std::remove_reference_t<V>, View>
#else
template<
typename V,
typename E = std::enable_if_t<
std::is_same<std::remove_reference_t<V>, View>::value>>
#endif
constexpr reverse_view(int, V && v) : v_{(V &&) v}
{
// To keep the code simpler, we just store the iterator to the end
// of v, whether v is a common_range or has different iterator and
// sentinel types.
first_ = set_rev_rng_first<
std::is_same<view_iterator, view_sentinel>::value>::call(v_);
}
constexpr iterator begin() const { return first_; }
constexpr iterator end() const
{
return boost::stl_interfaces::make_reverse_iterator(v_.begin());
}
// Return the underlying view that this view reverses.
constexpr View base() const { return v_; }
private:
View v_ = View();
iterator first_;
};
// is_reverse_view lets us detect construction of a reverse_view from
// another reverse_view, and take appropriate action (see below).
template<typename T>
struct is_reverse_view : std::false_type
{};
template<typename T>
struct is_reverse_view<reverse_view<T>> : std::true_type
{};
#if defined(__cpp_deduction_guides)
template<typename R>
reverse_view(int, R &&)->detail::reverse_view<std::remove_reference_t<R>>;
#endif
//]
}
#if defined(__cpp_deduction_guides)
//[ reverse_defn
// We want to condition how we construct our view based on whether R is itself
// a reverse_view. If R is a reverse_view, just return the view it's
// reversing.
//
// In C++20 and later, you might want to constrain this lambda to require that
// R is a std::ranges::view, since that's what reverse_view requires.
inline constexpr boost::stl_interfaces::closure reverse =
[]<typename R>(R && r) {
if constexpr (detail::is_reverse_view<std::decay_t<R>>::value) {
return ((R &&) r).base();
} else {
return detail::reverse_view(0, (R &&) r);
}
};
//]
#endif
//[ reverse_enable_borrowed_range
// Don't forget to designate our view as a borrowed range.
#if BOOST_STL_INTERFACES_USE_CONCEPTS
namespace std::ranges {
template<typename View>
inline constexpr bool enable_borrowed_range<detail::reverse_view<View>> =
true;
}
#endif
//]
#endif
|