#pragma once #include #include #include namespace AMDiS { // NOTE: this is a backport of the dune-common TransformedRangeView with added size() function. namespace Impl { // Helper class to mimic a pointer for proxy objects. // This is needed to implement operator-> on an iterator // using proxy-values. It stores the proxy value but // provides operator-> like a pointer. template class PointerProxy { public: PointerProxy(ProxyType&& p) : p_(p) {} ProxyType* operator->() { return &p_; } ProxyType p_; }; // An iterator transforming a wrapped iterator using // an unary function. It inherits the iterator-category // of the underlying iterator. template ::iterator_category> class MappedRangeIterator; template class MappedRangeIterator { public: using iterator_category = std::forward_iterator_tag; using reference = decltype(std::declval()(*(std::declval()))); using value_type = std::decay_t; using pointer = PointerProxy; // If we later want to allow standalone MappedRangeIterators, // we could customize the FunctionPointer to be a default-constructible, // copy-assignable type storing a function but acting like a pointer // to function. using FunctionPointer = const F*; constexpr MappedRangeIterator(const I& it, FunctionPointer f) noexcept : it_(it) , f_(f) {} // Explicitly initialize members. Using a plain // // constexpr MappedRangeIterator() noexcept {} // // would default-initialize the members while // // constexpr MappedRangeIterator() noexcept : it_(), f_() {} // // leads to value-initialization. This is a case where // both are really different. If it_ is a raw pointer (i.e. POD) // then default-initialization leaves it uninitialized while // value-initialization zero-initializes it. constexpr MappedRangeIterator() noexcept : it_() , f_() {} // Dereferencing returns a value created by the function constexpr reference operator*() const noexcept { return (*f_)(*it_); } // Dereferencing returns a value created by the function pointer operator->() const noexcept { return (*f_)(*it_); } constexpr MappedRangeIterator& operator=(MappedRangeIterator const&) = default; constexpr bool operator==(const MappedRangeIterator& other) const noexcept { return (it_ == other.it_); } constexpr bool operator!=(const MappedRangeIterator& other) const noexcept { return (it_ != other.it_); } MappedRangeIterator& operator++() noexcept { ++it_; return *this; } MappedRangeIterator operator++(int) noexcept { MappedRangeIterator copy(*this); ++(*this); return copy; } protected: I it_; FunctionPointer f_; }; template class MappedRangeIterator : public MappedRangeIterator { protected: using Base = MappedRangeIterator; using Base::it_; using Base::f_; public: using iterator_category = std::bidirectional_iterator_tag; using reference = typename Base::reference; using value_type = typename Base::value_type; using pointer = typename Base::pointer; using FunctionPointer = typename Base::FunctionPointer; // inheriting constructor using Base::Base; // Member functions of the forward_iterator that need // to be redefined because the base class methods return a // forward_iterator. constexpr MappedRangeIterator& operator=(MappedRangeIterator const&) = default; MappedRangeIterator& operator++() noexcept { ++it_; return *this; } MappedRangeIterator operator++(int) noexcept { MappedRangeIterator copy(*this); ++(*this); return copy; } // Additional member functions of bidirectional_iterator MappedRangeIterator& operator--() noexcept { --(this->it_); return *this; } MappedRangeIterator operator--(int) noexcept { MappedRangeIterator copy(*this); --(*this); return copy; } }; template class MappedRangeIterator : public MappedRangeIterator { protected: using Base = MappedRangeIterator; using Base::it_; using Base::f_; public: using iterator_category = std::random_access_iterator_tag; using reference = typename Base::reference; using value_type = typename Base::value_type; using pointer = typename Base::pointer; using difference_type = typename std::iterator_traits::difference_type; using FunctionPointer = typename Base::FunctionPointer; // inheriting constructor using Base::Base; // Member functions of the forward_iterator that need // to be redefined because the base class methods return a // forward_iterator. constexpr MappedRangeIterator& operator=(MappedRangeIterator const&) = default; MappedRangeIterator& operator++() noexcept { ++it_; return *this; } MappedRangeIterator operator++(int) noexcept { MappedRangeIterator copy(*this); ++(*this); return copy; } // Member functions of the bidirectional_iterator that need // to be redefined because the base class methods return a // bidirectional_iterator. MappedRangeIterator& operator--() noexcept { --(this->it_); return *this; } MappedRangeIterator operator--(int) noexcept { MappedRangeIterator copy(*this); --(*this); return copy; } // Additional member functions of random_access_iterator MappedRangeIterator& operator+=(difference_type n) noexcept { it_ += n; return *this; } MappedRangeIterator& operator-=(difference_type n) noexcept { it_ -= n; return *this; } bool operator<(const MappedRangeIterator& other) noexcept { return it_(const MappedRangeIterator& other) noexcept { return it_>other.it_; } bool operator>=(const MappedRangeIterator& other) noexcept { return it_>=other.it_; } reference operator[](difference_type n) noexcept { return (*f_)(*(it_+n)); } friend MappedRangeIterator operator+(const MappedRangeIterator& it, difference_type n) noexcept { return MappedRangeIterator(it.it_+n, it.f_); } friend MappedRangeIterator operator+(difference_type n, const MappedRangeIterator& it) noexcept { return MappedRangeIterator(n+it.it_, it.f_); } friend MappedRangeIterator operator-(const MappedRangeIterator& it, difference_type n) noexcept { return MappedRangeIterator(it.it_-n, it.f_); } friend difference_type operator-(const MappedRangeIterator& first, const MappedRangeIterator& second) noexcept { return first.it_-second.it_; } }; } // namespace Impl /** * \brief A range transforming the values of another range on-the-fly * * This behaves like a range providing `begin()` and `end()`. * The iterators over this range internally iterate over * the wrapped range. When dereferencing the iterator, * the value is transformed on-the-fly using a given * transformation function leaving the underlying range * unchanged. * * The transformation may either return temorary values * or l-value references. In the former case the range behaves * like a proxy-container. In the latter case it forwards these * references allowing, e.g., to sort a subset of some container * by applying a transformation to an index-range for those values. * * The iterators of the MappedRangeView have the same * iterator_category as the ones of the wrapped container. * * If range is given as r-value, then the returned MappedRangeView * stores it by value, if range is given as (const) l-value, then the * MappedRangeView stores it by (const) reference. * * If R is a value type, then the MappedRangeView stores the wrapped range by value, * if R is a reference type, then the MappedRangeView stores the wrapped range by reference. * * \tparam R Underlying range. * \tparam F Unary function used to transform the values in the underlying range. **/ template class MappedRangeView { using RawConstIterator = TYPEOF(std::declval().begin()); using RawIterator = TYPEOF(std::declval().begin()); public: /** * \brief Iterator type * * This inherits the iterator_category of the iterators * of the underlying range. */ using const_iterator = Impl::MappedRangeIterator; using iterator = Impl::MappedRangeIterator; /** * \brief Construct from range and function */ template constexpr MappedRangeView(RR&& rawRange, F const& f) noexcept : rawRange_(FWD(rawRange)) , f_(f) {} /** * \brief Obtain a iterator to the first element * * The life time of the returned iterator is bound to * the life time of the range since it only contains a * pointer to the transformation function stored * in the range. */ constexpr const_iterator begin() const noexcept { return const_iterator(rawRange_.begin(), &f_); } constexpr iterator begin() noexcept { return iterator(rawRange_.begin(), &f_); } /** * \brief Return the number of elements in the range, if availble. * * Note, this function is only availble if the underlying raw range * knows its size and provides a function `size()`. */ template ().size())> constexpr auto size() const noexcept { return rawRange_.size(); } /// Provide element access for random-accessible ranges template ().operator[](std::size_t(0)))> decltype(auto) operator[](std::size_t i) const { return f_(rawRange_[i]); } /** * \brief Checks whether the range is empty */ constexpr bool empty() const noexcept { return rawRange_.begin() == rawRange_.end(); } /** * \brief Obtain a iterator past the last element * * The life time of the returned iterator is bound to * the life time of the range since it only contains a * pointer to the transformation function stored * in the range. */ constexpr const_iterator end() const noexcept { return const_iterator(rawRange_.end(), &f_); } constexpr iterator end() noexcept { return iterator(rawRange_.end(), &f_); } private: R rawRange_; F f_; }; /** * \brief Create a MappedRangeView * * \param range The range the transform * \param f Unary function that should the applied to the entries of the range. * * This behaves like a range providing `begin()` and `end()`. * The iterators over this range internally iterate over * the wrapped range. When dereferencing the iterator, * the value is transformed on-the-fly using a given * transformation function leaving the underlying range * unchanged. * * The transformation may either return temporary values * or l-value references. In the former case the range behaves * like a proxy-container. In the latter case it forwards these * references allowing, e.g., to sort a subset of some container * by applying a transformation to an index-range for those values. * * The iterators of the MappedRangeView have the same * iterator_category as the ones of the wrapped container. * * If range is an r-value, then the MappedRangeView stores it by value, * if range is an l-value, then the MappedRangeView stores it by reference. **/ template auto mappedRangeView(R&& range, F const& f) { return MappedRangeView(FWD(range), f); } template auto mappedIterator(Iter it, F const* f) { using iterator = Impl::MappedRangeIterator; return iterator(it, f); } template auto mappedConstIterator(ConstIter it, F const* f) { using const_iterator = Impl::MappedRangeIterator; return const_iterator(it, f); } } // end namespace AMDiS