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
|