SpECTRE Documentation Coverage Report
Current view: top level - Utilities - StlBoilerplate.hpp Coverage Total Hit
Commit: 0d1d717af84a81fb1284a20c4013708fe85cc2f0 Lines: 17.0 % 112 19
Test Date: 2026-10-22 17:29:42
Legend: Lines:     hit not hit

            Line data    Source code
       1            0 : // Distributed under the MIT License.
       2              : // See LICENSE.txt for details.
       3              : 
       4              : #pragma once
       5              : 
       6              : #include <algorithm>
       7              : #include <cstddef>
       8              : #include <iterator>
       9              : #include <limits>
      10              : #include <stdexcept>
      11              : #include <type_traits>
      12              : 
      13              : #include "Utilities/ErrorHandling/Assert.hpp"
      14              : #include "Utilities/Gsl.hpp"
      15              : #include "Utilities/MakeString.hpp"
      16              : 
      17              : /// Classes to generate repetitive code needed for STL compatibility.
      18            1 : namespace stl_boilerplate {
      19              : /// Base class to generate methods for a random-access iterator.  This
      20              : /// class takes the derived class and the (optionally `const`)
      21              : /// `value_type` of the iterator.  The exposed `value_type` alias will
      22              : /// always be non-const, as required by the standard, but the template
      23              : /// parameter's constness will affect the `reference` and `pointer`
      24              : /// aliases.
      25              : ///
      26              : /// The derived class must implement `operator*`, `operator+=`,
      27              : /// `operator-` (of two iterators), and `operator==`.
      28              : ///
      29              : /// \snippet Test_StlBoilerplate.cpp RandomAccessIterator
      30              : ///
      31              : /// This class is inspired by Boost's `iterator_facade`, except that
      32              : /// that has a lot of special cases, some of which work poorly because
      33              : /// they predate C++11.
      34              : template <typename Iter, typename ValueType>
      35            1 : class RandomAccessIterator {
      36              :  protected:
      37            0 :   RandomAccessIterator() = default;
      38            0 :   RandomAccessIterator(const RandomAccessIterator&) = default;
      39            0 :   RandomAccessIterator(RandomAccessIterator&&) = default;
      40            0 :   RandomAccessIterator& operator=(const RandomAccessIterator&) = default;
      41            0 :   RandomAccessIterator& operator=(RandomAccessIterator&&) = default;
      42            0 :   ~RandomAccessIterator() = default;
      43              : 
      44              :  public:
      45            0 :   using iterator_category = std::random_access_iterator_tag;
      46            0 :   using value_type = std::remove_const_t<ValueType>;
      47            0 :   using reference = ValueType&;
      48            0 :   using pointer = ValueType*;
      49            0 :   using difference_type = std::ptrdiff_t;
      50              : 
      51            0 :   pointer operator->() const { return &**dthis(); }
      52              : 
      53            0 :   Iter& operator++() { return *dthis() += 1; }
      54            0 :   Iter operator++(int) {
      55              :     const auto ret = *dthis();
      56              :     ++*dthis();
      57              :     return ret;
      58              :   }
      59            0 :   Iter& operator--() { return *dthis() += -1; }
      60            0 :   Iter operator--(int) {
      61              :     const auto ret = *dthis();
      62              :     --*dthis();
      63              :     return ret;
      64              :   }
      65              : 
      66            0 :   Iter& operator-=(const difference_type n) { return *dthis() += -n; }
      67              : 
      68            0 :   reference operator[](const difference_type n) const {
      69              :     auto temp = *dthis();
      70              :     temp += n;
      71              :     return *temp;
      72              :   }
      73              : 
      74              :  private:
      75              :   // derived this
      76            0 :   Iter* dthis() { return static_cast<Iter*>(this); }
      77            0 :   const Iter* dthis() const { return static_cast<const Iter*>(this); }
      78              : };
      79              : 
      80              : template <typename Iter, typename ValueType>
      81            0 : bool operator!=(const RandomAccessIterator<Iter, ValueType>& a,
      82              :                 const RandomAccessIterator<Iter, ValueType>& b) {
      83              :   return not(static_cast<const Iter&>(a) == static_cast<const Iter&>(b));
      84              : }
      85              : 
      86              : template <typename Iter, typename ValueType>
      87            0 : bool operator<(const RandomAccessIterator<Iter, ValueType>& a,
      88              :                const RandomAccessIterator<Iter, ValueType>& b) {
      89              :   return static_cast<const Iter&>(b) - static_cast<const Iter&>(a) > 0;
      90              : }
      91              : 
      92              : template <typename Iter, typename ValueType>
      93            0 : bool operator>(const RandomAccessIterator<Iter, ValueType>& a,
      94              :                const RandomAccessIterator<Iter, ValueType>& b) {
      95              :   return b < a;
      96              : }
      97              : 
      98              : template <typename Iter, typename ValueType>
      99            0 : bool operator<=(const RandomAccessIterator<Iter, ValueType>& a,
     100              :                 const RandomAccessIterator<Iter, ValueType>& b) {
     101              :   return not(a > b);
     102              : }
     103              : 
     104              : template <typename Iter, typename ValueType>
     105            0 : bool operator>=(const RandomAccessIterator<Iter, ValueType>& a,
     106              :                 const RandomAccessIterator<Iter, ValueType>& b) {
     107              :   return not(a < b);
     108              : }
     109              : 
     110              : template <typename Iter, typename ValueType>
     111            0 : Iter operator+(
     112              :     const RandomAccessIterator<Iter, ValueType>& a,
     113              :     const typename RandomAccessIterator<Iter, ValueType>::difference_type& n) {
     114              :   auto result = static_cast<const Iter&>(a);
     115              :   result += n;
     116              :   return result;
     117              : }
     118              : 
     119              : template <typename Iter, typename ValueType>
     120            0 : Iter operator+(
     121              :     const typename RandomAccessIterator<Iter, ValueType>::difference_type& n,
     122              :     const RandomAccessIterator<Iter, ValueType>& a) {
     123              :   return a + n;
     124              : }
     125              : 
     126              : template <typename Iter, typename ValueType>
     127            0 : Iter operator-(
     128              :     const RandomAccessIterator<Iter, ValueType>& a,
     129              :     const typename RandomAccessIterator<Iter, ValueType>::difference_type& n) {
     130              :   auto result = static_cast<const Iter&>(a);
     131              :   result -= n;
     132              :   return result;
     133              : }
     134              : 
     135              : /// Base class to generate methods for a random-access sequence,
     136              : /// similar to a `std::array`.
     137              : ///
     138              : /// This class takes the derived class and the `value_type` of the
     139              : /// sequence as template parameters.  The third template parameter
     140              : /// specifies whether to define comparison operators.  Comparisons are
     141              : /// required by the standard, but for derived classes with additional
     142              : /// functionality the autogenerated ones can give incorrect results.
     143              : ///
     144              : /// The derived class must implement `size` and `operator[]`.
     145              : ///
     146              : /// \snippet Test_StlBoilerplate.cpp RandomAccessSequence
     147              : ///
     148              : /// This class provides methods for accessing and modifying elements
     149              : /// of the sequence, such as `front` and `begin`, as well as iterators
     150              : /// and reverse iterators.  Methods modifying the sequence itself,
     151              : /// such as `insert`, require explicit implementations.
     152              : template <typename Sequence, typename ValueType, bool DefineComparisons>
     153            1 : class RandomAccessSequence {
     154              :  protected:
     155            0 :   RandomAccessSequence() = default;
     156            0 :   RandomAccessSequence(const RandomAccessSequence&) = default;
     157            0 :   RandomAccessSequence(RandomAccessSequence&&) = default;
     158            0 :   RandomAccessSequence& operator=(const RandomAccessSequence&) = default;
     159            0 :   RandomAccessSequence& operator=(RandomAccessSequence&&) = default;
     160            0 :   ~RandomAccessSequence() = default;
     161              : 
     162              :  public:
     163            0 :   using value_type = ValueType;
     164            0 :   using reference = value_type&;
     165            0 :   using const_reference = const value_type&;
     166            0 :   using pointer = ValueType*;
     167            0 :   using const_pointer = const ValueType*;
     168              : 
     169            0 :   class const_iterator
     170              :       : public RandomAccessIterator<const_iterator, const value_type> {
     171              :    public:
     172            0 :     const_iterator() = default;
     173              : 
     174            0 :     const value_type& operator*() const { return (*container_)[offset_]; }
     175            0 :     const_iterator& operator+=(const std::ptrdiff_t n) {
     176              :       offset_ += static_cast<size_t>(n);
     177              :       return *this;
     178              :     }
     179              : 
     180              :    private:
     181            0 :     friend bool operator==(const const_iterator& a, const const_iterator& b) {
     182              :       return a.container_ == b.container_ and a.offset_ == b.offset_;
     183              :     }
     184              : 
     185            0 :     friend std::ptrdiff_t operator-(const const_iterator& a,
     186              :                                     const const_iterator& b) {
     187              :       ASSERT(a.container_ == b.container_, "Subtracting unrelated iterators");
     188              :       return static_cast<std::ptrdiff_t>(a.offset_) -
     189              :              static_cast<std::ptrdiff_t>(b.offset_);
     190              :     }
     191              : 
     192            0 :     friend RandomAccessSequence;
     193            0 :     const_iterator(const gsl::not_null<const Sequence*> container,
     194              :                    const size_t offset)
     195              :         : container_(container), offset_(offset) {}
     196              : 
     197            0 :     const Sequence* container_{nullptr};
     198            0 :     size_t offset_{0};
     199              :   };
     200              : 
     201            0 :   class iterator : public RandomAccessIterator<iterator, value_type> {
     202              :    public:
     203            0 :     iterator() = default;
     204              : 
     205            0 :     value_type& operator*() const { return (*container_)[offset_]; }
     206            0 :     iterator& operator+=(const std::ptrdiff_t n) {
     207              :       offset_ += static_cast<size_t>(n);
     208              :       return *this;
     209              :     }
     210              : 
     211            0 :     operator const_iterator() const {
     212              :       return container_->cbegin() + static_cast<std::ptrdiff_t>(offset_);
     213              :     }
     214              : 
     215              :    private:
     216            0 :     friend bool operator==(const iterator& a, const iterator& b) {
     217              :       return a.container_ == b.container_ and a.offset_ == b.offset_;
     218              :     }
     219              : 
     220            0 :     friend std::ptrdiff_t operator-(const iterator& a, const iterator& b) {
     221              :       ASSERT(a.container_ == b.container_, "Subtracting unrelated iterators");
     222              :       return static_cast<std::ptrdiff_t>(a.offset_) -
     223              :              static_cast<std::ptrdiff_t>(b.offset_);
     224              :     }
     225              : 
     226            0 :     friend RandomAccessSequence;
     227            0 :     iterator(const gsl::not_null<Sequence*> container, const size_t offset)
     228              :         : container_(container), offset_(offset) {}
     229              : 
     230            0 :     Sequence* container_{nullptr};
     231            0 :     size_t offset_{0};
     232              :   };
     233              : 
     234            0 :   using reverse_iterator = std::reverse_iterator<iterator>;
     235            0 :   using const_reverse_iterator = std::reverse_iterator<const_iterator>;
     236            0 :   using difference_type =
     237              :       typename std::iterator_traits<const_iterator>::difference_type;
     238            0 :   using size_type = size_t;
     239              : 
     240            0 :   iterator begin() & { return {dthis(), 0}; }
     241            0 :   const_iterator begin() const& { return {dthis(), 0}; }
     242            0 :   const_iterator cbegin() const& { return begin(); }
     243            0 :   iterator end() & { return {dthis(), dthis()->size()}; }
     244            0 :   const_iterator end() const& { return {dthis(), dthis()->size()}; }
     245            0 :   const_iterator cend() const& { return end(); }
     246              : 
     247            0 :   reverse_iterator rbegin() & { return reverse_iterator(end()); }
     248            0 :   const_reverse_iterator rbegin() const& {
     249              :     return const_reverse_iterator(end());
     250              :   }
     251            0 :   const_reverse_iterator crbegin() const& { return rbegin(); }
     252            0 :   reverse_iterator rend() & { return reverse_iterator(begin()); }
     253            0 :   const_reverse_iterator rend() const& {
     254              :     return const_reverse_iterator(begin());
     255              :   }
     256            0 :   const_reverse_iterator crend() const& { return rend(); }
     257              : 
     258              :   /// Not part of the standard, but any attempt to store the results
     259              :   /// of a call that would match one of these would be undefined
     260              :   /// behavior.  Valid uses would be: use as temporaries, but that can
     261              :   /// usually be better done through the normal container access; and
     262              :   /// use in unevaluated contexts, but those should really be
     263              :   /// explicitly requesting lvalue references if that's what they're
     264              :   /// checking.  Either of these can be easily worked around.  If
     265              :   /// these things happen in code outside our control we may have to
     266              :   /// reconsider.
     267              :   /// @{
     268            1 :   void begin() && = delete;
     269            1 :   void begin() const&& = delete;
     270            1 :   void cbegin() && = delete;
     271            1 :   void cbegin() const&& = delete;
     272            1 :   void end() && = delete;
     273            1 :   void end() const&& = delete;
     274            1 :   void cend() && = delete;
     275            1 :   void cend() const&& = delete;
     276            1 :   void rbegin() && = delete;
     277            1 :   void rbegin() const&& = delete;
     278            1 :   void crbegin() && = delete;
     279            1 :   void crbegin() const&& = delete;
     280            1 :   void rend() && = delete;
     281            1 :   void rend() const&& = delete;
     282            1 :   void crend() && = delete;
     283            1 :   void crend() const&& = delete;
     284              :   /// @}
     285              : 
     286            0 :   size_type max_size() const { return std::numeric_limits<size_type>::max(); }
     287            0 :   bool empty() const { return begin() == end(); }
     288              : 
     289            0 :   reference front() { return *begin(); }
     290            0 :   const_reference front() const { return *begin(); }
     291            0 :   reference back() { return *(end() - 1); }
     292            0 :   const_reference back() const { return *(end() - 1); }
     293              : 
     294            0 :   reference at(const size_type n) {
     295              :     if (n >= dthis()->size()) {
     296              :       throw std::out_of_range(MakeString{} << "RandomAccessSequence::at " << n
     297              :                                            << " >= " << dthis()->size());
     298              :     }
     299              :     return (*dthis())[n];
     300              :   }
     301            0 :   const_reference at(const size_type n) const {
     302              :     if (n >= dthis()->size()) {
     303              :       throw std::out_of_range(MakeString{} << "RandomAccessSequence::at " << n
     304              :                                            << " >= " << dthis()->size());
     305              :     }
     306              :     return (*dthis())[n];
     307              :   }
     308              : 
     309              :  private:
     310              :   // derived this
     311            0 :   Sequence* dthis() { return static_cast<Sequence*>(this); }
     312            0 :   const Sequence* dthis() const { return static_cast<const Sequence*>(this); }
     313              : };
     314              : 
     315              : template <typename Sequence, typename ValueType>
     316            0 : bool operator==(const RandomAccessSequence<Sequence, ValueType, true>& a,
     317              :                 const RandomAccessSequence<Sequence, ValueType, true>& b) {
     318              :   return static_cast<const Sequence&>(a).size() ==
     319              :              static_cast<const Sequence&>(b).size() and
     320              :          std::equal(a.begin(), a.end(), b.begin());
     321              : }
     322              : 
     323              : template <typename Sequence, typename ValueType>
     324            0 : bool operator!=(const RandomAccessSequence<Sequence, ValueType, true>& a,
     325              :                 const RandomAccessSequence<Sequence, ValueType, true>& b) {
     326              :   return not(a == b);
     327              : }
     328              : 
     329              : template <typename Sequence, typename ValueType>
     330            0 : bool operator<(const RandomAccessSequence<Sequence, ValueType, true>& a,
     331              :                const RandomAccessSequence<Sequence, ValueType, true>& b) {
     332              :   return std::lexicographical_compare(a.begin(), a.end(), b.begin(), b.end());
     333              : }
     334              : 
     335              : template <typename Sequence, typename ValueType>
     336            0 : bool operator>(const RandomAccessSequence<Sequence, ValueType, true>& a,
     337              :                const RandomAccessSequence<Sequence, ValueType, true>& b) {
     338              :   return b < a;
     339              : }
     340              : 
     341              : template <typename Sequence, typename ValueType>
     342            0 : bool operator<=(const RandomAccessSequence<Sequence, ValueType, true>& a,
     343              :                 const RandomAccessSequence<Sequence, ValueType, true>& b) {
     344              :   return not(a > b);
     345              : }
     346              : 
     347              : template <typename Sequence, typename ValueType>
     348            0 : bool operator>=(const RandomAccessSequence<Sequence, ValueType, true>& a,
     349              :                 const RandomAccessSequence<Sequence, ValueType, true>& b) {
     350              :   return not(a < b);
     351              : }
     352              : }  // namespace stl_boilerplate
        

Generated by: LCOV version 2.0-1