diff --git a/doc/ranges.md b/doc/ranges.md index 7478058..d4b8bf0 100644 --- a/doc/ranges.md +++ b/doc/ranges.md @@ -46,11 +46,10 @@ input range meet the requirements for an output range. These are called #include struct my_range: ostd::input_range { - using range_category = ostd::input_range_tag; - using value_type = T; - using reference = T &; - using size_type = size_t; - using difference_type = ptrdiff_t; + using range_category = ostd::input_range_tag; + using value_type = T; + using reference = T &; + using size_type = size_t; my_range(my_range const &); my_range &operator=(my_range const &); @@ -93,11 +92,10 @@ that none of the provided methods are `virtual`, so it's not safe to call them while expecting the overridden variants to be called. ~~~{.cc} - using range_category = ostd::input_range_tag; - using value_type = T; - using reference = T &; - using size_type = size_t; - using difference_type = ptrdiff_t; + using range_category = ostd::input_range_tag; + using value_type = T; + using reference = T &; + using size_type = size_t; ~~~ Any input range is required to have a series of types that define its traits. @@ -119,10 +117,7 @@ really is just a type that is returned by accesses to the range, as accesses are typically not meant to be copy the contents (but they totally can). The `size_type` alias represents the type typically used for sizes of the -range the object represents. It's typically `size_t`. Similarly, the -`difference_type` alias is used for a distance within the range. Usually -it's `ptrdiff_t`, but for example for I/O stream range types it can be the -stream's offset type. +range the object represents. It's typically `size_t`. Now let's take a look at the methods. @@ -191,10 +186,7 @@ an output range: #include struct my_range: ostd::output_range { - using value_type = T; - using reference = T &; - using size_type = size_t; - using difference_type = ptrdiff_t; + using value_type = T; my_range(my_range const &); my_range &operator=(my_range const &); @@ -211,16 +203,14 @@ an output range: As you can see, they're much simpler than input ranges. ~~~{.cc} - using value_type = T; - using reference = T &; - using size_type = size_t; - using difference_type = ptrdiff_t; + using value_type = T; ~~~ Why is there no `range_category` here? Well, it's already defined by the ostd::output\_range it derives (and has to derive) from. We already know that it will always be ostd::output\_range\_tag. Might as well avoid specifying it -always. +always. The size and reference types are not relevant for output ranges, so +they're not here either. Output ranges are always copyable, just like input ranges. There are no rules on state preservation. @@ -581,14 +571,13 @@ It will satisfy a forward range but won't have a writable reference type. using reference = int; // unused - using size_type = size_t; - using difference_type = ptrdiff_t; + using size_type = size_t; // only allow construction with specific args num_range() = delete; // our interval constructor - num_range(int beg, int end): p_a(beg), p_b(end) {} + num_range(value_type beg, value_type end): p_a(beg), p_b(end) {} bool empty() const { // we're empty once the beginning has reached the end @@ -604,7 +593,7 @@ It will satisfy a forward range but won't have a writable reference type. } private: - int p_a, p_b; + value_type p_a, p_b; }; ~~~ @@ -630,12 +619,9 @@ into stdout, each on a new line. #include struct strout_range: ostd::output_range { - using value_type = ostd::string_range; - using reference = ostd::string_range &; - using size_type = size_t; - using difference_type = ptrdiff_t; + using value_type = ostd::string_range; - void put(ostd::string_range v) { + void put(value_type v) { ostd::writeln(v); } }; diff --git a/ostd/io.hh b/ostd/io.hh index bbd27fc..c413ab8 100644 --- a/ostd/io.hh +++ b/ostd/io.hh @@ -278,8 +278,6 @@ namespace detail { /* lightweight output range for direct stdout */ struct stdout_range: output_range { using value_type = char; - using reference = char &; - using size_type = std::size_t; stdout_range() {} void put(char c) { diff --git a/ostd/range.hh b/ostd/range.hh index d7ddae0..f233e17 100644 --- a/ostd/range.hh +++ b/ostd/range.hh @@ -167,7 +167,7 @@ namespace detail { template constexpr bool test_range_category = range_category_test::value; - template + template struct range_traits_base { template static constexpr bool is_element_swappable_with = false; @@ -186,8 +186,8 @@ namespace detail { std::remove_reference_t >; - template - struct range_traits_base { + template + struct range_traits_base { using range_category = typename R::range_category; using size_type = typename R::size_type; using value_type = typename R::value_type; @@ -210,6 +210,19 @@ namespace detail { is_element_nothrow_swappable_with; }; + template + struct range_traits_base { + using range_category = typename R::range_category; + using value_type = typename R::value_type; + + template + static constexpr bool is_element_swappable_with = false; + static constexpr bool is_element_swappable = false; + template + static constexpr bool is_element_nothrow_swappable_with = false; + static constexpr bool is_element_nothrow_swappable = false; + }; + template struct range_traits_impl { template @@ -223,7 +236,7 @@ namespace detail { template struct range_traits_impl: range_traits_base< R, - std::is_convertible_v || + std::is_convertible_v, std::is_convertible_v > {}; } @@ -239,6 +252,10 @@ namespace detail { * * `value_type` - the type of the range's elements * * `reference` - the type returned from value accessors * + * The `size_type` and `reference` types will only be available for + * input-type ranges. Purely output ranges will only have the category + * and value type. + * * It will always contain the following members as well: * * ~~~{.cc} @@ -250,8 +267,8 @@ namespace detail { * static constexpr bool is_element_nothrow_swappable = is_element_swappable_with; * ~~~ * - * These members will be false for non-range types. Otherwise, the first - * member tests for the following: + * These members will be false for non-range types as well as purely output + * range types. Otherwise, the first member tests for the following: * * * Both ranges are an ostd::input_range_tag or better (not an output range). * * The `reference` member type of both ranges is an lvalue reference. @@ -288,6 +305,8 @@ struct range_traits: detail::range_traits_impl * typename ostd::range_traits::range_category * ~~~ * + * Works for all types of ranges. + * * @see ostd::range_size_t, ostd::range_value_t, ostd::range_reference_t */ template @@ -301,6 +320,8 @@ using range_category_t = typename range_traits::range_category; * typename ostd::range_traits::size_type * ~~~ * + * Works only for input-type ranges. + * * @see ostd::range_category_t, ostd::range_value_t, ostd::range_reference_t */ template @@ -314,6 +335,8 @@ using range_size_t = typename range_traits::size_type; * typename ostd::range_traits::value_type * ~~~ * + * Works for all types of ranges. + * * @see ostd::range_category_t, ostd::range_size_t, ostd::range_reference_t */ template @@ -327,6 +350,8 @@ using range_value_t = typename range_traits::value_type; * typename ostd::range_traits::reference * ~~~ * + * Works only for input-type ranges. + * * @see ostd::range_category_t, ostd::range_size_t, ostd::range_value_t */ template @@ -929,8 +954,6 @@ namespace detail { template struct noop_output_range: output_range> { using value_type = T; - using reference = T &; - using size_type = std::size_t; /** @brief Has no effect. */ void put(T const &) {} @@ -952,12 +975,10 @@ namespace detail { template struct counting_output_range: output_range> { using value_type = range_value_t; - using reference = range_reference_t; - using size_type = range_size_t; private: R p_range; - size_type p_written = 0; + size_t p_written = 0; public: counting_output_range() = delete; @@ -974,7 +995,7 @@ namespace detail { ++p_written; } - size_type get_written() const { + size_t get_written() const { return p_written; } }; @@ -1529,8 +1550,6 @@ namespace detail { template struct appender_range: output_range> { using value_type = typename T::value_type; - using reference = typename T::reference; - using size_type = typename T::size_type; /** @brief Default constructs the container inside. */ appender_range(): p_data() {} @@ -1560,16 +1579,16 @@ struct appender_range: output_range> { bool empty() const { return p_data.empty(); } /** @brief Reserves some capacity in the container. */ - void reserve(size_type cap) { p_data.reserve(cap); } + void reserve(typename T::size_type cap) { p_data.reserve(cap); } /** @brief Resizes the container. */ - void resize(size_type len) { p_data.resize(len); } + void resize(typename T::size_type len) { p_data.resize(len); } /** @brief Gets the container size. */ - size_type size() const { return p_data.size(); } + typename T::size_type size() const { return p_data.size(); } /** @brief Gets the container capacity. */ - size_type capacity() const { return p_data.capacity(); } + typename T::size_type capacity() const { return p_data.capacity(); } /** @brief Appends a copy of a value to the end of the container. */ void put(typename T::const_reference v) {