aboutsummaryrefslogtreecommitdiffstats
path: root/util/generic/iterator.h
blob: 19e9d20976df0e34c04d1997e8057a71c2db2541 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
#pragma once

#include <iterator>
#include <utility>

namespace NStlIterator {
    template <class T>
    class TProxy {
    public:
        TProxy() = default;
        TProxy(T&& value)
            : Value_(std::move(value))
        {
        }

        const T* operator->() const noexcept {
            return &Value_;
        }

        const T& operator*() const noexcept {
            return Value_;
        }

        bool operator==(const TProxy& rhs) const {
            return Value_ == rhs.Value_;
        }

    private:
        T Value_;
    };
} // namespace NStlIterator

/**
 * Range adaptor that turns a derived class with a Java-style iteration
 * interface into an STL range.
 *
 * Derived class is expected to define:
 * \code
 * TSomething* Next();
 * \endcode
 *
 * `Next()` returning `nullptr` signals end of range. Note that you can also use
 * pointer-like types instead of actual pointers (e.g. `TAtomicSharedPtr`).
 *
 * Since iteration state is stored inside the derived class, the resulting range
 * is an input range (works for single pass algorithms only). Technically speaking,
 * if you're returning a non-const pointer from `Next`, it can also work as an output range.
 *
 * Example usage:
 * \code
 * class TSquaresGenerator: public TInputRangeAdaptor<TSquaresGenerator> {
 * public:
 *     const double* Next() {
 *         Current_ = State_ * State_;
 *         State_ += 1.0;
 *         // Never return nullptr => we have infinite range!
 *         return &Current_;
 *     }
 *
 * private:
 *     double State_ = 0.0;
 *     double Current_ = 0.0;
 * }
 * \endcode
 */
template <class TSlave>
class TInputRangeAdaptor {
public: // TODO: private
    class TIterator {
    public:
        static constexpr bool IsNoexceptNext = noexcept(std::declval<TSlave>().Next());

        using difference_type = std::ptrdiff_t;
        using pointer = decltype(std::declval<TSlave>().Next());
        using reference = decltype(*std::declval<TSlave>().Next());
        using value_type = std::remove_cv_t<std::remove_reference_t<reference>>;
        using iterator_category = std::input_iterator_tag;

        inline TIterator() noexcept
            : Slave_(nullptr)
            , Cur_()
        {
        }

        inline TIterator(TSlave* slave) noexcept(IsNoexceptNext)
            : Slave_(slave)
            , Cur_(Slave_->Next())
        {
        }

        inline bool operator==(const TIterator& it) const noexcept {
            return Cur_ == it.Cur_;
        }

        inline bool operator!=(const TIterator& it) const noexcept {
            return !(*this == it);
        }

        inline pointer operator->() const noexcept {
            return Cur_;
        }

        inline reference operator*() const noexcept {
            return *Cur_;
        }

        inline TIterator& operator++() noexcept(IsNoexceptNext) {
            Cur_ = Slave_->Next();

            return *this;
        }

    private:
        TSlave* Slave_;
        pointer Cur_;
    };

public:
    using const_iterator = TIterator;
    using iterator = const_iterator;

    inline iterator begin() const noexcept(TIterator::IsNoexceptNext) {
        return TIterator(const_cast<TSlave*>(static_cast<const TSlave*>(this)));
    }

    inline iterator end() const noexcept {
        return TIterator();
    }
};

/**
 * Transform given reverse iterator into forward iterator pointing to the same element.
 *
 * @see http://stackoverflow.com/a/1830240
 */
template <class TIterator>
auto ToForwardIterator(TIterator iter) {
    return std::next(iter).base();
}