Skip to content

Instantly share code, notes, and snippets.

@CaseyCarter
Last active November 25, 2015 21:55
Show Gist options
  • Select an option

  • Save CaseyCarter/9d00256c4042556f7284 to your computer and use it in GitHub Desktop.

Select an option

Save CaseyCarter/9d00256c4042556f7284 to your computer and use it in GitHub Desktop.
#include <experimental/ranges/concepts>
#include <experimental/ranges/iterator>
#include <istream>
#include <stl2/detail/raw_ptr.hpp>
namespace ranges = std::experimental::ranges;
namespace detail {
template <ranges::Semiregular T, class charT, class traits>
class istream_cursor {
public:
istream_cursor() = default;
constexpr istream_cursor(ranges::default_sentinel)
noexcept(std::is_nothrow_default_constructible<T>::value)
: istream_cursor{} {}
istream_cursor(std::basic_istream<charT, traits>* is)
: is_{is}
{
next();
}
constexpr const T& get() const noexcept {
return t_;
}
void next() {
*is_ >> t_;
if (!*is_) {
is_ = nullptr;
}
}
constexpr bool done() const noexcept {
return is_ == nullptr;
}
constexpr bool equal(const istream_cursor& that) const noexcept {
return is_ == that.is_;
}
private:
T t_{};
ranges::detail::raw_ptr<std::basic_istream<charT, traits>> is_ = nullptr;
};
// The "T is big" case: the range holds a cursor...
template <ranges::Semiregular T, class charT, class traits>
class range_base : public istream_cursor<T, charT, traits> {
using base_t = istream_cursor<T, charT, traits>;
public:
range_base() = default;
range_base(std::istream& is) :
base_t{&is} {}
};
// ... and the iterators hold references to that cursor.
template <ranges::Semiregular T, class charT, class traits>
class iterator_base {
using cursor_t = istream_cursor<T, charT, traits>;
public:
using reference = const T&;
constexpr iterator_base() noexcept = default;
constexpr iterator_base(ranges::default_sentinel) noexcept
: iterator_base{}
{}
constexpr iterator_base(range_base<T, charT, traits>& rng) noexcept
: c_{&rng}
{}
protected:
cursor_t& pos() noexcept { return *c_; }
const cursor_t& pos() const noexcept { return *c_; }
private:
ranges::detail::raw_ptr<cursor_t> c_;
};
// The "T is small" case:
template <class T>
concept bool Small =
ranges::ext::TriviallyCopyable<T>() &&
sizeof(T) <= 4 * sizeof(void*);
// The range only holds a reference to the stream...
template <ranges::Semiregular T, class charT, class traits>
requires
Small<T>
class range_base<T, charT, traits> {
public:
range_base() = default;
range_base(std::istream& is) noexcept :
is_{&is} {}
ranges::detail::raw_ptr<std::basic_istream<charT, traits>> is_ = nullptr;
};
// ...and the iterators hold cursors.
template <ranges::Semiregular T, class charT, class traits>
requires
Small<T>
class iterator_base<T, charT, traits> :
private istream_cursor<T, charT, traits> {
using cursor_t = istream_cursor<T, charT, traits>;
public:
using reference = T;
iterator_base() = default;
iterator_base(ranges::default_sentinel) noexcept
: iterator_base{}
{}
iterator_base(range_base<T, charT, traits>& rng)
: cursor_t{rng.is_}
{}
protected:
cursor_t& pos() noexcept { return *this; }
const cursor_t& pos() const noexcept { return *this; }
};
}
template <ranges::Semiregular T, class charT = char,
class traits = std::char_traits<charT>>
requires
ranges::ext::StreamExtractable<T>
class istream_range : private detail::range_base<T, charT, traits> {
using base_t = detail::range_base<T, charT, traits>;
public:
class iterator : public detail::iterator_base<T, charT, traits> {
using base_t = detail::iterator_base<T, charT, traits>;
public:
using difference_type =
typename std::basic_istream<charT, traits>::off_type;
using value_type = T;
using iterator_category = ranges::input_iterator_tag;
using typename base_t::reference;
iterator() = default;
using base_t::base_t;
reference operator*() const {
return pos().get();
}
iterator& operator++() {
pos().next();
return *this;
}
iterator operator++(int) {
auto tmp = *this;
++*this;
return tmp;
}
friend bool operator==(const iterator& lhs, const iterator& rhs) noexcept {
return lhs.pos().equal(rhs.pos());
}
friend bool operator!=(const iterator& lhs, const iterator& rhs) noexcept {
return !(lhs == rhs);
}
friend bool operator==(const iterator& lhs, ranges::default_sentinel) noexcept {
return lhs.pos().done();
}
friend bool operator!=(const iterator& lhs, ranges::default_sentinel d) noexcept {
return !(lhs == d);
}
friend bool operator==(ranges::default_sentinel d, const iterator& rhs) noexcept {
return rhs == d;
}
friend bool operator!=(ranges::default_sentinel d, const iterator& rhs) noexcept {
return !(rhs == d);
}
private:
using base_t::pos;
};
istream_range() = default;
istream_range(std::istream& is) :
base_t{is} {}
iterator begin() {
return {static_cast<base_t&>(*this)};
}
ranges::default_sentinel end() noexcept {
return {};
}
};
template <ranges::InputIterator I, ranges::Sentinel<I> S, class T,
class Proj = ranges::identity>
requires
ranges::MoveConstructible<T>() &&
ranges::IndirectRegularCallable<ranges::__f<Proj>, I>() &&
requires (T t, ranges::indirect_result_of_t<Proj&(I)> result) {
t += result;
}
T sum(I first, S last, T total, Proj&& proj_ = Proj{}) {
auto&& proj = ranges::ext::make_callable_wrapper(std::forward<Proj>(proj_));
for (; first != last; ++first) {
total += proj(*first);
}
return total;
}
template <ranges::InputIterator I, ranges::Sentinel<I> S,
class Proj = ranges::identity, class T = ranges::value_type_t<I>>
requires
ranges::DefaultConstructible<T>() &&
ranges::MoveConstructible<T>() &&
ranges::IndirectRegularCallable<ranges::__f<Proj>, I>() &&
requires (T t, ranges::indirect_result_of_t<Proj&(I)> result) {
t += result;
}
T sum(I first, S last, Proj&& proj = Proj{}) {
return sum(first, last, T{}, std::forward<Proj>(proj));
}
template <ranges::InputRange Rng, class Proj = ranges::identity,
class T>
requires
ranges::MoveConstructible<T>() &&
ranges::IndirectRegularCallable<ranges::__f<Proj>, ranges::iterator_t<Rng>>() &&
requires (T t, ranges::indirect_result_of_t<Proj&(ranges::iterator_t<Rng>)> result) {
t += result;
}
T sum(Rng&& rng, T total, Proj&& proj = Proj{}) {
return sum(ranges::begin(rng), ranges::end(rng), std::move(total),
std::forward<Proj>(proj));
}
template <ranges::InputRange Rng, class Proj = ranges::identity,
class T = ranges::value_type_t<ranges::iterator_t<Rng>>>
requires
ranges::DefaultConstructible<T>() &&
ranges::MoveConstructible<T>() &&
ranges::IndirectRegularCallable<ranges::__f<Proj>, ranges::iterator_t<Rng>>() &&
requires (T t, ranges::indirect_result_of_t<Proj&(ranges::iterator_t<Rng>)> result) {
t += result;
}
T sum(Rng&& rng, Proj&& proj = Proj{}) {
return sum(ranges::begin(rng), ranges::end(rng), std::forward<Proj>(proj));
}
using sum_t = int;
auto sum_with_istream_iterator(std::istream& is) {
return sum(ranges::istream_iterator<sum_t>{is}, ranges::default_sentinel{});
}
auto sum_with_istream_range(std::istream& is) {
return sum(istream_range<sum_t>{is});
}
template <ranges::InputIterator I, ranges::Sentinel<I> S>
requires
ranges::Same<std::string, ranges::value_type_t<I>>()
std::size_t word_lengths(I i, S s) {
return sum(i, s, std::size_t{}, ranges::size);
}
template <ranges::InputRange Rng>
requires
ranges::Same<std::string, ranges::value_type_t<ranges::iterator_t<Rng>>>()
std::size_t word_lengths(Rng&& rng) {
return word_lengths(ranges::begin(rng), ranges::end(rng));
}
auto word_lengths_with_istream_iterator(std::istream& is) {
return word_lengths(ranges::istream_iterator<std::string>{is},
ranges::default_sentinel{});
}
auto word_lengths_with_istream_range(std::istream& is) {
return word_lengths(istream_range<std::string>{is});
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment