LCOV - code coverage report
Current view: top level - include/utils - UniqueStorage.h (source / functions) Hit Total Coverage
Test: idaholab/moose framework: 2bf808 Lines: 29 29 100.0 %
Date: 2025-07-17 01:28:37 Functions: 37 42 88.1 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : //* This file is part of the MOOSE framework
       2             : //* https://mooseframework.inl.gov
       3             : //*
       4             : //* All rights reserved, see COPYRIGHT for full restrictions
       5             : //* https://github.com/idaholab/moose/blob/master/COPYRIGHT
       6             : //*
       7             : //* Licensed under LGPL 2.1, please see LICENSE for details
       8             : //* https://www.gnu.org/licenses/lgpl-2.1.html
       9             : 
      10             : #pragma once
      11             : 
      12             : #include <memory>
      13             : #include <vector>
      14             : #include <utility>
      15             : #include <iostream>
      16             : 
      17             : template <class T>
      18             : class UniqueStorage;
      19             : template <typename T>
      20             : void storeHelper(std::ostream & stream, UniqueStorage<T> &, void *);
      21             : template <typename T>
      22             : void loadHelper(std::istream & stream, UniqueStorage<T> &, void *);
      23             : 
      24             : /**
      25             :  * Storage container that stores a vector of unique pointers of T,
      26             :  * but represents most of the public facing accessors (iterators,
      27             :  * operator[]) as a vector of T.
      28             :  *
      29             :  * That is, these accessors dereference the underlying storage.
      30             :  * More importantly, if data is not properly initialized using
      31             :  * setValue(), this dereferencing will either lead to an assertion
      32             :  * or a nullptr dereference.
      33             :  */
      34             : template <class T>
      35             : class UniqueStorage
      36             : {
      37             : public:
      38     1033884 :   UniqueStorage() = default;
      39      188262 :   UniqueStorage(UniqueStorage &&) = default;
      40             :   UniqueStorage(const UniqueStorage & mp) = delete;
      41             :   UniqueStorage & operator=(const UniqueStorage &) = delete;
      42             : 
      43             :   /**
      44             :    * Iterator that adds an additional dereference to BaseIterator.
      45             :    */
      46             :   template <class BaseIterator>
      47             :   struct DereferenceIterator : public BaseIterator
      48             :   {
      49      670496 :     DereferenceIterator(const BaseIterator & it) : BaseIterator(it) {}
      50             : 
      51             :     using value_type = typename BaseIterator::value_type::element_type;
      52             :     using pointer = value_type *;
      53             :     using reference = value_type &;
      54             : 
      55     5041501 :     reference operator*() const
      56             :     {
      57     5041501 :       auto & val = BaseIterator::operator*();
      58             :       mooseAssert(val, "Null object");
      59     5041501 :       return *val;
      60             :     }
      61             :     pointer operator->() const { return BaseIterator::operator*().get(); }
      62             :     reference operator[](size_t n) const
      63             :     {
      64             :       auto & val = BaseIterator::operator[](n);
      65             :       mooseAssert(val, "Null object");
      66             :       return *val;
      67             :     }
      68             :   };
      69             : 
      70             :   using values_type = typename std::vector<std::unique_ptr<T>>;
      71             :   using iterator = DereferenceIterator<typename values_type::iterator>;
      72             :   using const_iterator = DereferenceIterator<typename values_type::const_iterator>;
      73             : 
      74             :   /**
      75             :    * Begin and end iterators to the underlying data.
      76             :    *
      77             :    * Note that dereferencing these iterators may lead to an assertion
      78             :    * or the dereference of a nullptr whether or not the underlying data
      79             :    * is initialized.
      80             :    */
      81             :   ///@{
      82      274142 :   iterator begin() { return iterator(_values.begin()); }
      83      274142 :   iterator end() { return iterator(_values.end()); }
      84       61106 :   const_iterator begin() const { return const_iterator(_values.begin()); }
      85       61106 :   const_iterator end() const { return const_iterator(_values.end()); }
      86             :   ///@}
      87             : 
      88             :   /**
      89             :    * @returns A reference to the underlying data at index \p i.
      90             :    *
      91             :    * Note that the underlying data may not necessarily be initialized,
      92             :    * in which case this will throw an assertion or dereference a nullptr.
      93             :    *
      94             :    * You can check whether or not the underlying data is initialized
      95             :    * with hasValue(i).
      96             :    */
      97             :   ///@{
      98   102183765 :   const T & operator[](const std::size_t i) const
      99             :   {
     100             :     mooseAssert(hasValue(i), "Null object");
     101   102183765 :     return *pointerValue(i);
     102             :   }
     103   100717971 :   T & operator[](const std::size_t i) { return const_cast<T &>(std::as_const(*this)[i]); }
     104             :   ///@}
     105             : 
     106             :   /**
     107             :    * @returns The size of the underlying storage.
     108             :    *
     109             :    * Note that this is not necessarily the size of _constructed_ objects,
     110             :    * as underlying objects could be uninitialized
     111             :    */
     112    59430197 :   std::size_t size() const { return _values.size(); }
     113             :   /**
     114             :    * @returns Whether or not the underlying storage is empty.
     115             :    */
     116    11505054 :   bool empty() const { return _values.empty(); }
     117             : 
     118             :   /**
     119             :    * @returns whether or not the underlying object at index \p is initialized
     120             :    */
     121    67335993 :   bool hasValue(const std::size_t i) const { return pointerValue(i) != nullptr; }
     122             : 
     123             :   /**
     124             :    * @returns A pointer to the underlying data at index \p i
     125             :    *
     126             :    * The pointer will be nullptr if !hasValue(i), that is, if the
     127             :    * unique_ptr at index \p i is not initialized
     128             :    */
     129             :   ///@{
     130      130689 :   const T * queryValue(const std::size_t i) const { return pointerValue(i).get(); }
     131       71274 :   T * queryValue(const std::size_t i)
     132             :   {
     133       71274 :     return const_cast<T *>(std::as_const(*this).queryValue(i));
     134             :   }
     135             :   ///@}
     136             : 
     137             : protected:
     138             :   /**
     139             :    * Sets the underlying unique_ptr at index \p i to \p ptr.
     140             :    *
     141             :    * This can be used to construct objects in the storage, i.e.,
     142             :    * setPointer(0, std::make_unique<T>(...));
     143             :    */
     144      578479 :   void setPointer(const std::size_t i, std::unique_ptr<T> && ptr)
     145             :   {
     146             :     mooseAssert(size() > i, "Invalid size");
     147      578479 :     _values[i] = std::move(ptr);
     148      578479 :   }
     149             : 
     150             :   /**
     151             :    * Adds the given object in \p ptr to the storage.
     152             :    */
     153     2336391 :   T & addPointer(std::unique_ptr<T> && ptr)
     154             :   {
     155             :     mooseAssert(ptr, "Null object");
     156     2336391 :     return *_values.emplace_back(std::move(ptr));
     157             :   }
     158             : 
     159             :   /**
     160             :    * Resizes the underlying vector.
     161             :    */
     162      383224 :   void resize(const std::size_t size) { _values.resize(size); }
     163             : 
     164             :   /**
     165             :    * Clears the underlying vector.
     166             :    */
     167             :   void clear() { _values.clear(); }
     168             : 
     169             : private:
     170             :   /**
     171             :    * Returns a read-only reference to the underlying unique pointer
     172             :    * at index \p i.
     173             :    */
     174   169650453 :   const std::unique_ptr<T> & pointerValue(const std::size_t i) const
     175             :   {
     176             :     mooseAssert(size() > i, "Invalid size");
     177   169650453 :     return _values[i];
     178             :   }
     179             :   /**
     180             :    * Returns a reference to the underlying unique pointer
     181             :    * at index \p i.
     182             :    */
     183           6 :   std::unique_ptr<T> & pointerValue(const std::size_t i)
     184             :   {
     185           6 :     return const_cast<std::unique_ptr<T> &>(std::as_const(*this).pointerValue(i));
     186             :   }
     187             : 
     188             :   friend void storeHelper<>(std::ostream & stream, UniqueStorage<T> &, void *);
     189             :   friend void loadHelper<>(std::istream & stream, UniqueStorage<T> &, void *);
     190             : 
     191             :   /// The underlying data
     192             :   values_type _values;
     193             : };

Generated by: LCOV version 1.14