Subversion Repositories Games.Descent

Rev

Blame | Last modification | View Log | Download | RSS feed

  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
  141.