Last active
July 28, 2020 21:00
-
-
Save schaumb/815ccf4e26faf66568baf25bbea1929d to your computer and use it in GitHub Desktop.
limiting iterator maximum range. output_iterator too
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#pragma once | |
#include <boost/iterator/iterator_categories.hpp> | |
#include <boost/iterator/iterator_adaptor.hpp> | |
namespace bxlx { | |
namespace detail { | |
template<typename Proxy> | |
struct enable_proxy { | |
Proxy p; | |
bool enable; | |
enable_proxy(Proxy&& p, bool enable) : p(std::forward<Proxy>(p)), enable(enable) {} | |
template<typename T> | |
enable_proxy& operator=(T&& val) { | |
if (enable) | |
std::forward<Proxy>(p) = std::forward<T>(val); | |
return *this; | |
} | |
}; | |
template<typename It, typename LimitType, typename = typename boost::iterator_traversal<It>::type> | |
struct limit_iterator : boost::iterator_adaptor<limit_iterator<It, LimitType>, It> { | |
static_assert(std::is_integral_v<LimitType>, "limit type can only be integral type"); | |
using Base = boost::iterator_adaptor<limit_iterator<It, LimitType>, It>; | |
limit_iterator() = default; | |
limit_iterator(It it, LimitType limit, std::enable_if_t<true, LimitType> curr = LimitType{}) : Base(it), limit(limit), curr(curr) { | |
if constexpr (std::is_signed_v<LimitType>) | |
if (curr < LimitType{}) | |
advance(std::abs(curr)); | |
} | |
friend class boost::iterator_core_access; | |
private: | |
LimitType const& get_curr() const { | |
return curr; | |
} | |
LimitType const& get_limit() const { | |
return limit; | |
} | |
bool equal(limit_iterator const& x) const { | |
return !(x.get_curr() < x.get_limit() || get_curr() < get_limit()) || get_curr() == x.get_curr() || this->base_reference() == x.base_reference(); | |
} | |
void advance(typename Base::difference_type n) { | |
curr += n; | |
std::advance(this->base_reference(), n); | |
} | |
void increment() { | |
if constexpr (std::is_signed_v<LimitType>) | |
if (curr < LimitType{}) | |
return advance(std::abs(curr)); | |
++curr; | |
++this->base_reference(); | |
} | |
void decrement() { | |
if (curr > limit) | |
advance(-static_cast<std::ptrdiff_t>(curr - limit)); | |
--curr; | |
--this->base_reference(); | |
} | |
typename Base::difference_type distance_to(limit_iterator const& y) const { | |
return std::min( | |
static_cast<typename Base::difference_type>(std::min(y.get_limit(), y.get_curr()) - std::min(get_limit(), get_curr())), | |
std::distance(this->base_reference(), y.base_reference())); | |
} | |
LimitType limit; | |
LimitType curr; | |
}; | |
template<typename It, typename LimitType> | |
struct limit_iterator<It, LimitType, boost::incrementable_traversal_tag> | |
: boost::iterator_adaptor<limit_iterator<It, LimitType>, It, boost::use_default, boost::incrementable_traversal_tag, | |
enable_proxy<decltype(*std::declval<It>())>, std::ptrdiff_t> { | |
static_assert(std::is_integral_v<LimitType>, "limit type can only be integral type"); | |
using Base = boost::iterator_adaptor<limit_iterator<It, LimitType>, It, boost::use_default, boost::incrementable_traversal_tag, | |
enable_proxy<decltype(*std::declval<It>())>, std::ptrdiff_t>; | |
limit_iterator(It it, LimitType limit, std::enable_if_t<true, LimitType> curr = LimitType{}) : Base(it), limit(limit), curr(curr) {} | |
friend class boost::iterator_core_access; | |
private: | |
LimitType const& get_curr() const { | |
return curr; | |
} | |
LimitType const& get_limit() const { | |
return limit; | |
} | |
void increment() { | |
++curr; | |
++this->base_reference(); | |
} | |
typename Base::reference dereference() const { | |
return {*static_cast<It>(this->base_reference()), LimitType{} <= get_curr() && get_curr()+1 < get_limit()}; | |
} | |
LimitType limit; | |
LimitType curr; | |
}; | |
} | |
using detail::limit_iterator; | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#pragma once | |
#include "limit_iterator.hpp" | |
#include <boost/range/traversal.hpp> | |
#include <boost/range/iterator_range.hpp> | |
#include <boost/range/adaptor/reversed.hpp> | |
#include <boost/range/adaptor/type_erased.hpp> | |
#include <stdexcept> | |
#include <boost/throw_exception.hpp> | |
namespace bxlx { | |
namespace detail { | |
template<typename Range, typename LimitType, std::enable_if_t<std::is_base_of_v<boost::bidirectional_traversal_tag, | |
typename boost::range_traversal<Range>::type>>* = nullptr> | |
decltype(auto) limit(Range&& range, [[maybe_unused]] LimitType limit) { | |
if constexpr (!std::is_integral_v<LimitType>) | |
return std::forward<Range>(range); | |
if constexpr (std::is_signed_v<LimitType>) { | |
if (limit >= 0) { | |
return boost::make_iterator_range( | |
bxlx::limit_iterator{range.begin(), limit}, | |
bxlx::limit_iterator{range.end(), limit, static_cast<LimitType>(std::distance(range.begin(), range.end()))} | |
) | boost::adaptors::type_erased<boost::use_default, boost::use_default>(); | |
} else | |
return bxlx::detail::limit(std::forward<Range>(range) | boost::adaptors::reversed, static_cast<std::make_unsigned_t<LimitType>>(-limit)) | boost::adaptors::reversed | |
| boost::adaptors::type_erased<boost::use_default, boost::use_default>(); | |
} else | |
return boost::make_iterator_range( | |
bxlx::limit_iterator{range.begin(), limit}, | |
bxlx::limit_iterator{range.end(), limit, static_cast<LimitType>(std::distance(range.begin(), range.end()))} | |
); | |
} | |
template<typename Range, typename LimitType, std::enable_if_t<!std::is_base_of_v<boost::bidirectional_traversal_tag, | |
typename boost::range_traversal<Range>::type>>* = nullptr> | |
decltype(auto) limit(Range&& range, [[maybe_unused]] LimitType limit) { | |
if constexpr (!std::is_integral_v<LimitType>) | |
return std::forward<Range>(range); | |
else { | |
if constexpr (std::is_signed_v<LimitType>) | |
if (limit < LimitType{}) | |
BOOST_THROW_EXCEPTION(std::underflow_error("Not bidirectional range with negative limit")); | |
return boost::make_iterator_range( | |
bxlx::limit_iterator{range.begin(), limit}, | |
bxlx::limit_iterator{range.end(), limit, limit} | |
); | |
} | |
} | |
template<typename LimitType> | |
struct limited_temp { | |
LimitType limit; | |
limited_temp(LimitType limit) : limit(limit) {} | |
template<typename Range, typename LimitTypeX> | |
friend decltype(auto) operator|(Range&& range, limited_temp<LimitTypeX> const& l) { | |
return bxlx::detail::limit(std::forward<Range>(range), l.limit); | |
} | |
}; | |
} | |
using detail::limit; | |
constexpr static auto limited = [](auto limit) { return detail::limited_temp{limit}; }; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment