Skip to content

Instantly share code, notes, and snippets.

@schaumb
Last active July 28, 2020 21:00
Show Gist options
  • Save schaumb/815ccf4e26faf66568baf25bbea1929d to your computer and use it in GitHub Desktop.
Save schaumb/815ccf4e26faf66568baf25bbea1929d to your computer and use it in GitHub Desktop.
limiting iterator maximum range. output_iterator too
#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;
}
#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