3be3d78f8c86bca3844056ccbbf08282e786691f
[folly.git] / folly / Memory.h
1 /*
2  * Copyright 2013 Facebook, Inc.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *   http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16
17 #ifndef FOLLY_MEMORY_H_
18 #define FOLLY_MEMORY_H_
19
20 #include "folly/Traits.h"
21
22 #include <memory>
23 #include <limits>
24 #include <utility>
25 #include <exception>
26 #include <stdexcept>
27
28 #include <cstddef>
29
30 namespace folly {
31
32 /**
33  * For exception safety and consistency with make_shared. Erase me when
34  * we have std::make_unique().
35  *
36  * @author Louis Brandy (ldbrandy@fb.com)
37  * @author Xu Ning (xning@fb.com)
38  */
39
40 template<typename T, typename Dp = std::default_delete<T>, typename... Args>
41 std::unique_ptr<T, Dp> make_unique(Args&&... args) {
42   return std::unique_ptr<T, Dp>(new T(std::forward<Args>(args)...));
43 }
44
45 /*
46  * StlAllocator wraps a SimpleAllocator into a STL-compliant
47  * allocator, maintaining an instance pointer to the simple allocator
48  * object.  The underlying SimpleAllocator object must outlive all
49  * instances of StlAllocator using it.
50  *
51  * A SimpleAllocator must provide two methods:
52  *
53  *    void* allocate(size_t size);
54  *    void deallocate(void* ptr);
55  *
56  * which, respectively, allocate a block of size bytes (aligned to the
57  * maximum alignment required on your system), throwing std::bad_alloc
58  * if the allocation can't be satisfied, and free a previously
59  * allocated block.
60  *
61  * Note that the following allocator resembles the standard allocator
62  * quite well:
63  *
64  * class MallocAllocator {
65  *  public:
66  *   void* allocate(size_t size) {
67  *     void* p = malloc(size);
68  *     if (!p) throw std::bad_alloc();
69  *     return p;
70  *   }
71  *   void deallocate(void* p) {
72  *     free(p);
73  *   }
74  * };
75  *
76  * But note that if you pass StlAllocator<MallocAllocator,...> to a
77  * standard container it will be larger due to the contained state
78  * pointer.
79  *
80  * author: Tudor Bosman <tudorb@fb.com>
81  */
82
83 // This would be so much simpler with std::allocator_traits, but gcc 4.6.2
84 // doesn't support it.
85 template <class Alloc, class T> class StlAllocator;
86
87 template <class Alloc> class StlAllocator<Alloc, void> {
88  public:
89   typedef void value_type;
90   typedef void* pointer;
91   typedef const void* const_pointer;
92
93   StlAllocator() : alloc_(nullptr) { }
94   explicit StlAllocator(Alloc* alloc) : alloc_(alloc) { }
95
96   Alloc* alloc() const {
97     return alloc_;
98   }
99
100   template <class U> struct rebind {
101     typedef StlAllocator<Alloc, U> other;
102   };
103
104   bool operator!=(const StlAllocator<Alloc, void>& other) const {
105     return alloc_ != other.alloc_;
106   }
107
108   bool operator==(const StlAllocator<Alloc, void>& other) const {
109     return alloc_ == other.alloc_;
110   }
111
112  private:
113   Alloc* alloc_;
114 };
115
116 template <class Alloc, class T>
117 class StlAllocator {
118  public:
119   typedef T value_type;
120   typedef T* pointer;
121   typedef const T* const_pointer;
122   typedef T& reference;
123   typedef const T& const_reference;
124
125   typedef ptrdiff_t difference_type;
126   typedef size_t size_type;
127
128   StlAllocator() : alloc_(nullptr) { }
129   explicit StlAllocator(Alloc* alloc) : alloc_(alloc) { }
130
131   template <class U> StlAllocator(const StlAllocator<Alloc, U>& other)
132     : alloc_(other.alloc()) { }
133
134   T* allocate(size_t n, const void* hint = nullptr) {
135     return static_cast<T*>(alloc_->allocate(n * sizeof(T)));
136   }
137
138   void deallocate(T* p, size_t n) {
139     alloc_->deallocate(p);
140   }
141
142   size_t max_size() const {
143     return std::numeric_limits<size_t>::max();
144   }
145
146   T* address(T& x) const {
147     return std::addressof(x);
148   }
149
150   const T* address(const T& x) const {
151     return std::addressof(x);
152   }
153
154   template <class... Args>
155   void construct(T* p, Args&&... args) {
156     new (p) T(std::forward<Args>(args)...);
157   }
158
159   void destroy(T* p) {
160     p->~T();
161   }
162
163   Alloc* alloc() const {
164     return alloc_;
165   }
166
167   template <class U> struct rebind {
168     typedef StlAllocator<Alloc, U> other;
169   };
170
171   bool operator!=(const StlAllocator<Alloc, T>& other) const {
172     return alloc_ != other.alloc_;
173   }
174
175   bool operator==(const StlAllocator<Alloc, T>& other) const {
176     return alloc_ == other.alloc_;
177   }
178
179  private:
180   Alloc* alloc_;
181 };
182
183 /**
184  * Helper function to obtain rebound allocators
185  *
186  * @author: Marcelo Juchem <marcelo@fb.com>
187  */
188 template <typename T, typename Allocator>
189 typename Allocator::template rebind<T>::other rebind_allocator(
190   Allocator const& allocator
191 ) {
192   return typename Allocator::template rebind<T>::other(allocator);
193 }
194
195 /*
196  * Helper classes/functions for creating a unique_ptr using a custom
197  * allocator.
198  *
199  * @author: Marcelo Juchem <marcelo@fb.com>
200  */
201
202 // Derives from the allocator to take advantage of the empty base
203 // optimization when possible.
204 template <typename Allocator>
205 class allocator_delete
206   : private std::remove_reference<Allocator>::type
207 {
208   typedef typename std::remove_reference<Allocator>::type allocator_type;
209
210 public:
211   allocator_delete() = default;
212
213   explicit allocator_delete(const allocator_type& allocator)
214     : allocator_type(allocator)
215   {}
216
217   explicit allocator_delete(allocator_type&& allocator)
218     : allocator_type(std::move(allocator))
219   {}
220
221   template <typename U>
222   allocator_delete(const allocator_delete<U>& other)
223     : allocator_type(other.get_allocator())
224   {}
225
226   allocator_type& get_allocator() const { return *this; }
227
228   void operator()(typename allocator_type::pointer p) const {
229     if (!p) return;
230     const_cast<allocator_delete*>(this)->destroy(p);
231     const_cast<allocator_delete*>(this)->deallocate(p, 1);
232   }
233 };
234
235 template <typename T, typename Allocator>
236 class is_simple_allocator {
237   FOLLY_CREATE_HAS_MEMBER_FN_TRAITS(has_destroy, destroy);
238
239   typedef typename std::remove_const<
240     typename std::remove_reference<Allocator>::type
241   >::type allocator;
242   typedef typename std::remove_reference<T>::type value_type;
243   typedef value_type* pointer;
244
245 public:
246   constexpr static bool value = !has_destroy<allocator, void(pointer)>::value
247     && !has_destroy<allocator, void(void*)>::value;
248 };
249
250 template <typename T, typename Allocator>
251 struct as_stl_allocator {
252   typedef typename std::conditional<
253     is_simple_allocator<T, Allocator>::value,
254     folly::StlAllocator<
255       typename std::remove_reference<Allocator>::type,
256       typename std::remove_reference<T>::type
257     >,
258     typename std::remove_reference<Allocator>::type
259   >::type type;
260 };
261
262 template <typename T, typename Allocator>
263 typename std::enable_if<
264   is_simple_allocator<T, Allocator>::value,
265   folly::StlAllocator<
266     typename std::remove_reference<Allocator>::type,
267     typename std::remove_reference<T>::type
268   >
269 >::type make_stl_allocator(Allocator&& allocator) {
270   return folly::StlAllocator<
271     typename std::remove_reference<Allocator>::type,
272     typename std::remove_reference<T>::type
273   >(&allocator);
274 }
275
276 template <typename T, typename Allocator>
277 typename std::enable_if<
278   !is_simple_allocator<T, Allocator>::value,
279   typename std::remove_reference<Allocator>::type
280 >::type make_stl_allocator(Allocator&& allocator) {
281   return std::move(allocator);
282 }
283
284 /**
285  * AllocatorUniquePtr: a unique_ptr that supports both STL-style
286  * allocators and SimpleAllocator
287  *
288  * @author: Marcelo Juchem <marcelo@fb.com>
289  */
290
291 template <typename T, typename Allocator>
292 struct AllocatorUniquePtr {
293   typedef std::unique_ptr<T,
294     folly::allocator_delete<
295       typename std::conditional<
296         is_simple_allocator<T, Allocator>::value,
297         folly::StlAllocator<typename std::remove_reference<Allocator>::type, T>,
298         typename std::remove_reference<Allocator>::type
299       >::type
300     >
301   > type;
302 };
303
304 /**
305  * Functions to allocate a unique_ptr / shared_ptr, supporting both
306  * STL-style allocators and SimpleAllocator, analog to std::allocate_shared
307  *
308  * @author: Marcelo Juchem <marcelo@fb.com>
309  */
310
311 template <typename T, typename Allocator, typename ...Args>
312 typename AllocatorUniquePtr<T, Allocator>::type allocate_unique(
313   Allocator&& allocator, Args&&... args
314 ) {
315   auto stlAllocator = folly::make_stl_allocator<T>(
316     std::forward<Allocator>(allocator)
317   );
318   auto p = stlAllocator.allocate(1);
319
320   try {
321     stlAllocator.construct(p, std::forward<Args>(args)...);
322
323     return {p,
324       folly::allocator_delete<decltype(stlAllocator)>(std::move(stlAllocator))
325     };
326   } catch (...) {
327     stlAllocator.deallocate(p, 1);
328     throw;
329   }
330 }
331
332 template <typename T, typename Allocator, typename ...Args>
333 std::shared_ptr<T> allocate_shared(Allocator&& allocator, Args&&... args) {
334   return std::allocate_shared<T>(
335     folly::make_stl_allocator<T>(std::forward<Allocator>(allocator)),
336     std::forward<Args>(args)...
337   );
338 }
339
340 }  // namespace folly
341
342 #endif /* FOLLY_MEMORY_H_ */