Skip to content

HowTo write a View add number_full

Hannes Hauswedell edited this page Mar 2, 2018 · 8 revisions

view_add_number

The view data structure has changed little:

#include <range/v3/all.hpp>
#include <iostream>
#include <functional>

template <typename urng_t>
//     requires (bool)ranges::InputRange<std::decay_t<urng_t>>() &&
//              std::is_same_v<std::decay_t<ranges::range_reference_t<std::decay_t<urng_t>>>, uint64_t>
class view_add_number : public ranges::view_base
{
private:
    /* data members == "the state" */
    struct data_members_t
    {
        urng_t urange;
        uint64_t const the_number;
    };
    std::shared_ptr<data_members_t> data_members;
  • We have added an additional member, the number that we want to add
    /* the iterator type */
    struct iterator_t : ranges::iterator_t<std::remove_reference_t<urng_t> const>
    {
        uint64_t the_number;

        using base = ranges::iterator_t<std::remove_reference_t<urng_t> const>;

        iterator_t() = default;
        iterator_t(base const & b, uint64_t const n) : base{b}, the_number{n} {}

        iterator_t operator++(int)
        {
            return static_cast<base&>(*this)++;
        }

        iterator_t & operator++()
        {
            ++static_cast<base&>(*this);
            return (*this);
        }

        uint64_t operator*() const
        {
            return *static_cast<base>(*this) + the_number;
        }
    };
  • The iterator is basically the same, but it also holds a copy of the number and add that variable when dereferenced. In cases where the iterator needs access to more parts of the view's state, it might be more practical to save a (raw) pointer to the views data_members
public:
    /* member type definitions */
    using reference         = uint64_t;
    using const_reference   = uint64_t;
    using value_type        = uint64_t;

    using iterator          = iterator_t;
    using const_iterator    = iterator_t;

    /* constructors and deconstructors */
    view_add_number() = default;
    constexpr view_add_number(view_add_number const & rhs) = default;
    constexpr view_add_number(view_add_number && rhs) = default;
    constexpr view_add_number & operator=(view_add_number const & rhs) = default;
    constexpr view_add_number & operator=(view_add_number && rhs) = default;
    ~view_add_number() = default;

    view_add_number(urng_t && urange, uint64_t const number)
        : data_members{new data_members_t{std::forward<urng_t>(urange), number}}
    {}

    /* begin and end */
    iterator begin() const
    {
        return {std::cbegin(data_members->urange), data_members->the_number};
    }
    iterator cbegin() const
    {
        return begin();
    }

    iterator end() const
    {
        return {std::cend(data_members->urange), data_members->the_number};
    }

    iterator cend() const
    {
        return end();
    }
};
  • Nothing has changed here, except that constructor also take the number as second argument.
template <typename urng_t>
//     requires (bool)ranges::InputRange<std::decay_t<urng_t>>() &&
//              std::is_same_v<std::decay_t<ranges::range_reference_t<std::decay_t<urng_t>>>, uint64_t>
view_add_number(urng_t &&, uint64_t const) -> view_add_number<urng_t>;

static_assert((bool)ranges::InputRange<view_add_number<std::vector<uint64_t>>>());
static_assert((bool)ranges::View<view_add_number<std::vector<uint64_t>>>());
  • Nothing has changed here, either, except that the type deduction guide also handles number.

add_number_fn

We have some structural changes here:

struct add_number_fn
{
    template <typename urng_t>
//         requires (bool)ranges::InputRange<std::decay_t<urng_t>>() &&
//                  std::is_same_v<std::decay_t<ranges::range_reference_t<std::decay_t<urng_t>>>, uint64_t>
    auto operator()(urng_t && urange, uint64_t const the_number) const
    {
        return view_add_number{std::forward<urng_t>(urange), the_number};
    }

    auto operator()(uint64_t const the_number) const
    {
        return std::bind(add_number_fn(), std::placeholders::_1, the_number);
    }

};

template <typename urng_t>
//     requires (bool)ranges::InputRange<std::decay_t<urng_t>>() &&
//              std::is_same_v<std::decay_t<ranges::range_reference_t<std::decay_t<urng_t>>>, uint64_t>
auto operator|(urng_t && urange, decltype(std::bind(add_number_fn(),
                                                    std::placeholders::_1,
                                                    static_cast<uint64_t const &>(0ul))) const & bound_view)
{
    return bound_view(std::forward<urng_t>(urange));
}
  • First of all we need to acknowledge that we need both the input range and the number to construct our view_add_number, see our constructor above
  • The first overloaded operator() takes just these and easily facilitates the "functional" use of the view:
auto v = view::take(view::add_number(my_vector, 7), 3);
  • but we also want the pipe notation:
auto v = my_vector | view::add_number(7) | view::take(3);
  • To make this work we need to be able to call our generator functor's operator() with one argument, the number, but delay the actual construction until we also get input range from operator|; we do this in the second operator() definition by binding the first operator() (the one that takes two arguments) with the given number argument and a placeholder for input range.
  • We then overload the operator| as we did before (with the input range as left argument), but this time we take a right argument of exactly the bound type that the second operator() returns! (You gotta love modern C++!)
  • You may have noticed that the operator| is no longer a friend inside add_number_fn but instead a free function; this is because clang requires the type definition to be complete for the call to decltype() to succeed (GCC on the other hand can also handle a definition as friend).
  • Another clang-curiosity is the static_cast<uint64_t const &>(0ul); GCC just accepts 0ul or uint64_t{}.

view::add_number

This again, is the same as before:

namespace view
{

add_number_fn const add_number;

}
Clone this wiki locally