File: smart_ptr.hpp

package info (click to toggle)
foonathan-memory 0.7.4-3
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 1,644 kB
  • sloc: cpp: 12,425; xml: 139; sh: 48; makefile: 25
file content (197 lines) | stat: -rw-r--r-- 10,969 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
// Copyright (C) 2015-2025 Jonathan Müller and foonathan/memory contributors
// SPDX-License-Identifier: Zlib

#ifndef FOONATHAN_MEMORY_SMART_PTR_HPP_INCLUDED
#define FOONATHAN_MEMORY_SMART_PTR_HPP_INCLUDED

/// \file
/// \c std::make_unique() / \c std::make_shared() replacement allocating memory through a \concept{concept_rawallocator,RawAllocator}.
/// \note Only available on a hosted implementation.

#include "config.hpp"
#if !FOONATHAN_HOSTED_IMPLEMENTATION
#error "This header is only available for a hosted implementation."
#endif

#include <memory>
#include <type_traits>

#include "detail/utility.hpp"
#include "deleter.hpp"
#include "std_allocator.hpp"

namespace foonathan
{
    namespace memory
    {
        namespace detail
        {
            template <typename T, class RawAllocator, typename... Args>
            auto allocate_unique(allocator_reference<RawAllocator> alloc, Args&&... args)
                -> std::unique_ptr<T, allocator_deleter<T, RawAllocator>>
            {
                using raw_ptr = std::unique_ptr<T, allocator_deallocator<T, RawAllocator>>;

                auto memory = alloc.allocate_node(sizeof(T), alignof(T));
                // raw_ptr deallocates memory in case of constructor exception
                raw_ptr result(static_cast<T*>(memory), {alloc});
                // call constructor
                ::new (memory) T(detail::forward<Args>(args)...);
                // pass ownership to return value using a deleter that calls destructor
                return {result.release(), {alloc}};
            }

            template <typename T, typename... Args>
            void construct(std::true_type, T* cur, T* end, Args&&... args)
            {
                for (; cur != end; ++cur)
                    ::new (static_cast<void*>(cur)) T(detail::forward<Args>(args)...);
            }

            template <typename T, typename... Args>
            void construct(std::false_type, T* begin, T* end, Args&&... args)
            {
#if FOONATHAN_HAS_EXCEPTION_SUPPORT
                auto cur = begin;
                try
                {
                    for (; cur != end; ++cur)
                        ::new (static_cast<void*>(cur)) T(detail::forward<Args>(args)...);
                }
                catch (...)
                {
                    for (auto el = begin; el != cur; ++el)
                        el->~T();
                    throw;
                }
#else
                construct(std::true_type{}, begin, end, detail::forward<Args>(args)...);
#endif
            }

            template <typename T, class RawAllocator>
            auto allocate_array_unique(std::size_t size, allocator_reference<RawAllocator> alloc)
                -> std::unique_ptr<T[], allocator_deleter<T[], RawAllocator>>
            {
                using raw_ptr = std::unique_ptr<T[], allocator_deallocator<T[], RawAllocator>>;

                auto memory = alloc.allocate_array(size, sizeof(T), alignof(T));
                // raw_ptr deallocates memory in case of constructor exception
                raw_ptr result(static_cast<T*>(memory), {alloc, size});
                construct(std::integral_constant<bool, noexcept(T())>{}, result.get(),
                          result.get() + size);
                // pass ownership to return value using a deleter that calls destructor
                return {result.release(), {alloc, size}};
            }
        } // namespace detail

        /// A \c std::unique_ptr that deletes using a \concept{concept_rawallocator,RawAllocator}.
        ///
        /// It is an alias template using \ref allocator_deleter as \c Deleter class.
        /// \ingroup adapter
        template <typename T, class RawAllocator>
        FOONATHAN_ALIAS_TEMPLATE(unique_ptr,
                                 std::unique_ptr<T, allocator_deleter<T, RawAllocator>>);

        /// A \c std::unique_ptr that deletes using a \concept{concept_rawallocator,RawAllocator} and allows polymorphic types.
        ///
        /// It can only be created by converting a regular unique pointer to a pointer to a derived class,
        /// and is meant to be used inside containers.
        /// It is an alias template using \ref allocator_polymorphic_deleter as \c Deleter class.
        /// \note It has a relatively high overhead, so only use it if you have to.
        /// \ingroup adapter
        template <class BaseType, class RawAllocator>
        FOONATHAN_ALIAS_TEMPLATE(
            unique_base_ptr,
            std::unique_ptr<BaseType, allocator_polymorphic_deleter<BaseType, RawAllocator>>);

        /// Creates a \c std::unique_ptr using a \concept{concept_rawallocator,RawAllocator} for the allocation.
        /// \effects Allocates memory for the given type using the allocator
        /// and creates a new object inside it passing the given arguments to its constructor.
        /// \returns A \c std::unique_ptr owning that memory.
        /// \note If the allocator is stateful a reference to the \c RawAllocator will be stored inside the deleter,
        /// the caller has to ensure that the object lives as long as the smart pointer.
        /// \ingroup adapter
        template <typename T, class RawAllocator, typename... Args>
        auto allocate_unique(RawAllocator&& alloc, Args&&... args) -> FOONATHAN_REQUIRES_RET(
            !std::is_array<T>::value,
            std::unique_ptr<T, allocator_deleter<T, typename std::decay<RawAllocator>::type>>)
        {
            return detail::allocate_unique<T>(make_allocator_reference(
                                                  detail::forward<RawAllocator>(alloc)),
                                              detail::forward<Args>(args)...);
        }

        /// Creates a \c std::unique_ptr using a type-erased \concept{concept_rawallocator,RawAllocator} for the allocation.
        /// It is the same as the other overload but stores the reference to the allocator type-erased inside the \c std::unique_ptr.
        /// \effects Allocates memory for the given type using the allocator
        /// and creates a new object inside it passing the given arguments to its constructor.
        /// \returns A \c std::unique_ptr with a type-erased allocator reference owning that memory.
        /// \note If the allocator is stateful a reference to the \c RawAllocator will be stored inside the deleter,
        /// the caller has to ensure that the object lives as long as the smart pointer.
        /// \ingroup adapter
        template <typename T, class RawAllocator, typename... Args>
        auto allocate_unique(any_allocator, RawAllocator&& alloc, Args&&... args)
            -> FOONATHAN_REQUIRES_RET(!std::is_array<T>::value,
                                      std::unique_ptr<T, allocator_deleter<T, any_allocator>>)
        {
            return detail::allocate_unique<T, any_allocator>(make_allocator_reference(
                                                                 detail::forward<RawAllocator>(
                                                                     alloc)),
                                                             detail::forward<Args>(args)...);
        }

        /// Creates a \c std::unique_ptr owning an array using a \concept{concept_rawallocator,RawAllocator} for the allocation.
        /// \effects Allocates memory for an array of given size and value initializes each element inside of it.
        /// \returns A \c std::unique_ptr owning that array.
        /// \note If the allocator is stateful a reference to the \c RawAllocator will be stored inside the deleter,
        /// the caller has to ensure that the object lives as long as the smart pointer.
        /// \ingroup adapter
        template <typename T, class RawAllocator>
        auto allocate_unique(RawAllocator&& alloc, std::size_t size) -> FOONATHAN_REQUIRES_RET(
            std::is_array<T>::value,
            std::unique_ptr<T, allocator_deleter<T, typename std::decay<RawAllocator>::type>>)
        {
            return detail::allocate_array_unique<
                typename std::remove_extent<T>::type>(size,
                                                      make_allocator_reference(
                                                          detail::forward<RawAllocator>(alloc)));
        }

        /// Creates a \c std::unique_ptr owning an array using a type-erased \concept{concept_rawallocator,RawAllocator} for the allocation.
        /// It is the same as the other overload but stores the reference to the allocator type-erased inside the \c std::unique_ptr.
        /// \effects Allocates memory for an array of given size and value initializes each element inside of it.
        /// \returns A \c std::unique_ptr with a type-erased allocator reference owning that array.
        /// \note If the allocator is stateful a reference to the \c RawAllocator will be stored inside the deleter,
        /// the caller has to ensure that the object lives as long as the smart pointer.
        /// \ingroup adapter
        template <typename T, class RawAllocator>
        auto allocate_unique(any_allocator, RawAllocator&& alloc, std::size_t size)
            -> FOONATHAN_REQUIRES_RET(std::is_array<T>::value,
                                      std::unique_ptr<T, allocator_deleter<T, any_allocator>>)
        {
            return detail::allocate_array_unique<typename std::remove_extent<T>::type,
                                                 any_allocator>(size,
                                                                make_allocator_reference(
                                                                    detail::forward<RawAllocator>(
                                                                        alloc)));
        }

        /// Creates a \c std::shared_ptr using a \concept{concept_rawallocator,RawAllocator} for the allocation.
        /// It is similar to \c std::allocate_shared but uses a \c RawAllocator (and thus also supports any \c Allocator).
        /// \effects Calls \ref std_allocator::make_std_allocator to wrap the allocator and forwards to \c std::allocate_shared.
        /// \returns A \c std::shared_ptr created using \c std::allocate_shared.
        /// \note If the allocator is stateful a reference to the \c RawAllocator will be stored inside the shared pointer,
        /// the caller has to ensure that the object lives as long as the smart pointer.
        /// \ingroup adapter
        template <typename T, class RawAllocator, typename... Args>
        std::shared_ptr<T> allocate_shared(RawAllocator&& alloc, Args&&... args)
        {
            return std::allocate_shared<T>(make_std_allocator<T>(
                                               detail::forward<RawAllocator>(alloc)),
                                           detail::forward<Args>(args)...);
        }
    } // namespace memory
} // namespace foonathan

#endif // FOONATHAN_MEMORY_SMART_PTR_HPP_INCLUDED