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
39namespace boost {
40namespace 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 */
58template< auto DefaultValue, auto... UnallocatedValues >
59struct 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
79struct default_resource_t { };
80
81//! Keyword representing default, unallocated resource argument
82BOOST_INLINE_VARIABLE constexpr default_resource_t default_resource = { };
83
84namespace detail {
85
86// The type trait indicates whether \c T is a possibly qualified \c default_resource_t type
87template< typename T >
88struct is_default_resource : public std::false_type { };
89template< >
90struct is_default_resource< default_resource_t > : public std::true_type { };
91template< >
92struct is_default_resource< const default_resource_t > : public std::true_type { };
93template< >
94struct is_default_resource< volatile default_resource_t > : public std::true_type { };
95template< >
96struct is_default_resource< const volatile default_resource_t > : public std::true_type { };
97template< typename T >
98struct is_default_resource< T& > : public is_default_resource< T >::type { };
99
100// Lightweight reference wrapper
101template< typename T >
102class ref_wrapper
103{
104private:
105 T* m_value;
106
107public:
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
138template< typename T >
139struct wrap_reference
140{
141 using type = T;
142};
143
144template< typename T >
145struct wrap_reference< T& >
146{
147 using type = ref_wrapper< T >;
148};
149
150template< typename Resource, bool UseCompactStorage >
151class resource_holder :
152 public detail::compact_storage< typename wrap_reference< Resource >::type >
153{
154public:
155 using resource_type = Resource;
156 using internal_resource_type = typename wrap_reference< resource_type >::type;
157
158private:
159 using resource_base = detail::compact_storage< internal_resource_type >;
160
161public:
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
215private:
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
234template< typename Resource >
235class resource_holder< Resource, false >
236{
237public:
238 using resource_type = Resource;
239 using internal_resource_type = typename wrap_reference< resource_type >::type;
240
241private:
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
245public:
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
302private:
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
321template< typename Resource, typename Deleter >
322class deleter_holder :
323 public detail::compact_storage< typename wrap_reference< Deleter >::type >
324{
325public:
326 using resource_type = Resource;
327 using deleter_type = Deleter;
328 using internal_deleter_type = typename wrap_reference< deleter_type >::type;
329
330private:
331 using deleter_base = detail::compact_storage< internal_deleter_type >;
332
333public:
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
381private:
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 */
431template< typename Resource, typename Deleter >
432using 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
438template< typename Resource, typename Deleter, typename Traits >
439class unique_resource_data :
440 public detail::resource_holder< Resource, use_resource_compact_storage< Resource, Deleter >::value >,
441 public detail::deleter_holder< Resource, Deleter >
442{
443public:
444 using resource_type = Resource;
445 using deleter_type = Deleter;
446 using traits_type = Traits;
447
448private:
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
453public:
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
462public:
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
631private:
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
720template< typename Resource, typename Deleter >
721class 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{
725public:
726 using resource_type = Resource;
727 using deleter_type = Deleter;
728 using traits_type = void;
729
730private:
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
734public:
735 using internal_resource_type = typename resource_holder::internal_resource_type;
736 using internal_deleter_type = typename deleter_holder::internal_deleter_type;
737
738private:
739 bool m_allocated;
740
741public:
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
913private:
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
993template< typename T >
994struct 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
1004template< typename T >
1005struct is_dereferenceable : public is_dereferenceable_impl< T >::type { };
1006template< >
1007struct is_dereferenceable< void* > : public std::false_type { };
1008template< >
1009struct is_dereferenceable< const void* > : public std::false_type { };
1010template< >
1011struct is_dereferenceable< volatile void* > : public std::false_type { };
1012template< >
1013struct is_dereferenceable< const volatile void* > : public std::false_type { };
1014template< >
1015struct is_dereferenceable< void*& > : public std::false_type { };
1016template< >
1017struct is_dereferenceable< const void*& > : public std::false_type { };
1018template< >
1019struct is_dereferenceable< volatile void*& > : public std::false_type { };
1020template< >
1021struct is_dereferenceable< const volatile void*& > : public std::false_type { };
1022template< >
1023struct is_dereferenceable< void* const& > : public std::false_type { };
1024template< >
1025struct is_dereferenceable< const void* const& > : public std::false_type { };
1026template< >
1027struct is_dereferenceable< volatile void* const& > : public std::false_type { };
1028template< >
1029struct is_dereferenceable< const volatile void* const& > : public std::false_type { };
1030template< >
1031struct is_dereferenceable< void* volatile& > : public std::false_type { };
1032template< >
1033struct is_dereferenceable< const void* volatile& > : public std::false_type { };
1034template< >
1035struct is_dereferenceable< volatile void* volatile& > : public std::false_type { };
1036template< >
1037struct is_dereferenceable< const volatile void* volatile& > : public std::false_type { };
1038template< >
1039struct is_dereferenceable< void* const volatile& > : public std::false_type { };
1040template< >
1041struct is_dereferenceable< const void* const volatile& > : public std::false_type { };
1042template< >
1043struct is_dereferenceable< volatile void* const volatile& > : public std::false_type { };
1044template< >
1045struct is_dereferenceable< const volatile void* const volatile& > : public std::false_type { };
1046
1047template< typename T, bool = detail::is_dereferenceable< T >::value >
1048struct dereference_traits { };
1049template< typename T >
1050struct 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 */
1112template< typename Resource, typename Deleter, typename Traits BOOST_SCOPE_DETAIL_DOC(= void) >
1113class unique_resource
1114{
1115public:
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
1124private:
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
1132public:
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
1569private:
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)
1597template<
1598 typename Resource,
1599 typename Deleter,
1600 typename = typename std::enable_if< !detail::is_default_resource< Resource >::value >::type
1601>
1602unique_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 */
1620template< typename Resource, typename Deleter, typename Invalid >
1621inline unique_resource< typename std::decay< Resource >::type, typename std::decay< Deleter >::type >
1622make_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

source code of boost/libs/scope/include/boost/scope/unique_resource.hpp