LCOV - code coverage report
Current view: top level - atermpp/include/mcrl2/atermpp/detail - aterm_container.h (source / functions) Hit Total Coverage
Test: mcrl2_coverage.info.cleaned Lines: 81 83 97.6 %
Date: 2024-05-04 03:44:52 Functions: 103 153 67.3 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : // Author(s): Maurice Laveaux
       2             : // Copyright: see the accompanying file COPYING or copy at
       3             : // https://github.com/mCRL2org/mCRL2/blob/master/COPYING
       4             : //
       5             : // Distributed under the Boost Software License, Version 1.0.
       6             : // (See accompanying file LICENSE_1_0.txt or copy at
       7             : // http://www.boost.org/LICENSE_1_0.txt)
       8             : //
       9             : 
      10             : #ifndef MCRL2_ATERMPP_DETAIL_ATERM_CONTAINER_H
      11             : #define MCRL2_ATERMPP_DETAIL_ATERM_CONTAINER_H
      12             : 
      13             : #include <stack>
      14             : #include <type_traits>
      15             : #include "mcrl2/atermpp/aterm.h"
      16             : #include "mcrl2/atermpp/detail/aterm.h"
      17             : #include "mcrl2/atermpp/detail/aterm_pool_storage_implementation.h"
      18             : #include "mcrl2/utilities/noncopyable.h"
      19             : 
      20             : namespace atermpp
      21             : {
      22             : 
      23             : typedef std::stack<std::reference_wrapper<detail::_aterm>> term_mark_stack;
      24             : 
      25         436 : inline void mark_term(const aterm& t, term_mark_stack& todo)
      26             : {
      27         436 :   if (t.defined())
      28             :   {
      29         436 :     detail::mark_term(*atermpp::detail::address(t),todo);
      30             :   }
      31         436 : }
      32             : 
      33             : 
      34             : namespace detail
      35             : {
      36             : 
      37             : /// \brief Provides safe storage of unprotected_aterm instances in a container by marking
      38             : ///        them during garbage collection.
      39             : /// 
      40             : /// \details Can not be inherited since it is being registered during construction and the 
      41             : ///          vptr is being updated by inherited classes otherwise.
      42             : class aterm_container final : private mcrl2::utilities::noncopyable
      43             : {
      44             : public:
      45             :   aterm_container(std::function<void(term_mark_stack&)> mark_func, std::function<std::size_t()> size_func);
      46             :   ~aterm_container();
      47             : 
      48             :   /// \brief Ensure that all the terms in the containers.
      49         994 :   void mark(term_mark_stack& todo) const
      50             :   {
      51         994 :     mark_func(todo);
      52         994 :   }
      53             : 
      54         994 :   inline std::size_t size() const 
      55             :   {
      56         994 :     return size_func();
      57             :   }
      58             : 
      59             : private:
      60             :   std::function<void(term_mark_stack&)> mark_func;
      61             :   std::function<std::size_t()> size_func;
      62             : };
      63             : 
      64             : template<class T, typename Type = void >  
      65             : class reference_aterm;
      66             : 
      67             : 
      68             : template<class T>
      69             : struct is_pair_helper : public std::false_type
      70             : {};
      71             : 
      72             : template<class T, class U>
      73             : struct is_pair_helper<std::pair<T,U> > : public std::true_type
      74             : {};
      75             : 
      76             : template<class T>
      77             : struct is_pair : public is_pair_helper<typename std::decay<T>::type >
      78             : {};
      79             : 
      80             : template<class T>
      81             : struct is_reference_aterm_helper : public std::false_type
      82             : {};
      83             : 
      84             : template<class T>
      85             : struct is_reference_aterm_helper<reference_aterm<T> > : public std::true_type
      86             : {};
      87             : 
      88             : template<class T>
      89             : struct is_reference_aterm : public is_reference_aterm_helper<typename std::decay<T>::type >
      90             : {};
      91             : 
      92             : /// \brief Base class that should not be used. 
      93             : template<class T, typename Type >
      94             : class reference_aterm
      95             : {
      96             : protected:
      97             :   typedef typename std::decay<T>::type T_type;
      98             :   T_type m_t;
      99             : public:
     100             :   reference_aterm() = default;
     101             : 
     102       30159 :   reference_aterm(const T_type& other) noexcept
     103       30159 :    : m_t(other)
     104       30159 :   { }
     105             :  
     106             :   template <class... Args>
     107        4112 :   reference_aterm(Args&&... args) noexcept
     108        4112 :    : m_t(std::forward<Args>(args)...)
     109        4112 :   { }
     110             :  
     111             :   template <class... Args>
     112       12021 :   reference_aterm(const Args&... args) noexcept
     113       12021 :    : m_t(args...)
     114       12021 :   { }
     115             :  
     116             :   reference_aterm(T_type&& other) noexcept
     117             :    : m_t(std::forward(other))
     118             :   {}
     119             : 
     120             :   const reference_aterm& operator=(const T_type& other) noexcept
     121             :   {
     122             :     static_assert(std::is_base_of<aterm, T_type>::value);
     123             :     m_t=other;
     124             :     return m_t;
     125             :   }
     126             : 
     127             :   const reference_aterm& operator=(T_type&& other) noexcept
     128             :   {
     129             :     static_assert(std::is_base_of<aterm, T_type>::value);
     130             :     m_t = std::forward(other);
     131             :     return m_t;
     132             :   }
     133             : 
     134             :   /// Converts implicitly to a protected term of type T.
     135       85457 :   operator T_type&()
     136             :   {
     137       85457 :     return m_t;
     138             :   }
     139             : 
     140       20557 :   operator const T_type&() const
     141             :   {
     142       20557 :     return m_t;
     143             :   }
     144             : 
     145             :   /// For types that are not a std::pair, or a type convertible to an aterm
     146             :   /// it is necessary that a dedicated mark function is provided that calls mark_term
     147             :   /// on all aterm types in the class T, when this class is stored in an atermpp container.
     148             :   /// See below for an example, where the code is given for pairs, aterms and built in types.
     149             :   /// The container is traversed during garbage collection, such that all terms in these
     150             :   /// containers are protected individually, without putting them all explicitly in 
     151             :   /// protection sets. 
     152         218 :   void mark(std::stack<std::reference_wrapper<detail::_aterm>>& todo) const
     153             :   {
     154         218 :     m_t.mark(todo);
     155         218 :   }
     156             : 
     157             : };
     158             : 
     159             : /// \brief A reference aterm applied to fundamental types, such as int, bool. Nothing needs to happen with such
     160             : ///       terms. But a special class is needed, because such types are not classes, and we cannot derive from it.
     161             : template<class T>
     162             : class reference_aterm < T, typename std::enable_if<std::is_fundamental<typename std::decay<T>::type>::value>::type >
     163             : {
     164             : protected:
     165             :   typedef typename std::decay<T>::type T_type;
     166             :   T_type m_t;
     167             : 
     168             : public:
     169             : 
     170             :   /// \brief Default constructor.
     171             :   reference_aterm() noexcept = default;
     172             :   
     173        3892 :   reference_aterm(const T& other) noexcept
     174        3892 :    : m_t(other)
     175        3892 :   { }
     176             :   
     177             :   reference_aterm(T_type&& other) noexcept  
     178             :    : m_t(std::move(other))
     179             :   {} 
     180             :   
     181       42853 :   const T& operator=(const T& other) noexcept
     182             :   {
     183       42853 :     m_t=other;
     184       42853 :     return m_t;
     185             :   }
     186             : 
     187             :   const T& operator=(T&& other) noexcept
     188             :   {
     189             :     m_t = std::move(other);
     190             :     return m_t;
     191             :   }
     192             : 
     193             :   /// Converts implicitly to a protected term of type T.
     194    19683919 :   operator T&()
     195             :   {
     196    19683919 :     return m_t;
     197             :   } 
     198             : 
     199         137 :   operator const T&() const
     200             :   {
     201         137 :     return m_t;
     202             :   }
     203             : 
     204        3471 :   void mark(std::stack<std::reference_wrapper<detail::_aterm>>& /* todo */) const
     205             :   {
     206             :     /* Do nothing */
     207        3471 :   } 
     208             : 
     209             :   bool operator==(const reference_aterm& other) const
     210             :   {
     211             :     return m_t==other.m_t;
     212             :   }
     213             : 
     214             : };
     215             : 
     216             : /// \brief An unprotected term that is stored inside an aterm_container.
     217             : template<typename T>
     218             : class reference_aterm<T, typename std::enable_if<std::is_base_of<aterm, T>::value>::type> : public unprotected_aterm
     219             : {
     220             : public:
     221             :   /// \brief Default constructor.
     222      336128 :   reference_aterm() noexcept = default;
     223             : 
     224             :   explicit reference_aterm(const unprotected_aterm& other) noexcept
     225             :   {
     226             :     m_term = detail::address(other);
     227             :   }
     228             : 
     229   141183508 :   reference_aterm(const T& other) noexcept
     230   141183508 :    : unprotected_aterm(detail::address(other))
     231   141183508 :   { }
     232             : 
     233             :   reference_aterm(unprotected_aterm&& other) noexcept
     234             :    : unprotected_aterm(detail::address(other))
     235             :   {
     236             :   }
     237             : 
     238             :   const reference_aterm& operator=(const unprotected_aterm& other) noexcept;
     239             :   const reference_aterm& operator=(unprotected_aterm&& other) noexcept;
     240             : 
     241             :   /// Converts implicitly to a protected term of type T.
     242     7782144 :   operator T&()
     243             :   {
     244             :     static_assert(std::is_base_of<aterm, T>::value,"Term must be derived from an aterm");
     245             :     static_assert(sizeof(T)==sizeof(std::size_t),"Term derived from an aterm must not have extra fields");
     246     7782144 :     return reinterpret_cast<T&>(*this);
     247             :   }
     248             : 
     249     3811905 :   operator const T&() const
     250             :   {
     251             :     static_assert(std::is_base_of<aterm, T>::value,"Term must be derived from an aterm");
     252             :     static_assert(sizeof(T)==sizeof(std::size_t),"Term derived from an aterm must not have extra fields");
     253     3811905 :     return reinterpret_cast<const T&>(*this);
     254             : 
     255             :   }
     256             : 
     257     6548165 :   void mark(std::stack<std::reference_wrapper<detail::_aterm>>& todo) const
     258             :   {
     259     6548165 :     if (defined())
     260             :     {
     261     6546852 :       mark_term(*m_term,todo);
     262             :     }
     263     6548165 :   }
     264             : };
     265             : 
     266             : template<typename T>
     267             : typename std::pair<typename std::conditional<is_reference_aterm<typename T::first_type>::value,
     268             :                                                       typename T::first_type,
     269             :                                                       reference_aterm< typename T::first_type > >::type,
     270             :                                           typename std::conditional<is_reference_aterm<typename T::second_type>::value,
     271             :                                                       typename T::second_type,
     272             :                                                       reference_aterm< typename T::second_type > >::type >
     273    19767384 : reference_aterm_pair_constructor_helper(const T& other)
     274             : {
     275             :   if constexpr (is_reference_aterm<typename T::first_type>::value && is_reference_aterm<typename T::second_type>::value)
     276             :   {
     277        3494 :     return other; 
     278             :   }
     279             :   else if constexpr (is_reference_aterm<typename T::first_type>::value && !is_reference_aterm<typename T::second_type>::value)
     280             :   {
     281             :     return std::pair(other.first, reference_aterm<typename T::second_type>(other.second));
     282             :   }
     283             :   else if constexpr (!is_reference_aterm<typename T::first_type>::value && is_reference_aterm<typename T::second_type>::value)
     284             :   {
     285             :     return std::pair(reference_aterm<typename T::first_type>(other.first),other.second);
     286             :   }
     287             :   else 
     288             :   { 
     289             :     static_assert(!is_reference_aterm<typename T::first_type>::value && 
     290             :                   !is_reference_aterm<typename T::second_type>::value,"Logic error");
     291             :   
     292    19763890 :     return std::pair(reference_aterm<typename T::first_type>(other.first), reference_aterm<typename T::second_type>(other.second));
     293             :   }
     294             : }
     295             : 
     296             : 
     297             : 
     298             : 
     299             : /// \brief A pair that is stored into an atermpp container. This class takes care that all aterms that occur (recursively) inside
     300             : ///        such a pair are marked, whears non-aterm types are not marked. 
     301             : template<typename T>
     302             : class reference_aterm<T, typename std::enable_if<is_pair<T>::value>::type > : 
     303             :                          public std::pair<typename std::conditional<is_reference_aterm<typename T::first_type>::value,
     304             :                                                       typename T::first_type,
     305             :                                                       reference_aterm< typename T::first_type > >::type, 
     306             :                                           typename std::conditional<is_reference_aterm<typename T::second_type>::value,
     307             :                                                       typename T::second_type,
     308             :                                                       reference_aterm< typename T::second_type > >::type >
     309             : {
     310             : protected:
     311             :   typedef std::pair<typename std::conditional<is_reference_aterm<typename T::first_type>::value,
     312             :                                           typename T::first_type,
     313             :                                           reference_aterm< typename T::first_type > >::type,
     314             :                     typename std::conditional<is_reference_aterm<typename T::second_type>::value,
     315             :                                           typename T::second_type,
     316             :                                           reference_aterm< typename T::second_type > >::type >  super;
     317             :   typedef T std_pair;
     318             : 
     319             : public:
     320             :   /// \brief Default constructor.
     321             :   reference_aterm() = default;
     322             : 
     323       46324 :   reference_aterm(const reference_aterm& other)
     324       46324 :     : super()
     325             :   {
     326       46324 :     *this = other;
     327       46324 :   }
     328             : 
     329    19767384 :   reference_aterm(const std_pair& other)
     330    19767384 :     : super(reference_aterm_pair_constructor_helper(other))
     331    19767384 :   {}
     332             : 
     333             :   reference_aterm(std_pair&& other)
     334             :    : super(reference_aterm<typename T::first_type >(std::move(other.first)),
     335             :            reference_aterm<typename T::second_type>(std::move(other.second)))
     336             :   {} 
     337             : 
     338       46324 :   reference_aterm& operator=(const reference_aterm& other)
     339             :   {
     340       46324 :     super::first=other.first;
     341       46324 :     super::second=other.second;
     342       46324 :     return *this;
     343             :   }
     344             : 
     345             :   const reference_aterm& operator=(const std_pair& other)
     346             :   {
     347             :     super::first=other.first;
     348             :     super::second=other.second;
     349             :     return *this;
     350             :   }
     351             : 
     352             :   reference_aterm& operator=(reference_aterm&& other)
     353             :   {
     354             :     super::first = std::move(other.first);
     355             :     super::second = std::move(other.second);
     356             :     return *this;
     357             :   }
     358             : 
     359             :   const reference_aterm& operator=(std_pair&& other)
     360             :   {
     361             :     super::first = other.first;
     362             :     super::second = other.second;
     363             :     return *this;
     364             :   }
     365             : 
     366             : 
     367             :   /// Converts implicitly to a protected term of type std::pair<T,U>..
     368             :   operator std_pair&()
     369             :   {
     370             :     return reinterpret_cast<std_pair>(*this);
     371             :   }
     372             : 
     373    19763890 :   operator const std_pair&() const
     374             :   {
     375    19763890 :     return *reinterpret_cast<std_pair const*>(this);
     376             :   }
     377             : 
     378        6965 :   void mark(std::stack<std::reference_wrapper<detail::_aterm>>& todo) const
     379             :   {
     380             :     if constexpr (is_reference_aterm<typename T::first_type>::value)
     381             :     {
     382        3494 :       super::first.mark(todo);
     383             :     }
     384             :     else 
     385             :     {
     386        3471 :       reference_aterm<typename T::first_type>(super::first).mark(todo);
     387             :     }
     388             : 
     389             :     if constexpr (is_reference_aterm<typename T::second_type>::value)
     390             :     {
     391        3494 :       super::second.mark(todo);
     392             :     }
     393             :     else 
     394             :     {
     395        3471 :       reference_aterm<typename T::second_type>(super::second).mark(todo);
     396             :     }
     397        6965 :   }
     398             : }; 
     399             : 
     400             : template<typename T, typename Allocator>
     401             : class aterm_allocator
     402             : {
     403             : public:
     404             :   using value_type = T;
     405             :   using size_type = std::size_t;
     406             :   using difference_type = std::ptrdiff_t;
     407             : 
     408             :   //static_assert(std::is_same_v<value_type, typename Allocator::value_type>, "Types should be equal");
     409             : 
     410             :   template <class U>
     411             :   struct rebind
     412             :   {
     413             :       typedef aterm_allocator<U, typename Allocator::template rebind<U>::other> other;
     414             :   };
     415             : 
     416             :   aterm_allocator() = default;
     417             : 
     418             :   /// \details The unused parameter is to make the interface equivalent
     419             :   ///          to the allocator.
     420             :   T* allocate(size_type n, const void* hint = nullptr)
     421             :   {
     422             :     return m_allocator.allocate(n, hint);
     423             :   }
     424             : 
     425             :   /// \details The unused parameter is to make the interface equivalent
     426             :   ///          to the allocator.
     427             :   void deallocate(T* p, size_type n);
     428             : 
     429             :   // Move assignment and construction is possible.
     430             :   aterm_allocator(aterm_allocator&&) = default;
     431             :   aterm_allocator& operator=(aterm_allocator&&) = default;
     432             : 
     433             : private:
     434             :   Allocator m_allocator;
     435             : };
     436             : 
     437             : template<typename Container>
     438             : class generic_aterm_container
     439             : {
     440             : public:
     441             :   /// \brief Constructor
     442       52057 :   generic_aterm_container(const Container& container)
     443       53051 :    : m_container(std::bind([&container](term_mark_stack& todo) {   
     444             :       // Marking contained terms.       
     445     6545896 :       for (const typename Container::value_type& element: container) 
     446             :       {
     447             :         static_assert(is_reference_aterm<reference_aterm<typename Container::value_type> >::value);
     448             :         if constexpr (is_reference_aterm<typename Container::value_type>::value)
     449             :         {
     450             :           static_assert(is_reference_aterm<typename Container::value_type >::value);
     451     6541395 :           element.mark(todo);
     452             :         }
     453             :         else
     454             :         {
     455             :           static_assert(!is_reference_aterm<typename Container::value_type >::value);
     456        3494 :           reference_aterm<typename Container::value_type>(element).mark(todo);
     457             :         }
     458             :       }        
     459             :      }, std::placeholders::_1),
     460       52135 :      std::bind([&container]() -> std::size_t {  
     461             :       // Return the number of elements in the container.
     462         994 :       return container.size();
     463             :      }))
     464       52057 :   {}
     465             : 
     466             :   // Container is a reference so unclear what to do in these cases.
     467             :   generic_aterm_container(const generic_aterm_container&) = delete;
     468             :   generic_aterm_container(generic_aterm_container&&) = delete;
     469             : 
     470             :   // It is fine here if the container gets updated, but the functions stay the same.
     471           0 :   generic_aterm_container& operator=(const generic_aterm_container&) 
     472             :   {
     473           0 :     return *this;
     474             :   };
     475             : 
     476             :   generic_aterm_container& operator=(generic_aterm_container&) 
     477             :   {
     478             :     return *this;
     479             :   }
     480             : 
     481             : protected:
     482             :   aterm_container m_container;
     483             : };
     484             : 
     485             : } // namespace detail
     486             : } // namespace atermpp
     487             : 
     488             : namespace std
     489             : {
     490             : 
     491             : /// \brief specialization of the standard std::hash function.
     492             : template<class T>
     493             : struct hash<atermpp::detail::reference_aterm<T>>
     494             : {
     495    20623977 :   std::size_t operator()(const atermpp::detail::reference_aterm<T>& t) const
     496             :   {
     497    20623977 :     return std::hash<T>()(t);
     498             :   }
     499             : };
     500             : 
     501             : } // namespace std
     502             : 
     503             : #endif // MCRL2_ATERMPP_DETAIL_ATERM_CONTAINER_H

Generated by: LCOV version 1.14