1 | /* |
2 | * Distributed under the Boost Software License, Version 1.0. |
3 | * (See accompanying file LICENSE_1_0.txt or copy at |
4 | * https://www.boost.org/LICENSE_1_0.txt) |
5 | * |
6 | * Copyright (c) 2022-2024 Andrey Semashev |
7 | */ |
8 | /*! |
9 | * \file scope/unique_resource.hpp |
10 | * |
11 | * This header contains definition of \c unique_resource template. |
12 | */ |
13 | |
14 | #ifndef BOOST_SCOPE_UNIQUE_RESOURCE_HPP_INCLUDED_ |
15 | #define BOOST_SCOPE_UNIQUE_RESOURCE_HPP_INCLUDED_ |
16 | |
17 | #include <new> // for placement new |
18 | #include <type_traits> |
19 | #include <boost/core/addressof.hpp> |
20 | #include <boost/core/invoke_swap.hpp> |
21 | #include <boost/scope/unique_resource_fwd.hpp> |
22 | #include <boost/scope/detail/config.hpp> |
23 | #include <boost/scope/detail/compact_storage.hpp> |
24 | #include <boost/scope/detail/move_or_copy_assign_ref.hpp> |
25 | #include <boost/scope/detail/move_or_copy_construct_ref.hpp> |
26 | #include <boost/scope/detail/is_nonnull_default_constructible.hpp> |
27 | #include <boost/scope/detail/type_traits/is_swappable.hpp> |
28 | #include <boost/scope/detail/type_traits/is_nothrow_swappable.hpp> |
29 | #include <boost/scope/detail/type_traits/is_nothrow_invocable.hpp> |
30 | #include <boost/scope/detail/type_traits/negation.hpp> |
31 | #include <boost/scope/detail/type_traits/conjunction.hpp> |
32 | #include <boost/scope/detail/type_traits/disjunction.hpp> |
33 | #include <boost/scope/detail/header.hpp> |
34 | |
35 | #ifdef BOOST_HAS_PRAGMA_ONCE |
36 | #pragma once |
37 | #endif |
38 | |
39 | namespace boost { |
40 | namespace scope { |
41 | |
42 | #if !defined(BOOST_NO_CXX17_FOLD_EXPRESSIONS) && !defined(BOOST_NO_CXX17_AUTO_NONTYPE_TEMPLATE_PARAMS) |
43 | |
44 | /*! |
45 | * \brief Simple resource traits for one or more unallocated resource values. |
46 | * |
47 | * This class template generates resource traits for `unique_resource` that specify |
48 | * one or more unallocated resource values. The first value, specified in the \c DefaultValue |
49 | * non-type template parameter, is considered the default. The other values, listed in |
50 | * \c UnallocatedValues, are optional. Any resource values other than \c DefaultValue |
51 | * or listed in \c UnallocatedValues are considered as allocated. |
52 | * |
53 | * In order for the generated resource traits to enable optimized implementation of |
54 | * `unique_resource`, the resource type must support non-throwing construction and assignment |
55 | * from, and comparison for (in)equality with \c DefaultValue or any of the resource |
56 | * values listed in \c UnallocatedValues. |
57 | */ |
58 | template< auto DefaultValue, auto... UnallocatedValues > |
59 | struct unallocated_resource |
60 | { |
61 | //! Returns the default resource value |
62 | static decltype(DefaultValue) make_default() noexcept |
63 | { |
64 | return DefaultValue; |
65 | } |
66 | |
67 | //! Tests if \a res is an allocated resource value |
68 | template< typename Resource > |
69 | static bool is_allocated(Resource const& res) noexcept |
70 | { |
71 | static_assert(noexcept(res != DefaultValue && (... && (res != UnallocatedValues))), |
72 | "Invalid unallocated resource value types: comparing resource values with the unallocated values must be noexcept" ); |
73 | return res != DefaultValue && (... && (res != UnallocatedValues)); |
74 | } |
75 | }; |
76 | |
77 | #endif // !defined(BOOST_NO_CXX17_FOLD_EXPRESSIONS) && !defined(BOOST_NO_CXX17_AUTO_NONTYPE_TEMPLATE_PARAMS) |
78 | |
79 | struct default_resource_t { }; |
80 | |
81 | //! Keyword representing default, unallocated resource argument |
82 | BOOST_INLINE_VARIABLE constexpr default_resource_t default_resource = { }; |
83 | |
84 | namespace detail { |
85 | |
86 | // The type trait indicates whether \c T is a possibly qualified \c default_resource_t type |
87 | template< typename T > |
88 | struct is_default_resource : public std::false_type { }; |
89 | template< > |
90 | struct is_default_resource< default_resource_t > : public std::true_type { }; |
91 | template< > |
92 | struct is_default_resource< const default_resource_t > : public std::true_type { }; |
93 | template< > |
94 | struct is_default_resource< volatile default_resource_t > : public std::true_type { }; |
95 | template< > |
96 | struct is_default_resource< const volatile default_resource_t > : public std::true_type { }; |
97 | template< typename T > |
98 | struct is_default_resource< T& > : public is_default_resource< T >::type { }; |
99 | |
100 | // Lightweight reference wrapper |
101 | template< typename T > |
102 | class ref_wrapper |
103 | { |
104 | private: |
105 | T* m_value; |
106 | |
107 | public: |
108 | explicit |
109 | #if !defined(BOOST_CORE_NO_CONSTEXPR_ADDRESSOF) |
110 | constexpr |
111 | #endif |
112 | ref_wrapper(T& value) noexcept : |
113 | m_value(boost::addressof(value)) |
114 | { |
115 | } |
116 | |
117 | ref_wrapper& operator= (T& value) noexcept |
118 | { |
119 | m_value = boost::addressof(value); |
120 | return *this; |
121 | } |
122 | |
123 | ref_wrapper(T&&) = delete; |
124 | ref_wrapper& operator= (T&&) = delete; |
125 | |
126 | operator T& () const noexcept |
127 | { |
128 | return *m_value; |
129 | } |
130 | |
131 | template< typename... Args > |
132 | void operator() (Args&&... args) const noexcept(detail::is_nothrow_invocable< T&, Args&&... >::value) |
133 | { |
134 | (*m_value)(static_cast< Args&& >(args)...); |
135 | } |
136 | }; |
137 | |
138 | template< typename T > |
139 | struct wrap_reference |
140 | { |
141 | using type = T; |
142 | }; |
143 | |
144 | template< typename T > |
145 | struct wrap_reference< T& > |
146 | { |
147 | using type = ref_wrapper< T >; |
148 | }; |
149 | |
150 | template< typename Resource, bool UseCompactStorage > |
151 | class resource_holder : |
152 | public detail::compact_storage< typename wrap_reference< Resource >::type > |
153 | { |
154 | public: |
155 | using resource_type = Resource; |
156 | using internal_resource_type = typename wrap_reference< resource_type >::type; |
157 | |
158 | private: |
159 | using resource_base = detail::compact_storage< internal_resource_type >; |
160 | |
161 | public: |
162 | template< |
163 | bool Requires = std::is_default_constructible< internal_resource_type >::value, |
164 | typename = typename std::enable_if< Requires >::type |
165 | > |
166 | constexpr resource_holder() noexcept(std::is_nothrow_default_constructible< internal_resource_type >::value) : |
167 | resource_base() |
168 | { |
169 | } |
170 | |
171 | template< |
172 | typename R, |
173 | typename = typename std::enable_if< std::is_constructible< internal_resource_type, R >::value >::type |
174 | > |
175 | explicit resource_holder(R&& res) noexcept(std::is_nothrow_constructible< internal_resource_type, R >::value) : |
176 | resource_base(static_cast< R&& >(res)) |
177 | { |
178 | } |
179 | |
180 | template< |
181 | typename R, |
182 | typename D, |
183 | typename = typename std::enable_if< std::is_constructible< internal_resource_type, R >::value >::type |
184 | > |
185 | explicit resource_holder(R&& res, D&& del, bool allocated) noexcept(std::is_nothrow_constructible< internal_resource_type, R >::value) : |
186 | resource_holder(static_cast< R&& >(res), static_cast< D&& >(del), allocated, typename std::is_nothrow_constructible< resource_type, R >::type()) |
187 | { |
188 | } |
189 | |
190 | resource_type& get() noexcept |
191 | { |
192 | return resource_base::get(); |
193 | } |
194 | |
195 | resource_type const& get() const noexcept |
196 | { |
197 | return resource_base::get(); |
198 | } |
199 | |
200 | internal_resource_type& get_internal() noexcept |
201 | { |
202 | return resource_base::get(); |
203 | } |
204 | |
205 | internal_resource_type const& get_internal() const noexcept |
206 | { |
207 | return resource_base::get(); |
208 | } |
209 | |
210 | void move_from(internal_resource_type&& that) noexcept(std::is_nothrow_move_assignable< internal_resource_type >::value) |
211 | { |
212 | resource_base::get() = static_cast< internal_resource_type&& >(that); |
213 | } |
214 | |
215 | private: |
216 | template< typename R, typename D > |
217 | explicit resource_holder(R&& res, D&& del, bool allocated, std::true_type) noexcept : |
218 | resource_base(static_cast< R&& >(res)) |
219 | { |
220 | } |
221 | |
222 | template< typename R, typename D > |
223 | explicit resource_holder(R&& res, D&& del, bool allocated, std::false_type) try : |
224 | resource_base(res) |
225 | { |
226 | } |
227 | catch (...) |
228 | { |
229 | if (allocated) |
230 | del(res); |
231 | } |
232 | }; |
233 | |
234 | template< typename Resource > |
235 | class resource_holder< Resource, false > |
236 | { |
237 | public: |
238 | using resource_type = Resource; |
239 | using internal_resource_type = typename wrap_reference< resource_type >::type; |
240 | |
241 | private: |
242 | // Note: Not using compact_storage since we will need to reuse storage for this complete object in move_from |
243 | internal_resource_type m_resource; |
244 | |
245 | public: |
246 | template< |
247 | bool Requires = std::is_default_constructible< internal_resource_type >::value, |
248 | typename = typename std::enable_if< Requires >::type |
249 | > |
250 | constexpr resource_holder() noexcept(std::is_nothrow_default_constructible< internal_resource_type >::value) : |
251 | m_resource() |
252 | { |
253 | } |
254 | |
255 | template< |
256 | typename R, |
257 | typename = typename std::enable_if< std::is_constructible< internal_resource_type, R >::value >::type |
258 | > |
259 | explicit resource_holder(R&& res) noexcept(std::is_nothrow_constructible< internal_resource_type, R >::value) : |
260 | m_resource(static_cast< R&& >(res)) |
261 | { |
262 | } |
263 | |
264 | template< |
265 | typename R, |
266 | typename D, |
267 | typename = typename std::enable_if< std::is_constructible< internal_resource_type, R >::value >::type |
268 | > |
269 | explicit resource_holder(R&& res, D&& del, bool allocated) noexcept(std::is_nothrow_constructible< internal_resource_type, R >::value) : |
270 | resource_holder(static_cast< R&& >(res), static_cast< D&& >(del), allocated, typename std::is_nothrow_constructible< resource_type, R >::type()) |
271 | { |
272 | } |
273 | |
274 | resource_type& get() noexcept |
275 | { |
276 | return m_resource; |
277 | } |
278 | |
279 | resource_type const& get() const noexcept |
280 | { |
281 | return m_resource; |
282 | } |
283 | |
284 | internal_resource_type& get_internal() noexcept |
285 | { |
286 | return m_resource; |
287 | } |
288 | |
289 | internal_resource_type const& get_internal() const noexcept |
290 | { |
291 | return m_resource; |
292 | } |
293 | |
294 | void move_from(internal_resource_type&& that) |
295 | noexcept(std::is_nothrow_constructible< internal_resource_type, typename detail::move_or_copy_construct_ref< resource_type >::type >::value) |
296 | { |
297 | internal_resource_type* p = boost::addressof(m_resource); |
298 | p->~internal_resource_type(); |
299 | new (p) internal_resource_type(static_cast< typename detail::move_or_copy_construct_ref< resource_type >::type >(that)); |
300 | } |
301 | |
302 | private: |
303 | template< typename R, typename D > |
304 | explicit resource_holder(R&& res, D&& del, bool allocated, std::true_type) noexcept : |
305 | m_resource(static_cast< R&& >(res)) |
306 | { |
307 | } |
308 | |
309 | template< typename R, typename D > |
310 | explicit resource_holder(R&& res, D&& del, bool allocated, std::false_type) try : |
311 | m_resource(res) |
312 | { |
313 | } |
314 | catch (...) |
315 | { |
316 | if (allocated) |
317 | del(res); |
318 | } |
319 | }; |
320 | |
321 | template< typename Resource, typename Deleter > |
322 | class deleter_holder : |
323 | public detail::compact_storage< typename wrap_reference< Deleter >::type > |
324 | { |
325 | public: |
326 | using resource_type = Resource; |
327 | using deleter_type = Deleter; |
328 | using internal_deleter_type = typename wrap_reference< deleter_type >::type; |
329 | |
330 | private: |
331 | using deleter_base = detail::compact_storage< internal_deleter_type >; |
332 | |
333 | public: |
334 | template< |
335 | bool Requires = detail::is_nonnull_default_constructible< internal_deleter_type >::value, |
336 | typename = typename std::enable_if< Requires >::type |
337 | > |
338 | constexpr deleter_holder() noexcept(detail::is_nothrow_nonnull_default_constructible< internal_deleter_type >::value) : |
339 | deleter_base() |
340 | { |
341 | } |
342 | |
343 | template< |
344 | typename D, |
345 | typename = typename std::enable_if< std::is_constructible< internal_deleter_type, D >::value >::type |
346 | > |
347 | explicit deleter_holder(D&& del) noexcept(std::is_nothrow_constructible< internal_deleter_type, D >::value) : |
348 | deleter_base(static_cast< D&& >(del)) |
349 | { |
350 | } |
351 | |
352 | template< |
353 | typename D, |
354 | typename = typename std::enable_if< std::is_constructible< internal_deleter_type, D >::value >::type |
355 | > |
356 | explicit deleter_holder(D&& del, resource_type& res, bool allocated) noexcept(std::is_nothrow_constructible< internal_deleter_type, D >::value) : |
357 | deleter_holder(static_cast< D&& >(del), res, allocated, typename std::is_nothrow_constructible< internal_deleter_type, D >::type()) |
358 | { |
359 | } |
360 | |
361 | deleter_type& get() noexcept |
362 | { |
363 | return deleter_base::get(); |
364 | } |
365 | |
366 | deleter_type const& get() const noexcept |
367 | { |
368 | return deleter_base::get(); |
369 | } |
370 | |
371 | internal_deleter_type& get_internal() noexcept |
372 | { |
373 | return deleter_base::get(); |
374 | } |
375 | |
376 | internal_deleter_type const& get_internal() const noexcept |
377 | { |
378 | return deleter_base::get(); |
379 | } |
380 | |
381 | private: |
382 | template< typename D > |
383 | explicit deleter_holder(D&& del, resource_type& res, bool allocated, std::true_type) noexcept : |
384 | deleter_base(static_cast< D&& >(del)) |
385 | { |
386 | } |
387 | |
388 | template< typename D > |
389 | explicit deleter_holder(D&& del, resource_type& res, bool allocated, std::false_type) try : |
390 | deleter_base(del) |
391 | { |
392 | } |
393 | catch (...) |
394 | { |
395 | if (BOOST_LIKELY(allocated)) |
396 | del(res); |
397 | } |
398 | }; |
399 | |
400 | /* |
401 | * This metafunction indicates whether \c resource_holder should use \c compact_storage |
402 | * to optimize storage for the resource object. Its definition must be coherent with |
403 | * `resource_holder::move_from` definition and move constructor implementation in |
404 | * \c unique_resource_data. |
405 | * |
406 | * There is one tricky case with \c unique_resource move constructor, when the resource move |
407 | * constructor is noexcept and deleter's move and copy constructors are not. It is possible |
408 | * that \c unique_resource_data move constructor moves the resource into the object being |
409 | * constructed but fails to construct the deleter. In this case we want to move the resource |
410 | * back to the original \c unique_resource_data object (which is guaranteed to not throw since |
411 | * the resource's move constructor is non-throwing). |
412 | * |
413 | * However, if we use the move constructor to move the resource back, we need to use placement |
414 | * new, and this only lets us create a complete object of the resource type, which prohibits |
415 | * the use of \c compact_storage, as it may create the resource object as a base subobject of |
416 | * \c compact_storage. Using placement new on a base subobject may corrupt data that is placed |
417 | * in the trailing padding bits of the resource type. |
418 | * |
419 | * To work around this limitation, we also test if move assignment of the resource type is |
420 | * also non-throwing (which is reasonable to expect, given that the move constructor is |
421 | * non-throwing). If it is, we can avoid having to destroy and move-construct the resource and |
422 | * use move-assignment instead. This doesn't require a complete object of the resource type |
423 | * and allows us to use \c compact_storage. If move assignment is not noexcept then we have |
424 | * to use the move constructor and disable the \c compact_storage optimization. |
425 | * |
426 | * So this trait has to detect (a) whether we are affected by this tricky case of the |
427 | * \c unique_resource move constructor in the first place and (b) whether we can use move |
428 | * assignment to move the resource back to the original \c unique_resource object. If we're |
429 | * not affected or we can use move assignment then we enable \c compact_storage. |
430 | */ |
431 | template< typename Resource, typename Deleter > |
432 | using use_resource_compact_storage = detail::disjunction< |
433 | std::is_nothrow_move_assignable< typename wrap_reference< Resource >::type >, |
434 | std::is_nothrow_constructible< typename wrap_reference< Deleter >::type, typename detail::move_or_copy_construct_ref< Deleter >::type >, |
435 | detail::negation< std::is_nothrow_constructible< typename wrap_reference< Resource >::type, typename detail::move_or_copy_construct_ref< Resource >::type > > |
436 | >; |
437 | |
438 | template< typename Resource, typename Deleter, typename Traits > |
439 | class unique_resource_data : |
440 | public detail::resource_holder< Resource, use_resource_compact_storage< Resource, Deleter >::value >, |
441 | public detail::deleter_holder< Resource, Deleter > |
442 | { |
443 | public: |
444 | using resource_type = Resource; |
445 | using deleter_type = Deleter; |
446 | using traits_type = Traits; |
447 | |
448 | private: |
449 | using resource_holder = detail::resource_holder< resource_type, use_resource_compact_storage< resource_type, deleter_type >::value >; |
450 | using deleter_holder = detail::deleter_holder< resource_type, deleter_type >; |
451 | using result_of_make_default = decltype(traits_type::make_default()); |
452 | |
453 | public: |
454 | using internal_resource_type = typename resource_holder::internal_resource_type; |
455 | using internal_deleter_type = typename deleter_holder::internal_deleter_type; |
456 | |
457 | static_assert(noexcept(traits_type::make_default()), "Invalid unique_resource resource traits: make_default must be noexcept" ); |
458 | static_assert(std::is_nothrow_assignable< internal_resource_type&, result_of_make_default >::value, |
459 | "Invalid unique_resource resource traits: resource must be nothrow-assignable from the result of make_default" ); |
460 | static_assert(noexcept(traits_type::is_allocated(std::declval< resource_type const& >())), "Invalid unique_resource resource traits: is_allocated must be noexcept" ); |
461 | |
462 | public: |
463 | template< |
464 | bool Requires = detail::conjunction< |
465 | std::is_constructible< resource_holder, result_of_make_default >, |
466 | std::is_default_constructible< deleter_holder > |
467 | >::value, |
468 | typename = typename std::enable_if< Requires >::type |
469 | > |
470 | constexpr unique_resource_data() |
471 | noexcept(detail::conjunction< |
472 | std::is_nothrow_constructible< resource_holder, result_of_make_default >, |
473 | std::is_nothrow_default_constructible< deleter_holder > |
474 | >::value) : |
475 | resource_holder(traits_type::make_default()), |
476 | deleter_holder() |
477 | { |
478 | } |
479 | |
480 | unique_resource_data(unique_resource_data const&) = delete; |
481 | unique_resource_data& operator= (unique_resource_data const&) = delete; |
482 | |
483 | unique_resource_data(unique_resource_data&& that) |
484 | noexcept(detail::conjunction< |
485 | std::is_nothrow_constructible< internal_resource_type, typename detail::move_or_copy_construct_ref< resource_type >::type >, |
486 | std::is_nothrow_constructible< internal_deleter_type, typename detail::move_or_copy_construct_ref< deleter_type >::type > |
487 | >::value) : |
488 | unique_resource_data |
489 | ( |
490 | static_cast< unique_resource_data&& >(that), |
491 | typename std::is_nothrow_constructible< internal_resource_type, typename detail::move_or_copy_construct_ref< resource_type >::type >::type(), |
492 | typename std::is_nothrow_constructible< internal_deleter_type, typename detail::move_or_copy_construct_ref< deleter_type >::type >::type() |
493 | ) |
494 | { |
495 | } |
496 | |
497 | template< |
498 | typename D, |
499 | typename = typename std::enable_if< detail::conjunction< |
500 | std::is_constructible< resource_holder, result_of_make_default >, |
501 | std::is_constructible< deleter_holder, D > |
502 | >::value >::type |
503 | > |
504 | explicit unique_resource_data(default_resource_t, D&& del) |
505 | noexcept(detail::conjunction< |
506 | std::is_nothrow_constructible< resource_holder, result_of_make_default >, |
507 | std::is_nothrow_constructible< deleter_holder, D > |
508 | >::value) : |
509 | resource_holder(traits_type::make_default()), |
510 | deleter_holder(static_cast< D&& >(del)) |
511 | { |
512 | } |
513 | |
514 | template< |
515 | typename R, |
516 | typename D, |
517 | typename = typename std::enable_if< detail::conjunction< |
518 | detail::negation< detail::is_default_resource< R > >, |
519 | std::is_constructible< resource_holder, R, D, bool >, |
520 | std::is_constructible< deleter_holder, D, resource_type&, bool > |
521 | >::value >::type |
522 | > |
523 | explicit unique_resource_data(R&& res, D&& del) |
524 | noexcept(detail::conjunction< |
525 | std::is_nothrow_constructible< resource_holder, R, D, bool >, |
526 | std::is_nothrow_constructible< deleter_holder, D, resource_type&, bool > |
527 | >::value) : |
528 | unique_resource_data(static_cast< R&& >(res), static_cast< D&& >(del), traits_type::is_allocated(res)) // don't forward res to is_allocated to make sure res is not moved-from on resource construction |
529 | { |
530 | // Since res may not be of the resource type, the is_allocated call made above may require a type conversion or pick a different overload. |
531 | // We still require it to be noexcept, as we need to know whether we should deallocate it. Otherwise we may leak the resource. |
532 | static_assert(noexcept(traits_type::is_allocated(res)), "Invalid unique_resource resource traits: is_allocated must be noexcept" ); |
533 | } |
534 | |
535 | template< |
536 | bool Requires = detail::conjunction< |
537 | std::is_assignable< internal_resource_type&, typename detail::move_or_copy_assign_ref< resource_type >::type >, |
538 | std::is_assignable< internal_deleter_type&, typename detail::move_or_copy_assign_ref< deleter_type >::type > |
539 | >::value |
540 | > |
541 | typename std::enable_if< Requires, unique_resource_data& >::type operator= (unique_resource_data&& that) |
542 | noexcept(detail::conjunction< |
543 | std::is_nothrow_assignable< internal_resource_type&, typename detail::move_or_copy_assign_ref< resource_type >::type >, |
544 | std::is_nothrow_assignable< internal_deleter_type&, typename detail::move_or_copy_assign_ref< deleter_type >::type > |
545 | >::value) |
546 | { |
547 | assign(static_cast< unique_resource_data&& >(that), typename std::is_nothrow_move_assignable< internal_deleter_type >::type()); |
548 | return *this; |
549 | } |
550 | |
551 | resource_type& get_resource() noexcept |
552 | { |
553 | return resource_holder::get(); |
554 | } |
555 | |
556 | resource_type const& get_resource() const noexcept |
557 | { |
558 | return resource_holder::get(); |
559 | } |
560 | |
561 | internal_resource_type& get_internal_resource() noexcept |
562 | { |
563 | return resource_holder::get_internal(); |
564 | } |
565 | |
566 | internal_resource_type const& get_internal_resource() const noexcept |
567 | { |
568 | return resource_holder::get_internal(); |
569 | } |
570 | |
571 | deleter_type& get_deleter() noexcept |
572 | { |
573 | return deleter_holder::get(); |
574 | } |
575 | |
576 | deleter_type const& get_deleter() const noexcept |
577 | { |
578 | return deleter_holder::get(); |
579 | } |
580 | |
581 | internal_deleter_type& get_internal_deleter() noexcept |
582 | { |
583 | return deleter_holder::get_internal(); |
584 | } |
585 | |
586 | internal_deleter_type const& get_internal_deleter() const noexcept |
587 | { |
588 | return deleter_holder::get_internal(); |
589 | } |
590 | |
591 | bool is_allocated() const noexcept |
592 | { |
593 | return traits_type::is_allocated(get_resource()); |
594 | } |
595 | |
596 | void set_unallocated() noexcept |
597 | { |
598 | get_internal_resource() = traits_type::make_default(); |
599 | } |
600 | |
601 | template< typename R > |
602 | void assign_resource(R&& res) noexcept(std::is_nothrow_assignable< internal_resource_type&, R >::value) |
603 | { |
604 | get_internal_resource() = static_cast< R&& >(res); |
605 | } |
606 | |
607 | template< |
608 | bool Requires = detail::conjunction< |
609 | detail::is_swappable< internal_resource_type >, |
610 | detail::is_swappable< internal_deleter_type >, |
611 | detail::disjunction< |
612 | detail::is_nothrow_swappable< internal_resource_type >, |
613 | detail::is_nothrow_swappable< internal_deleter_type > |
614 | > |
615 | >::value |
616 | > |
617 | typename std::enable_if< Requires >::type swap(unique_resource_data& that) |
618 | noexcept(detail::conjunction< detail::is_nothrow_swappable< internal_resource_type >, detail::is_nothrow_swappable< internal_deleter_type > >::value) |
619 | { |
620 | swap_impl |
621 | ( |
622 | that, |
623 | std::integral_constant< bool, detail::is_nothrow_swappable< internal_resource_type >::value >(), |
624 | std::integral_constant< bool, detail::conjunction< |
625 | detail::is_nothrow_swappable< internal_resource_type >, |
626 | detail::is_nothrow_swappable< internal_deleter_type > |
627 | >::value >() |
628 | ); |
629 | } |
630 | |
631 | private: |
632 | unique_resource_data(unique_resource_data&& that, std::true_type, std::true_type) noexcept : |
633 | resource_holder(static_cast< typename detail::move_or_copy_construct_ref< resource_type >::type >(that.get_resource())), |
634 | deleter_holder(static_cast< typename detail::move_or_copy_construct_ref< deleter_type >::type >(that.get_deleter())) |
635 | { |
636 | that.set_unallocated(); |
637 | } |
638 | |
639 | unique_resource_data(unique_resource_data&& that, std::false_type, std::true_type) : |
640 | resource_holder(static_cast< resource_type const& >(that.get_resource())), |
641 | deleter_holder(static_cast< typename detail::move_or_copy_construct_ref< deleter_type >::type >(that.get_deleter())) |
642 | { |
643 | that.set_unallocated(); |
644 | } |
645 | |
646 | unique_resource_data(unique_resource_data&& that, std::true_type, std::false_type) try : |
647 | resource_holder(static_cast< typename detail::move_or_copy_construct_ref< resource_type >::type >(that.get_resource())), |
648 | deleter_holder(static_cast< deleter_type const& >(that.get_deleter())) |
649 | { |
650 | that.set_unallocated(); |
651 | } |
652 | catch (...) |
653 | { |
654 | // Since only the deleter's constructor could have thrown an exception here, move the resource back |
655 | // to the original unique_resource. This is guaranteed to not throw. |
656 | that.resource_holder::move_from(static_cast< internal_resource_type&& >(resource_holder::get_internal())); |
657 | } |
658 | |
659 | unique_resource_data(unique_resource_data&& that, std::false_type, std::false_type) : |
660 | resource_holder(static_cast< resource_type const& >(that.get_resource())), |
661 | deleter_holder(static_cast< deleter_type const& >(that.get_deleter())) |
662 | { |
663 | that.set_unallocated(); |
664 | } |
665 | |
666 | template< |
667 | typename R, |
668 | typename D, |
669 | typename = typename std::enable_if< detail::conjunction< |
670 | std::is_constructible< resource_holder, R, D, bool >, |
671 | std::is_constructible< deleter_holder, D, resource_type&, bool > |
672 | >::value >::type |
673 | > |
674 | explicit unique_resource_data(R&& res, D&& del, bool allocated) |
675 | noexcept(detail::conjunction< |
676 | std::is_nothrow_constructible< resource_holder, R, D, bool >, |
677 | std::is_nothrow_constructible< deleter_holder, D, resource_type&, bool > |
678 | >::value) : |
679 | resource_holder(static_cast< R&& >(res), static_cast< D&& >(del), allocated), |
680 | deleter_holder(static_cast< D&& >(del), resource_holder::get(), allocated) |
681 | { |
682 | } |
683 | |
684 | void assign(unique_resource_data&& that, std::true_type) |
685 | noexcept(std::is_nothrow_assignable< internal_resource_type&, typename detail::move_or_copy_assign_ref< resource_type >::type >::value) |
686 | { |
687 | get_internal_resource() = static_cast< typename detail::move_or_copy_assign_ref< resource_type >::type >(that.get_resource()); |
688 | get_internal_deleter() = static_cast< typename detail::move_or_copy_assign_ref< deleter_type >::type >(that.get_deleter()); |
689 | |
690 | that.set_unallocated(); |
691 | } |
692 | |
693 | void assign(unique_resource_data&& that, std::false_type) |
694 | { |
695 | get_internal_deleter() = static_cast< typename detail::move_or_copy_assign_ref< deleter_type >::type >(that.get_deleter()); |
696 | get_internal_resource() = static_cast< typename detail::move_or_copy_assign_ref< resource_type >::type >(that.get_resource()); |
697 | |
698 | that.set_unallocated(); |
699 | } |
700 | |
701 | void swap_impl(unique_resource_data& that, std::true_type, std::true_type) noexcept |
702 | { |
703 | boost::core::invoke_swap(get_internal_resource(), that.get_internal_resource()); |
704 | boost::core::invoke_swap(get_internal_deleter(), that.get_internal_deleter()); |
705 | } |
706 | |
707 | void swap_impl(unique_resource_data& that, std::true_type, std::false_type) |
708 | { |
709 | boost::core::invoke_swap(get_internal_deleter(), that.get_internal_deleter()); |
710 | boost::core::invoke_swap(get_internal_resource(), that.get_internal_resource()); |
711 | } |
712 | |
713 | void swap_impl(unique_resource_data& that, std::false_type, std::false_type) |
714 | { |
715 | boost::core::invoke_swap(get_internal_resource(), that.get_internal_resource()); |
716 | boost::core::invoke_swap(get_internal_deleter(), that.get_internal_deleter()); |
717 | } |
718 | }; |
719 | |
720 | template< typename Resource, typename Deleter > |
721 | class unique_resource_data< Resource, Deleter, void > : |
722 | public detail::resource_holder< Resource, use_resource_compact_storage< Resource, Deleter >::value >, |
723 | public detail::deleter_holder< Resource, Deleter > |
724 | { |
725 | public: |
726 | using resource_type = Resource; |
727 | using deleter_type = Deleter; |
728 | using traits_type = void; |
729 | |
730 | private: |
731 | using resource_holder = detail::resource_holder< resource_type, use_resource_compact_storage< resource_type, deleter_type >::value >; |
732 | using deleter_holder = detail::deleter_holder< resource_type, deleter_type >; |
733 | |
734 | public: |
735 | using internal_resource_type = typename resource_holder::internal_resource_type; |
736 | using internal_deleter_type = typename deleter_holder::internal_deleter_type; |
737 | |
738 | private: |
739 | bool m_allocated; |
740 | |
741 | public: |
742 | template< |
743 | bool Requires = detail::conjunction< std::is_default_constructible< resource_holder >, std::is_default_constructible< deleter_holder > >::value, |
744 | typename = typename std::enable_if< Requires >::type |
745 | > |
746 | constexpr unique_resource_data() |
747 | noexcept(detail::conjunction< std::is_nothrow_default_constructible< resource_holder >, std::is_nothrow_default_constructible< deleter_holder > >::value) : |
748 | resource_holder(), |
749 | deleter_holder(), |
750 | m_allocated(false) |
751 | { |
752 | } |
753 | |
754 | unique_resource_data(unique_resource_data const&) = delete; |
755 | unique_resource_data& operator= (unique_resource_data const&) = delete; |
756 | |
757 | template< |
758 | bool Requires = detail::conjunction< |
759 | std::is_constructible< internal_resource_type, typename detail::move_or_copy_construct_ref< resource_type >::type >, |
760 | std::is_constructible< internal_deleter_type, typename detail::move_or_copy_construct_ref< deleter_type >::type > |
761 | >::value, |
762 | typename = typename std::enable_if< Requires >::type |
763 | > |
764 | unique_resource_data(unique_resource_data&& that) |
765 | noexcept(detail::conjunction< |
766 | std::is_nothrow_constructible< internal_resource_type, typename detail::move_or_copy_construct_ref< resource_type >::type >, |
767 | std::is_nothrow_constructible< internal_deleter_type, typename detail::move_or_copy_construct_ref< deleter_type >::type > |
768 | >::value) : |
769 | unique_resource_data |
770 | ( |
771 | static_cast< unique_resource_data&& >(that), |
772 | typename std::is_nothrow_constructible< internal_resource_type, typename detail::move_or_copy_construct_ref< resource_type >::type >::type(), |
773 | typename std::is_nothrow_constructible< internal_deleter_type, typename detail::move_or_copy_construct_ref< deleter_type >::type >::type() |
774 | ) |
775 | { |
776 | } |
777 | |
778 | template< |
779 | typename D, |
780 | typename = typename std::enable_if< detail::conjunction< |
781 | std::is_default_constructible< resource_holder >, |
782 | std::is_constructible< deleter_holder, D > |
783 | >::value >::type |
784 | > |
785 | explicit unique_resource_data(default_resource_t, D&& del) |
786 | noexcept(detail::conjunction< |
787 | std::is_nothrow_default_constructible< resource_holder >, |
788 | std::is_nothrow_constructible< deleter_holder, D > |
789 | >::value) : |
790 | resource_holder(), |
791 | deleter_holder(static_cast< D&& >(del)), |
792 | m_allocated(false) |
793 | { |
794 | } |
795 | |
796 | template< |
797 | typename R, |
798 | typename D, |
799 | typename = typename std::enable_if< detail::conjunction< |
800 | detail::negation< detail::is_default_resource< R > >, |
801 | std::is_constructible< resource_holder, R, D, bool >, |
802 | std::is_constructible< deleter_holder, D, resource_type&, bool > |
803 | >::value >::type |
804 | > |
805 | explicit unique_resource_data(R&& res, D&& del) |
806 | noexcept(detail::conjunction< |
807 | std::is_nothrow_constructible< resource_holder, R, D, bool >, |
808 | std::is_nothrow_constructible< deleter_holder, D, resource_type&, bool > |
809 | >::value) : |
810 | resource_holder(static_cast< R&& >(res), static_cast< D&& >(del), true), |
811 | deleter_holder(static_cast< D&& >(del), resource_holder::get(), true), |
812 | m_allocated(true) |
813 | { |
814 | } |
815 | |
816 | template< |
817 | bool Requires = detail::conjunction< |
818 | std::is_assignable< internal_resource_type&, typename detail::move_or_copy_assign_ref< resource_type >::type >, |
819 | std::is_assignable< internal_deleter_type&, typename detail::move_or_copy_assign_ref< deleter_type >::type > |
820 | >::value |
821 | > |
822 | typename std::enable_if< Requires, unique_resource_data& >::type operator= (unique_resource_data&& that) |
823 | noexcept(detail::conjunction< |
824 | std::is_nothrow_assignable< internal_resource_type&, typename detail::move_or_copy_assign_ref< resource_type >::type >, |
825 | std::is_nothrow_assignable< internal_deleter_type&, typename detail::move_or_copy_assign_ref< deleter_type >::type > |
826 | >::value) |
827 | { |
828 | assign(static_cast< unique_resource_data&& >(that), typename std::is_nothrow_move_assignable< internal_deleter_type >::type()); |
829 | return *this; |
830 | } |
831 | |
832 | resource_type& get_resource() noexcept |
833 | { |
834 | return resource_holder::get(); |
835 | } |
836 | |
837 | resource_type const& get_resource() const noexcept |
838 | { |
839 | return resource_holder::get(); |
840 | } |
841 | |
842 | internal_resource_type& get_internal_resource() noexcept |
843 | { |
844 | return resource_holder::get_internal(); |
845 | } |
846 | |
847 | internal_resource_type const& get_internal_resource() const noexcept |
848 | { |
849 | return resource_holder::get_internal(); |
850 | } |
851 | |
852 | deleter_type& get_deleter() noexcept |
853 | { |
854 | return deleter_holder::get(); |
855 | } |
856 | |
857 | deleter_type const& get_deleter() const noexcept |
858 | { |
859 | return deleter_holder::get(); |
860 | } |
861 | |
862 | internal_deleter_type& get_internal_deleter() noexcept |
863 | { |
864 | return deleter_holder::get_internal(); |
865 | } |
866 | |
867 | internal_deleter_type const& get_internal_deleter() const noexcept |
868 | { |
869 | return deleter_holder::get_internal(); |
870 | } |
871 | |
872 | bool is_allocated() const noexcept |
873 | { |
874 | return m_allocated; |
875 | } |
876 | |
877 | void set_unallocated() noexcept |
878 | { |
879 | m_allocated = false; |
880 | } |
881 | |
882 | template< typename R > |
883 | void assign_resource(R&& res) noexcept(std::is_nothrow_assignable< internal_resource_type&, R >::value) |
884 | { |
885 | get_internal_resource() = static_cast< R&& >(res); |
886 | m_allocated = true; |
887 | } |
888 | |
889 | template< |
890 | bool Requires = detail::conjunction< |
891 | detail::is_swappable< internal_resource_type >, |
892 | detail::is_swappable< internal_deleter_type >, |
893 | detail::disjunction< |
894 | detail::is_nothrow_swappable< internal_resource_type >, |
895 | detail::is_nothrow_swappable< internal_deleter_type > |
896 | > |
897 | >::value |
898 | > |
899 | typename std::enable_if< Requires >::type swap(unique_resource_data& that) |
900 | noexcept(detail::conjunction< detail::is_nothrow_swappable< internal_resource_type >, detail::is_nothrow_swappable< internal_deleter_type > >::value) |
901 | { |
902 | swap_impl |
903 | ( |
904 | that, |
905 | std::integral_constant< bool, detail::is_nothrow_swappable< internal_resource_type >::value >(), |
906 | std::integral_constant< bool, detail::conjunction< |
907 | detail::is_nothrow_swappable< internal_resource_type >, |
908 | detail::is_nothrow_swappable< internal_deleter_type > |
909 | >::value >() |
910 | ); |
911 | } |
912 | |
913 | private: |
914 | unique_resource_data(unique_resource_data&& that, std::true_type, std::true_type) noexcept : |
915 | resource_holder(static_cast< typename detail::move_or_copy_construct_ref< resource_type >::type >(that.get_resource())), |
916 | deleter_holder(static_cast< typename detail::move_or_copy_construct_ref< deleter_type >::type >(that.get_deleter())), |
917 | m_allocated(that.m_allocated) |
918 | { |
919 | that.m_allocated = false; |
920 | } |
921 | |
922 | unique_resource_data(unique_resource_data&& that, std::false_type, std::true_type) : |
923 | resource_holder(static_cast< resource_type const& >(that.get_resource())), |
924 | deleter_holder(static_cast< typename detail::move_or_copy_construct_ref< deleter_type >::type >(that.get_deleter())), |
925 | m_allocated(that.m_allocated) |
926 | { |
927 | that.m_allocated = false; |
928 | } |
929 | |
930 | unique_resource_data(unique_resource_data&& that, std::true_type, std::false_type) try : |
931 | resource_holder(static_cast< typename detail::move_or_copy_construct_ref< resource_type >::type >(that.get_resource())), |
932 | deleter_holder(static_cast< deleter_type const& >(that.get_deleter())), |
933 | m_allocated(that.m_allocated) |
934 | { |
935 | that.m_allocated = false; |
936 | } |
937 | catch (...) |
938 | { |
939 | // Since only the deleter's constructor could have thrown an exception here, move the resource back |
940 | // to the original unique_resource. This is guaranteed to not throw. |
941 | that.resource_holder::move_from(static_cast< internal_resource_type&& >(resource_holder::get_internal())); |
942 | } |
943 | |
944 | unique_resource_data(unique_resource_data&& that, std::false_type, std::false_type) : |
945 | resource_holder(static_cast< resource_type const& >(that.get_resource())), |
946 | deleter_holder(static_cast< deleter_type const& >(that.get_deleter())), |
947 | m_allocated(that.m_allocated) |
948 | { |
949 | that.m_allocated = false; |
950 | } |
951 | |
952 | void assign(unique_resource_data&& that, std::true_type) |
953 | noexcept(std::is_nothrow_assignable< internal_resource_type&, typename detail::move_or_copy_assign_ref< resource_type >::type >::value) |
954 | { |
955 | get_internal_resource() = static_cast< typename detail::move_or_copy_assign_ref< resource_type >::type >(that.get_resource()); |
956 | get_internal_deleter() = static_cast< typename detail::move_or_copy_assign_ref< deleter_type >::type >(that.get_deleter()); |
957 | |
958 | m_allocated = that.m_allocated; |
959 | that.m_allocated = false; |
960 | } |
961 | |
962 | void assign(unique_resource_data&& that, std::false_type) |
963 | { |
964 | get_internal_deleter() = static_cast< typename detail::move_or_copy_assign_ref< deleter_type >::type >(that.get_deleter()); |
965 | get_internal_resource() = static_cast< typename detail::move_or_copy_assign_ref< resource_type >::type >(that.get_resource()); |
966 | |
967 | m_allocated = that.m_allocated; |
968 | that.m_allocated = false; |
969 | } |
970 | |
971 | void swap_impl(unique_resource_data& that, std::true_type, std::true_type) noexcept |
972 | { |
973 | boost::core::invoke_swap(get_internal_resource(), that.get_internal_resource()); |
974 | boost::core::invoke_swap(get_internal_deleter(), that.get_internal_deleter()); |
975 | boost::core::invoke_swap(m_allocated, that.m_allocated); |
976 | } |
977 | |
978 | void swap_impl(unique_resource_data& that, std::true_type, std::false_type) |
979 | { |
980 | boost::core::invoke_swap(get_internal_deleter(), that.get_internal_deleter()); |
981 | boost::core::invoke_swap(get_internal_resource(), that.get_internal_resource()); |
982 | boost::core::invoke_swap(m_allocated, that.m_allocated); |
983 | } |
984 | |
985 | void swap_impl(unique_resource_data& that, std::false_type, std::false_type) |
986 | { |
987 | boost::core::invoke_swap(get_internal_resource(), that.get_internal_resource()); |
988 | boost::core::invoke_swap(get_internal_deleter(), that.get_internal_deleter()); |
989 | boost::core::invoke_swap(m_allocated, that.m_allocated); |
990 | } |
991 | }; |
992 | |
993 | template< typename T > |
994 | struct is_dereferenceable_impl |
995 | { |
996 | template< typename U, typename R = decltype(*std::declval< U const& >()) > |
997 | static std::true_type _is_dereferenceable_check(int); |
998 | template< typename U > |
999 | static std::false_type _is_dereferenceable_check(...); |
1000 | |
1001 | using type = decltype(is_dereferenceable_impl::_is_dereferenceable_check< T >(0)); |
1002 | }; |
1003 | |
1004 | template< typename T > |
1005 | struct is_dereferenceable : public is_dereferenceable_impl< T >::type { }; |
1006 | template< > |
1007 | struct is_dereferenceable< void* > : public std::false_type { }; |
1008 | template< > |
1009 | struct is_dereferenceable< const void* > : public std::false_type { }; |
1010 | template< > |
1011 | struct is_dereferenceable< volatile void* > : public std::false_type { }; |
1012 | template< > |
1013 | struct is_dereferenceable< const volatile void* > : public std::false_type { }; |
1014 | template< > |
1015 | struct is_dereferenceable< void*& > : public std::false_type { }; |
1016 | template< > |
1017 | struct is_dereferenceable< const void*& > : public std::false_type { }; |
1018 | template< > |
1019 | struct is_dereferenceable< volatile void*& > : public std::false_type { }; |
1020 | template< > |
1021 | struct is_dereferenceable< const volatile void*& > : public std::false_type { }; |
1022 | template< > |
1023 | struct is_dereferenceable< void* const& > : public std::false_type { }; |
1024 | template< > |
1025 | struct is_dereferenceable< const void* const& > : public std::false_type { }; |
1026 | template< > |
1027 | struct is_dereferenceable< volatile void* const& > : public std::false_type { }; |
1028 | template< > |
1029 | struct is_dereferenceable< const volatile void* const& > : public std::false_type { }; |
1030 | template< > |
1031 | struct is_dereferenceable< void* volatile& > : public std::false_type { }; |
1032 | template< > |
1033 | struct is_dereferenceable< const void* volatile& > : public std::false_type { }; |
1034 | template< > |
1035 | struct is_dereferenceable< volatile void* volatile& > : public std::false_type { }; |
1036 | template< > |
1037 | struct is_dereferenceable< const volatile void* volatile& > : public std::false_type { }; |
1038 | template< > |
1039 | struct is_dereferenceable< void* const volatile& > : public std::false_type { }; |
1040 | template< > |
1041 | struct is_dereferenceable< const void* const volatile& > : public std::false_type { }; |
1042 | template< > |
1043 | struct is_dereferenceable< volatile void* const volatile& > : public std::false_type { }; |
1044 | template< > |
1045 | struct is_dereferenceable< const volatile void* const volatile& > : public std::false_type { }; |
1046 | |
1047 | template< typename T, bool = detail::is_dereferenceable< T >::value > |
1048 | struct dereference_traits { }; |
1049 | template< typename T > |
1050 | struct dereference_traits< T, true > |
1051 | { |
1052 | using result_type = decltype(*std::declval< T const& >()); |
1053 | static constexpr bool is_noexcept = noexcept(*std::declval< T const& >()); |
1054 | }; |
1055 | |
1056 | } // namespace detail |
1057 | |
1058 | /*! |
1059 | * \brief RAII wrapper for automatically reclaiming arbitrary resources. |
1060 | * |
1061 | * A \c unique_resource object exclusively owns wrapped resource and invokes |
1062 | * the deleter function object on it on destruction. The wrapped resource can have |
1063 | * any type that is: |
1064 | * |
1065 | * \li Move-constructible, where the move constructor is marked as `noexcept`, or |
1066 | * \li Copy-constructible, or |
1067 | * \li An lvalue reference to an object type. |
1068 | * |
1069 | * The deleter must be a function object type that is callable on an lvalue |
1070 | * of the resource type. The deleter must be copy-constructible. |
1071 | * |
1072 | * An optional resource traits template parameter may be specified. Resource |
1073 | * traits can be used to optimize \c unique_resource implementation when |
1074 | * the following conditions are met: |
1075 | * |
1076 | * \li There is at least one value of the resource type that is considered |
1077 | * unallocated (that is, no allocated resource shall be equal to one of |
1078 | * the unallocated resource values). The unallocated resource values need not |
1079 | * be deallocated using the deleter. |
1080 | * \li One of the unallocated resource values can be considered the default. |
1081 | * Constructing the default resource value and assigning it to a resource |
1082 | * object (whether allocated or not) shall not throw exceptions. |
1083 | * \li Resource objects can be tested for being unallocated. Such a test shall |
1084 | * not throw exceptions. |
1085 | * |
1086 | * If specified, the resource traits must be a class type that has the following |
1087 | * public static members: |
1088 | * |
1089 | * \li `R make_default() noexcept` - must return the default resource value such |
1090 | * that `std::is_constructible< Resource, R >::value && |
1091 | * std::is_nothrow_assignable< Resource&, R >::value` is \c true. |
1092 | * \li `bool is_allocated(Resource const& res) noexcept` - must return \c true |
1093 | * if \c res is not one of the unallocated resource values and \c false |
1094 | * otherwise. |
1095 | * |
1096 | * Note that `is_allocated(make_default())` must always return \c false. |
1097 | * |
1098 | * When resource traits satisfying the above requirements are specified, |
1099 | * \c unique_resource will be able to avoid storing additional indication of |
1100 | * whether the owned resource object needs to be deallocated with the deleter |
1101 | * on destruction. It will use the default resource value to initialize the owned |
1102 | * resource object when \c unique_resource is not in the allocated state. |
1103 | * Additionally, it will be possible to construct \c unique_resource with |
1104 | * unallocated resource values, which will create \c unique_resource objects in |
1105 | * unallocated state (the deleter will not be called on unallocated resource |
1106 | * values). |
1107 | * |
1108 | * \tparam Resource Resource type. |
1109 | * \tparam Deleter Resource deleter function object type. |
1110 | * \tparam Traits Optional resource traits type. |
1111 | */ |
1112 | template< typename Resource, typename Deleter, typename Traits BOOST_SCOPE_DETAIL_DOC(= void) > |
1113 | class unique_resource |
1114 | { |
1115 | public: |
1116 | //! Resource type |
1117 | using resource_type = Resource; |
1118 | //! Deleter type |
1119 | using deleter_type = Deleter; |
1120 | //! Resource traits |
1121 | using traits_type = Traits; |
1122 | |
1123 | //! \cond |
1124 | private: |
1125 | using data = detail::unique_resource_data< resource_type, deleter_type, traits_type >; |
1126 | using internal_resource_type = typename data::internal_resource_type; |
1127 | using internal_deleter_type = typename data::internal_deleter_type; |
1128 | |
1129 | data m_data; |
1130 | |
1131 | //! \endcond |
1132 | public: |
1133 | /*! |
1134 | * \brief Constructs an unallocated unique resource guard. |
1135 | * |
1136 | * **Requires:** Default \c Resource value can be constructed. \c Deleter is default-constructible |
1137 | * and is not a pointer to function. |
1138 | * |
1139 | * **Effects:** Initializes the \c Resource object with the default resource value. Default-constructs |
1140 | * the \c Deleter object. |
1141 | * |
1142 | * **Throws:** Nothing, unless construction of \c Resource or \c Deleter throws. |
1143 | * |
1144 | * \post `this->allocated() == false` |
1145 | */ |
1146 | //! \cond |
1147 | template< |
1148 | bool Requires = std::is_default_constructible< data >::value, |
1149 | typename = typename std::enable_if< Requires >::type |
1150 | > |
1151 | //! \endcond |
1152 | constexpr unique_resource() noexcept(BOOST_SCOPE_DETAIL_DOC_HIDDEN(std::is_nothrow_default_constructible< data >::value)) |
1153 | { |
1154 | } |
1155 | |
1156 | /*! |
1157 | * \brief Constructs an unallocated unique resource guard with the given deleter. |
1158 | * |
1159 | * **Requires:** Default \c Resource value can be constructed and \c Deleter is constructible from \a del. |
1160 | * |
1161 | * **Effects:** Initializes the \c Resource value with the default resource value. If \c Deleter is nothrow |
1162 | * constructible from `D&&` then constructs \c Deleter from `std::forward< D >(del)`, |
1163 | * otherwise constructs from `del`. |
1164 | * |
1165 | * **Throws:** Nothing, unless construction of \c Resource or \c Deleter throws. |
1166 | * |
1167 | * \param res A tag argument indicating default resource value. |
1168 | * \param del Resource deleter function object. |
1169 | * |
1170 | * \post `this->allocated() == false` |
1171 | */ |
1172 | template< |
1173 | typename D |
1174 | //! \cond |
1175 | , typename = typename std::enable_if< |
1176 | std::is_constructible< data, default_resource_t, typename detail::move_or_copy_construct_ref< D, deleter_type >::type >::value |
1177 | >::type |
1178 | //! \endcond |
1179 | > |
1180 | unique_resource(default_resource_t res, D&& del) |
1181 | noexcept(BOOST_SCOPE_DETAIL_DOC_HIDDEN( |
1182 | std::is_nothrow_constructible< |
1183 | data, |
1184 | default_resource_t, |
1185 | typename detail::move_or_copy_construct_ref< D, deleter_type >::type |
1186 | >::value |
1187 | )) : |
1188 | m_data |
1189 | ( |
1190 | res, |
1191 | static_cast< typename detail::move_or_copy_construct_ref< D, deleter_type >::type >(del) |
1192 | ) |
1193 | { |
1194 | } |
1195 | |
1196 | /*! |
1197 | * \brief Constructs a unique resource guard with the given resource and a default-constructed deleter. |
1198 | * |
1199 | * **Requires:** \c Resource is constructible from \a res. \c Deleter is default-constructible and |
1200 | * is not a pointer to function. |
1201 | * |
1202 | * **Effects:** Constructs the unique resource object as if by calling |
1203 | * `unique_resource(std::forward< R >(res), Deleter())`. |
1204 | * |
1205 | * **Throws:** Nothing, unless construction of \c Resource or \c Deleter throws. |
1206 | * |
1207 | * \param res Resource object. |
1208 | */ |
1209 | template< |
1210 | typename R |
1211 | //! \cond |
1212 | , typename = typename std::enable_if< detail::conjunction< |
1213 | detail::is_nothrow_nonnull_default_constructible< deleter_type >, |
1214 | std::is_constructible< data, typename detail::move_or_copy_construct_ref< R, resource_type >::type, typename detail::move_or_copy_construct_ref< deleter_type >::type >, |
1215 | detail::disjunction< detail::negation< std::is_reference< resource_type > >, std::is_reference< R > > // prevent binding lvalue-reference resource to an rvalue |
1216 | >::value >::type |
1217 | //! \endcond |
1218 | > |
1219 | explicit unique_resource(R&& res) |
1220 | noexcept(BOOST_SCOPE_DETAIL_DOC_HIDDEN( |
1221 | std::is_nothrow_constructible< |
1222 | data, |
1223 | typename detail::move_or_copy_construct_ref< R, resource_type >::type, |
1224 | typename detail::move_or_copy_construct_ref< deleter_type >::type |
1225 | >::value |
1226 | )) : |
1227 | m_data |
1228 | ( |
1229 | static_cast< typename detail::move_or_copy_construct_ref< R, resource_type >::type >(res), |
1230 | static_cast< typename detail::move_or_copy_construct_ref< deleter_type >::type >(deleter_type()) |
1231 | ) |
1232 | { |
1233 | } |
1234 | |
1235 | /*! |
1236 | * \brief Constructs a unique resource guard with the given resource and deleter. |
1237 | * |
1238 | * **Requires:** \c Resource is constructible from \a res and \c Deleter is constructible from \a del. |
1239 | * |
1240 | * **Effects:** If \c Resource is nothrow constructible from `R&&` then constructs \c Resource |
1241 | * from `std::forward< R >(res)`, otherwise constructs from `res`. If \c Deleter |
1242 | * is nothrow constructible from `D&&` then constructs \c Deleter from |
1243 | * `std::forward< D >(del)`, otherwise constructs from `del`. |
1244 | * |
1245 | * If construction of \c Resource or \c Deleter throws and \a res is not an unallocated resource |
1246 | * value, invokes \a del on \a res (if \c Resource construction failed) or the constructed |
1247 | * \c Resource object (if \c Deleter construction failed). |
1248 | * |
1249 | * **Throws:** Nothing, unless construction of \c Resource or \c Deleter throws. |
1250 | * |
1251 | * \param res Resource object. |
1252 | * \param del Resource deleter function object. |
1253 | * |
1254 | * \post If \a res is an unallocated resource value then `this->allocated() == false`, otherwise |
1255 | * `this->allocated() == true`. |
1256 | */ |
1257 | template< |
1258 | typename R, |
1259 | typename D |
1260 | //! \cond |
1261 | , typename = typename std::enable_if< detail::conjunction< |
1262 | std::is_constructible< data, typename detail::move_or_copy_construct_ref< R, resource_type >::type, typename detail::move_or_copy_construct_ref< D, deleter_type >::type >, |
1263 | detail::disjunction< detail::negation< std::is_reference< resource_type > >, std::is_reference< R > > // prevent binding lvalue-reference resource to an rvalue |
1264 | >::value >::type |
1265 | //! \endcond |
1266 | > |
1267 | unique_resource(R&& res, D&& del) |
1268 | noexcept(BOOST_SCOPE_DETAIL_DOC_HIDDEN( |
1269 | std::is_nothrow_constructible< |
1270 | data, |
1271 | typename detail::move_or_copy_construct_ref< R, resource_type >::type, |
1272 | typename detail::move_or_copy_construct_ref< D, deleter_type >::type |
1273 | >::value |
1274 | )) : |
1275 | m_data |
1276 | ( |
1277 | static_cast< typename detail::move_or_copy_construct_ref< R, resource_type >::type >(res), |
1278 | static_cast< typename detail::move_or_copy_construct_ref< D, deleter_type >::type >(del) |
1279 | ) |
1280 | { |
1281 | } |
1282 | |
1283 | unique_resource(unique_resource const&) = delete; |
1284 | unique_resource& operator= (unique_resource const&) = delete; |
1285 | |
1286 | /*! |
1287 | * \brief Move-constructs a unique resource guard. |
1288 | * |
1289 | * **Requires:** \c Resource and \c Deleter are move-constructible. |
1290 | * |
1291 | * **Effects:** If \c Resource is nothrow move-constructible then move-constructs \c Resource, |
1292 | * otherwise copy-constructs. If \c Deleter is nothrow move-constructible then move-constructs |
1293 | * \c Deleter, otherwise copy-constructs. Deactivates the moved-from unique resource object. |
1294 | * |
1295 | * If an exception is thrown during construction, \a that is left in its original state. |
1296 | * |
1297 | * \note This logic ensures that in case of exception the resource is not leaked and remains owned by the |
1298 | * move source. |
1299 | * |
1300 | * **Throws:** Nothing, unless construction of \c Resource or \c Deleter throws. |
1301 | * |
1302 | * \param that Move source. |
1303 | * |
1304 | * \post Let \c allocated be equal to `that.allocated()` prior to the operation. Then |
1305 | * `this->allocated() == allocated` and `that.allocated() == false`. |
1306 | */ |
1307 | //! \cond |
1308 | template< |
1309 | bool Requires = std::is_move_constructible< data >::value, |
1310 | typename = typename std::enable_if< Requires >::type |
1311 | > |
1312 | //! \endcond |
1313 | unique_resource(unique_resource&& that) noexcept(BOOST_SCOPE_DETAIL_DOC_HIDDEN(std::is_nothrow_move_constructible< data >::value)) : |
1314 | m_data(static_cast< data&& >(that.m_data)) |
1315 | { |
1316 | } |
1317 | |
1318 | /*! |
1319 | * \brief Move-assigns a unique resource guard. |
1320 | * |
1321 | * **Requires:** \c Resource and \c Deleter are move-assignable. |
1322 | * |
1323 | * **Effects:** Calls `this->reset()`. Then, if \c Deleter is nothrow move-assignable, move-assigns |
1324 | * the \c Deleter object first and the \c Resource object next. Otherwise, move-assigns |
1325 | * the objects in reverse order. Lastly, deactivates the moved-from unique resource object. |
1326 | * |
1327 | * If an exception is thrown, \a that is left in its original state. |
1328 | * |
1329 | * \note The different orders of assignment ensure that in case of exception the resource is not leaked |
1330 | * and remains owned by the move source. |
1331 | * |
1332 | * **Throws:** Nothing, unless assignment of \c Resource or \c Deleter throws. |
1333 | * |
1334 | * \param that Move source. |
1335 | * |
1336 | * \post Let \c allocated be equal to `that.allocated()` prior to the operation. Then |
1337 | * `this->allocated() == allocated` and `that.allocated() == false`. |
1338 | */ |
1339 | #if !defined(BOOST_SCOPE_DOXYGEN) |
1340 | template< bool Requires = std::is_move_assignable< data >::value > |
1341 | typename std::enable_if< Requires, unique_resource& >::type |
1342 | #else |
1343 | unique_resource& |
1344 | #endif |
1345 | operator= (unique_resource&& that) |
1346 | noexcept(BOOST_SCOPE_DETAIL_DOC_HIDDEN(std::is_nothrow_move_assignable< data >::value)) |
1347 | { |
1348 | reset(); |
1349 | m_data = static_cast< data&& >(that.m_data); |
1350 | return *this; |
1351 | } |
1352 | |
1353 | /*! |
1354 | * \brief If the resource is allocated, calls the deleter function on it. Destroys the resource and the deleter. |
1355 | * |
1356 | * **Throws:** Nothing, unless invoking the deleter throws. |
1357 | */ |
1358 | ~unique_resource() noexcept(BOOST_SCOPE_DETAIL_DOC_HIDDEN(detail::is_nothrow_invocable< deleter_type&, resource_type& >::value)) |
1359 | { |
1360 | if (BOOST_LIKELY(m_data.is_allocated())) |
1361 | m_data.get_deleter()(m_data.get_resource()); |
1362 | } |
1363 | |
1364 | /*! |
1365 | * \brief Returns \c true if the resource is allocated and to be reclaimed by the deleter, otherwise \c false. |
1366 | * |
1367 | * \note This method does not test the value of the resource. |
1368 | * |
1369 | * **Throws:** Nothing. |
1370 | */ |
1371 | explicit operator bool () const noexcept |
1372 | { |
1373 | return m_data.is_allocated(); |
1374 | } |
1375 | |
1376 | /*! |
1377 | * \brief Returns \c true if the resource is allocated and to be reclaimed by the deleter, otherwise \c false. |
1378 | * |
1379 | * **Throws:** Nothing. |
1380 | */ |
1381 | bool allocated() const noexcept |
1382 | { |
1383 | return m_data.is_allocated(); |
1384 | } |
1385 | |
1386 | /*! |
1387 | * \brief Returns a reference to the resource object. |
1388 | * |
1389 | * **Throws:** Nothing. |
1390 | */ |
1391 | resource_type const& get() const noexcept |
1392 | { |
1393 | return m_data.get_resource(); |
1394 | } |
1395 | |
1396 | /*! |
1397 | * \brief Returns a reference to the deleter object. |
1398 | * |
1399 | * **Throws:** Nothing. |
1400 | */ |
1401 | deleter_type const& get_deleter() const noexcept |
1402 | { |
1403 | return m_data.get_deleter(); |
1404 | } |
1405 | |
1406 | /*! |
1407 | * \brief Marks the resource as unallocated. Does not call the deleter if the resource was previously allocated. |
1408 | * |
1409 | * **Throws:** Nothing. |
1410 | * |
1411 | * \post `this->allocated() == false` |
1412 | */ |
1413 | void release() noexcept |
1414 | { |
1415 | m_data.set_unallocated(); |
1416 | } |
1417 | |
1418 | /*! |
1419 | * \brief If the resource is allocated, calls the deleter function on it and marks the resource as unallocated. |
1420 | * |
1421 | * **Throws:** Nothing, unless invoking the deleter throws. |
1422 | * |
1423 | * \post `this->allocated() == false` |
1424 | */ |
1425 | void reset() noexcept(BOOST_SCOPE_DETAIL_DOC_HIDDEN(detail::is_nothrow_invocable< deleter_type&, resource_type& >::value)) |
1426 | { |
1427 | if (BOOST_LIKELY(m_data.is_allocated())) |
1428 | { |
1429 | m_data.get_deleter()(m_data.get_resource()); |
1430 | m_data.set_unallocated(); |
1431 | } |
1432 | } |
1433 | |
1434 | /*! |
1435 | * \brief Assigns a new resource object to the unique resource wrapper. |
1436 | * |
1437 | * **Effects:** Calls `this->reset()`. Then, if \c Resource is nothrow assignable from `R&&`, |
1438 | * assigns `std::forward< R >(res)` to the stored resource object, otherwise assigns |
1439 | * `res`. |
1440 | * |
1441 | * If \a res is not an unallocated resource value and an exception is thrown during the operation, |
1442 | * invokes the stored deleter on \a res before returning with the exception. |
1443 | * |
1444 | * **Throws:** Nothing, unless invoking the deleter throws. |
1445 | * |
1446 | * \param res Resource object to assign. |
1447 | * |
1448 | * \post `this->allocated() == false` |
1449 | */ |
1450 | template< typename R > |
1451 | #if !defined(BOOST_SCOPE_DOXYGEN) |
1452 | typename std::enable_if< detail::conjunction< |
1453 | std::is_assignable< internal_resource_type&, typename detail::move_or_copy_assign_ref< R, resource_type >::type >, |
1454 | detail::disjunction< detail::negation< std::is_reference< resource_type > >, std::is_reference< R > > // prevent binding lvalue-reference resource to an rvalue |
1455 | >::value >::type |
1456 | #else |
1457 | void |
1458 | #endif |
1459 | reset(R&& res) |
1460 | noexcept(BOOST_SCOPE_DETAIL_DOC_HIDDEN( |
1461 | detail::conjunction< |
1462 | detail::is_nothrow_invocable< deleter_type&, resource_type& >, |
1463 | std::is_nothrow_assignable< internal_resource_type&, typename detail::move_or_copy_assign_ref< R, resource_type >::type > |
1464 | >::value |
1465 | )) |
1466 | { |
1467 | reset_impl |
1468 | ( |
1469 | static_cast< R&& >(res), |
1470 | typename detail::conjunction< |
1471 | detail::is_nothrow_invocable< deleter_type&, resource_type& >, |
1472 | std::is_nothrow_assignable< internal_resource_type&, typename detail::move_or_copy_assign_ref< R, resource_type >::type > |
1473 | >::type() |
1474 | ); |
1475 | } |
1476 | |
1477 | /*! |
1478 | * \brief Invokes indirection on the resource object. |
1479 | * |
1480 | * **Requires:** \c Resource is dereferenceable. |
1481 | * |
1482 | * **Effects:** Returns a reference to the resource object as if by calling `get()`. |
1483 | * |
1484 | * \note If \c Resource is not a pointer type, the compiler will invoke its `operator->`. |
1485 | * Such call sequence will continue until a pointer is obtained. |
1486 | * |
1487 | * **Throws:** Nothing. Note that any implicit subsequent calls to other `operator->` |
1488 | * functions that are caused by this call may have different throw conditions. |
1489 | */ |
1490 | #if !defined(BOOST_SCOPE_DOXYGEN) |
1491 | template< bool Requires = detail::is_dereferenceable< resource_type >::value > |
1492 | typename std::enable_if< Requires, resource_type const& >::type |
1493 | #else |
1494 | resource_type const& |
1495 | #endif |
1496 | operator-> () const noexcept |
1497 | { |
1498 | return get(); |
1499 | } |
1500 | |
1501 | /*! |
1502 | * \brief Dereferences the resource object. |
1503 | * |
1504 | * **Requires:** \c Resource is dereferenceable. |
1505 | * |
1506 | * **Effects:** Returns the result of dereferencing the resource object as if by calling `*get()`. |
1507 | * |
1508 | * **Throws:** Nothing, unless dereferencing the resource object throws. |
1509 | */ |
1510 | #if !defined(BOOST_SCOPE_DOXYGEN) |
1511 | template< bool Requires = detail::is_dereferenceable< resource_type >::value > |
1512 | typename detail::dereference_traits< resource_type, Requires >::result_type |
1513 | #else |
1514 | auto |
1515 | #endif |
1516 | operator* () const |
1517 | noexcept(BOOST_SCOPE_DETAIL_DOC_HIDDEN(detail::dereference_traits< resource_type, Requires >::is_noexcept)) |
1518 | { |
1519 | return *get(); |
1520 | } |
1521 | |
1522 | /*! |
1523 | * \brief Swaps two unique resource wrappers. |
1524 | * |
1525 | * **Requires:** \c Resource and \c Deleter are swappable. At least one of \c Resource and \c Deleter |
1526 | * is nothrow swappable. |
1527 | * |
1528 | * **Effects:** Swaps the resource objects and deleter objects stored in `*this` and \a that |
1529 | * as if by calling unqualified `swap` in a context where `std::swap` is |
1530 | * found by overload resolution. |
1531 | * |
1532 | * If an exception is thrown, and the failed swap operation supports strong exception |
1533 | * guarantee, both `*this` and \a that are left in their original states. |
1534 | * |
1535 | * **Throws:** Nothing, unless swapping the resource objects or deleters throw. |
1536 | * |
1537 | * \param that Unique resource wrapper to swap with. |
1538 | */ |
1539 | #if !defined(BOOST_SCOPE_DOXYGEN) |
1540 | template< bool Requires = detail::is_swappable< data >::value > |
1541 | typename std::enable_if< Requires >::type |
1542 | #else |
1543 | void |
1544 | #endif |
1545 | swap(unique_resource& that) |
1546 | noexcept(BOOST_SCOPE_DETAIL_DOC_HIDDEN(detail::is_nothrow_swappable< data >::value)) |
1547 | { |
1548 | m_data.swap(that.m_data); |
1549 | } |
1550 | |
1551 | /*! |
1552 | * \brief Swaps two unique resource wrappers. |
1553 | * |
1554 | * **Effects:** As if `left.swap(right)`. |
1555 | */ |
1556 | #if !defined(BOOST_SCOPE_DOXYGEN) |
1557 | template< bool Requires = detail::is_swappable< data >::value > |
1558 | friend typename std::enable_if< Requires >::type |
1559 | #else |
1560 | friend void |
1561 | #endif |
1562 | swap(unique_resource& left, unique_resource& right) |
1563 | noexcept(BOOST_SCOPE_DETAIL_DOC_HIDDEN(detail::is_nothrow_swappable< data >::value)) |
1564 | { |
1565 | left.swap(right); |
1566 | } |
1567 | |
1568 | //! \cond |
1569 | private: |
1570 | //! Assigns a new resource object to the unique resource wrapper. |
1571 | template< typename R > |
1572 | void reset_impl(R&& res, std::true_type) noexcept |
1573 | { |
1574 | reset(); |
1575 | m_data.assign_resource(static_cast< typename detail::move_or_copy_assign_ref< R, resource_type >::type >(res)); |
1576 | } |
1577 | |
1578 | //! Assigns a new resource object to the unique resource wrapper. |
1579 | template< typename R > |
1580 | void reset_impl(R&& res, std::false_type) |
1581 | { |
1582 | try |
1583 | { |
1584 | reset(); |
1585 | m_data.assign_resource(static_cast< typename detail::move_or_copy_assign_ref< R, resource_type >::type >(res)); |
1586 | } |
1587 | catch (...) |
1588 | { |
1589 | m_data.get_deleter()(static_cast< R&& >(res)); |
1590 | throw; |
1591 | } |
1592 | } |
1593 | //! \endcond |
1594 | }; |
1595 | |
1596 | #if !defined(BOOST_NO_CXX17_DEDUCTION_GUIDES) |
1597 | template< |
1598 | typename Resource, |
1599 | typename Deleter, |
1600 | typename = typename std::enable_if< !detail::is_default_resource< Resource >::value >::type |
1601 | > |
1602 | unique_resource(Resource, Deleter) -> unique_resource< Resource, Deleter >; |
1603 | #endif // !defined(BOOST_NO_CXX17_DEDUCTION_GUIDES) |
1604 | |
1605 | /*! |
1606 | * \brief Checks if the resource is valid and creates a \c unique_resource wrapper. |
1607 | * |
1608 | * **Effects:** If the resource \a res is not equal to \a invalid, creates a unique resource wrapper |
1609 | * that is in allocated state and owns \a res. Otherwise creates a unique resource wrapper |
1610 | * in unallocated state. |
1611 | * |
1612 | * \note This function does not call \a del if \a res is equal to \a invalid. |
1613 | * |
1614 | * **Throws:** Nothing, unless \c unique_resource constructor throws. |
1615 | * |
1616 | * \param res Resource to wrap. |
1617 | * \param invalid An invalid value for the resource. |
1618 | * \param del A deleter to invoke on the resource to free it. |
1619 | */ |
1620 | template< typename Resource, typename Deleter, typename Invalid > |
1621 | inline unique_resource< typename std::decay< Resource >::type, typename std::decay< Deleter >::type > |
1622 | make_unique_resource_checked(Resource&& res, Invalid const& invalid, Deleter&& del) |
1623 | noexcept(BOOST_SCOPE_DETAIL_DOC_HIDDEN( |
1624 | detail::conjunction< |
1625 | std::is_nothrow_constructible< typename std::decay< Resource >::type, typename detail::move_or_copy_construct_ref< Resource, typename std::decay< Resource >::type >::type >, |
1626 | std::is_nothrow_constructible< typename std::decay< Deleter >::type, typename detail::move_or_copy_construct_ref< Deleter, typename std::decay< Deleter >::type >::type > |
1627 | >::value |
1628 | )) |
1629 | { |
1630 | using unique_resource_type = unique_resource< typename std::decay< Resource >::type, typename std::decay< Deleter >::type >; |
1631 | if (!(res == invalid)) |
1632 | return unique_resource_type(static_cast< Resource&& >(res), static_cast< Deleter&& >(del)); |
1633 | else |
1634 | return unique_resource_type(default_resource_t(), static_cast< Deleter&& >(del)); |
1635 | } |
1636 | |
1637 | } // namespace scope |
1638 | } // namespace boost |
1639 | |
1640 | #include <boost/scope/detail/footer.hpp> |
1641 | |
1642 | #endif // BOOST_SCOPE_UNIQUE_RESOURCE_HPP_INCLUDED_ |
1643 | |