#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 dsx { template class segment_object_range_t { class iterator; class sentinel; const iterator b; public: segment_object_range_t(iterator &&o) : b(std::move(o)) { } const iterator &begin() const { return b; } static sentinel end() { return {}; } template static segment_object_range_t construct(const unique_segment &s, OF &of, SF &sf) { if (s.objects == object_none) return iterator(T(object_none, static_cast(nullptr))); auto &&opi = of(s.objects); const object_base &o = opi; #if DXX_SEGITER_DEBUG_OBJECT_LINKAGE /* 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 /* Check that the supplied SF can produce `const unique_segment &`. * In !NDEBUG builds, this would be checked as a side effect of the * assert conditions. In NDEBUG builds, it would not be checked. * * Wrap the expression in a sizeof to prevent the compiler from * emitting code to implement the test. */ static_cast(sizeof(&*sf(o.segnum) == &s)); return iterator(std::move(opi)); } }; template class segment_object_range_t::sentinel { }; template class segment_object_range_t::iterator : T { using T::m_ptr; using T::m_idx; public: using iterator_category = std::forward_iterator_tag; using value_type = T; using difference_type = std::ptrdiff_t; using pointer = void; using reference = T; 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 ni == object_none, then `m_ptr` is now invalid and these * tests would be undefined. */ #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; } constexpr bool operator==(const iterator &rhs) const { return m_idx == rhs.m_idx; } constexpr bool operator==(const sentinel &) const { return m_idx == object_none; } }; template ()(object_first))>> [[nodiscard]] static inline R objects_in(const unique_segment &s, OF &of, SF &sf) { return R::construct(s, of, sf); } } #endif