no reference/size_type for output ranges

master
Daniel Kolesa 2017-04-16 17:41:50 +02:00
parent 1619dac782
commit a743f7b755
3 changed files with 55 additions and 52 deletions

View File

@ -46,11 +46,10 @@ input range meet the requirements for an output range. These are called
#include <ostd/range.hh>
struct my_range: ostd::input_range<my_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 <ostd/range.hh>
struct my_range: ostd::output_range<my_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 <ostd/io.hh>
struct strout_range: ostd::output_range<strout_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);
}
};

View File

@ -278,8 +278,6 @@ namespace detail {
/* lightweight output range for direct stdout */
struct stdout_range: output_range<stdout_range> {
using value_type = char;
using reference = char &;
using size_type = std::size_t;
stdout_range() {}
void put(char c) {

View File

@ -167,7 +167,7 @@ namespace detail {
template<typename R>
constexpr bool test_range_category = range_category_test<R>::value;
template<typename R, bool>
template<typename R, bool, bool>
struct range_traits_base {
template<typename>
static constexpr bool is_element_swappable_with = false;
@ -186,8 +186,8 @@ namespace detail {
std::remove_reference_t<typename R::reference>
>;
template<typename R>
struct range_traits_base<R, true> {
template<typename R, bool B>
struct range_traits_base<R, true, B> {
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<R>;
};
template<typename R>
struct range_traits_base<R, false, true> {
using range_category = typename R::range_category;
using value_type = typename R::value_type;
template<typename>
static constexpr bool is_element_swappable_with = false;
static constexpr bool is_element_swappable = false;
template<typename R2>
static constexpr bool is_element_nothrow_swappable_with = false;
static constexpr bool is_element_nothrow_swappable = false;
};
template<typename R, bool>
struct range_traits_impl {
template<typename>
@ -223,7 +236,7 @@ namespace detail {
template<typename R>
struct range_traits_impl<R, true>: range_traits_base<
R,
std::is_convertible_v<typename R::range_category, input_range_tag> ||
std::is_convertible_v<typename R::range_category, input_range_tag>,
std::is_convertible_v<typename R::range_category, output_range_tag>
> {};
}
@ -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<R, R2>;
* ~~~
*
* 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<R, detail::test_range_category<R>
* typename ostd::range_traits<R>::range_category
* ~~~
*
* Works for all types of ranges.
*
* @see ostd::range_size_t, ostd::range_value_t, ostd::range_reference_t
*/
template<typename R>
@ -301,6 +320,8 @@ using range_category_t = typename range_traits<R>::range_category;
* typename ostd::range_traits<R>::size_type
* ~~~
*
* Works only for input-type ranges.
*
* @see ostd::range_category_t, ostd::range_value_t, ostd::range_reference_t
*/
template<typename R>
@ -314,6 +335,8 @@ using range_size_t = typename range_traits<R>::size_type;
* typename ostd::range_traits<R>::value_type
* ~~~
*
* Works for all types of ranges.
*
* @see ostd::range_category_t, ostd::range_size_t, ostd::range_reference_t
*/
template<typename R>
@ -327,6 +350,8 @@ using range_value_t = typename range_traits<R>::value_type;
* typename ostd::range_traits<R>::reference
* ~~~
*
* Works only for input-type ranges.
*
* @see ostd::range_category_t, ostd::range_size_t, ostd::range_value_t
*/
template<typename R>
@ -929,8 +954,6 @@ namespace detail {
template<typename T>
struct noop_output_range: output_range<noop_output_range<T>> {
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<typename R>
struct counting_output_range: output_range<counting_output_range<R>> {
using value_type = range_value_t<R>;
using reference = range_reference_t<R>;
using size_type = range_size_t<R>;
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<typename T>
struct appender_range: output_range<appender_range<T>> {
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<appender_range<T>> {
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) {