Subversion Repositories Games.Descent

Rev

Details | Last modification | View Log | RSS feed

Rev Author Line No. Line
1 pmbaty 1
#pragma once
2
 
3
#include "dsx-ns.h"
4
#ifdef dsx
5
#include <iterator>
6
#include <type_traits>
7
#include "dxxsconf.h"
8
#include "object.h"
9
#include "segment.h"
10
 
11
#ifndef DXX_SEGITER_DEBUG_OBJECT_LINKAGE
12
#       ifdef NDEBUG
13
#               define DXX_SEGITER_DEBUG_OBJECT_LINKAGE 0
14
#       else
15
#               define DXX_SEGITER_DEBUG_OBJECT_LINKAGE 1
16
#       endif
17
#endif
18
 
19
#if DXX_SEGITER_DEBUG_OBJECT_LINKAGE
20
#include <cassert>
21
#endif
22
 
23
namespace detail
24
{
25
 
26
template <typename>
27
struct unspecified_pointer_t;
28
 
29
};
30
 
31
namespace dsx {
32
 
33
template <typename T>
34
class segment_object_range_t
35
{
36
        class iterator;
37
        const iterator b;
38
public:
39
        segment_object_range_t(iterator &&o) :
40
                b(std::move(o))
41
        {
42
        }
43
        const iterator &begin() const { return b; }
44
        static iterator end() { return T(object_none, static_cast<typename T::allow_none_construction *>(nullptr)); }
45
        template <typename OF, typename SF>
46
                static segment_object_range_t construct(const unique_segment &s, OF &of, SF &sf)
47
                {
48
                        if (s.objects == object_none)
49
                                return end();
50
                        auto &&opi = of(s.objects);
51
                        const object_base &o = opi;
52
#if DXX_SEGITER_DEBUG_OBJECT_LINKAGE
53
                        /* Assert that the first object in the segment claims to be
54
                         * in the segment that claims to have that object.
55
                         */
56
                        assert(&*sf(o.segnum) == &s);
57
                        /* Assert that the first object in the segment agrees that there
58
                         * are no objects before it in the segment.
59
                         */
60
                        assert(o.prev == object_none);
61
#endif
62
                        /* Check that the supplied SF can produce `const unique_segment &`.
63
                         * In !NDEBUG builds, this would be checked as a side effect of the
64
                         * assert conditions.  In NDEBUG builds, it would not be checked.
65
                         *
66
                         * Wrap the expression in a sizeof to prevent the compiler from
67
                         * emitting code to implement the test.
68
                         */
69
                        static_cast<void>(sizeof(&*sf(o.segnum) == &s));
70
                        return iterator(std::move(opi));
71
                }
72
};
73
 
74
template <typename T>
75
class segment_object_range_t<T>::iterator :
76
        T
77
{
78
        using T::m_ptr;
79
        using T::m_idx;
80
public:
81
        using iterator_category = std::forward_iterator_tag;
82
        using value_type = T;
83
        using difference_type = std::ptrdiff_t;
84
        using pointer = void;
85
        using reference = T;
86
        iterator(T &&o) :
87
                T(std::move(o))
88
        {
89
        }
90
        T operator *() const { return *this; }
91
        iterator &operator++()
92
        {
93
                const auto oi = m_idx;
94
                const auto op = m_ptr;
95
                const auto ni = op->next;
96
                m_idx = ni;
97
                /* If ni == object_none, then the iterator is at end() and there should
98
                 * be no further reads of m_ptr.  Optimizing compilers will typically
99
                 * recognize that setting m_ptr=nullptr in this case is unnecessary and
100
                 * omit the store.  Omit the nullptr assignment so that less optimizing
101
                 * compilers do not generate a useless branch.
102
                 */
103
                m_ptr += static_cast<std::size_t>(ni) - static_cast<std::size_t>(oi);
104
                if (ni != object_none)
105
                {
106
                        /* If ni == object_none, then `m_ptr` is now invalid and these
107
                         * tests would be undefined.
108
                         */
109
#if DXX_SEGITER_DEBUG_OBJECT_LINKAGE
110
                        /* Assert that the next object in the segment agrees that
111
                         * the preceding object is the previous object.
112
                         */
113
                        assert(oi == m_ptr->prev);
114
                        /* Assert that the old object and new object are in the same
115
                         * segment.
116
                         */
117
                        assert(op->segnum == m_ptr->segnum);
118
#endif
119
                }
120
                return *this;
121
        }
122
        bool operator==(const iterator &rhs) const
123
        {
124
                return m_idx == rhs.m_idx;
125
        }
126
        bool operator!=(const iterator &rhs) const
127
        {
128
                return !(*this == rhs);
129
        }
130
};
131
 
132
template <typename OF, typename SF, typename R = segment_object_range_t<decltype(std::declval<OF &>()(object_first))>>
133
__attribute_warn_unused_result
134
static inline R objects_in(const unique_segment &s, OF &of, SF &sf)
135
{
136
        return R::construct(s, of, sf);
137
}
138
 
139
}
140
#endif