#pragma once #include "dsx-ns.h" #ifdef dsx #include #include #include "dxxsconf.h" #include "object.h" #include "segment.h" #ifndef DXX_SEGITER_DEBUG_OBJECT_LINKAGE # ifdef NDEBUG # define DXX_SEGITER_DEBUG_OBJECT_LINKAGE 0 # else # define DXX_SEGITER_DEBUG_OBJECT_LINKAGE 1 # endif #endif #if DXX_SEGITER_DEBUG_OBJECT_LINKAGE #include #endif namespace detail { template struct unspecified_pointer_t; }; namespace dsx { template class segment_object_range_t { class iterator; const iterator b; public: segment_object_range_t(iterator &&o) : b(std::move(o)) { } const iterator &begin() const { return b; } static iterator end() { return T(object_none, static_cast(nullptr)); } template /* Use enable_if to check that the supplied SF can produce * `const segment &`. In !NDEBUG builds, this would be * checked as a side effect of the assert conditions. In NDEBUG * builds, it would not be checked. */ static typename std::enable_if< std::is_same()(segment_first))>::value, segment_object_range_t >::type construct(const segment &s, OF &of, SF &sf) { if (s.objects == object_none) return end(); auto &&opi = of(s.objects); #ifdef NDEBUG (void)sf; #else const object &o = opi; /* Assert that the first object in the segment claims to be * in the segment that claims to have that object. */ assert(&*sf(o.segnum) == &s); /* Assert that the first object in the segment agrees that there * are no objects before it in the segment. */ assert(o.prev == object_none); #endif return iterator(std::move(opi)); } }; template class segment_object_range_t::iterator : public std::iterator, T>, T { using T::m_ptr; using T::m_idx; public: iterator(T &&o) : T(std::move(o)) { } T operator *() const { return *this; } iterator &operator++() { const auto oi = m_idx; const auto op = m_ptr; const auto ni = op->next; m_idx = ni; /* If ni == object_none, then the iterator is at end() and there should * be no further reads of m_ptr. Optimizing compilers will typically * recognize that setting m_ptr=nullptr in this case is unnecessary and * omit the store. Omit the nullptr assignment so that less optimizing * compilers do not generate a useless branch. */ m_ptr += static_cast(ni) - static_cast(oi); if (ni != object_none) { #if DXX_SEGITER_DEBUG_OBJECT_LINKAGE /* Assert that the next object in the segment agrees that * the preceding object is the previous object. */ assert(oi == m_ptr->prev); /* Assert that the old object and new object are in the same * segment. */ assert(op->segnum == m_ptr->segnum); #endif } return *this; } bool operator==(const iterator &rhs) const { return m_idx == rhs.m_idx; } bool operator!=(const iterator &rhs) const { return !(*this == rhs); } }; template ()(object_first))>> __attribute_warn_unused_result static inline R objects_in(const segment &s, OF &of, SF &sf) { return R::construct(s, of, sf); } } #endif