Last active
November 25, 2015 21:55
-
-
Save CaseyCarter/9d00256c4042556f7284 to your computer and use it in GitHub Desktop.
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
| #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