easier rebinding of allocators
[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  */
38
39 template<typename T, typename... Args>
40 std::unique_ptr<T> make_unique(Args&&... args) {
41   return std::unique_ptr<T>(new T(std::forward<Args>(args)...));
42 }
43
44 /**
45  * Wrap a SimpleAllocator into a STL-compliant allocator.
46  *
47  * The SimpleAllocator must provide two methods:
48  *    void* allocate(size_t size);
49  *    void deallocate(void* ptr);
50  * which, respectively, allocate a block of size bytes (aligned to the maximum
51  * alignment required on your system), throwing std::bad_alloc if the
52  * allocation can't be satisfied, and free a previously allocated block.
53  *
54  * Note that the following allocator resembles the standard allocator
55  * quite well:
56  *
57  * class MallocAllocator {
58  *  public:
59  *   void* allocate(size_t size) {
60  *     void* p = malloc(size);
61  *     if (!p) throw std::bad_alloc();
62  *     return p;
63  *   }
64  *   void deallocate(void* p) {
65  *     free(p);
66  *   }
67  * };
68  *
69  * author: Tudor Bosman <tudorb@fb.com>
70  */
71
72 // This would be so much simpler with std::allocator_traits, but gcc 4.6.2
73 // doesn't support it
74 template <class Alloc, class T> class StlAllocator;
75
76 template <class Alloc> class StlAllocator<Alloc, void> {
77  public:
78   typedef void value_type;
79   typedef void* pointer;
80   typedef const void* const_pointer;
81
82   StlAllocator() : alloc_(nullptr) { }
83   explicit StlAllocator(Alloc* alloc) : alloc_(alloc) { }
84
85   Alloc* alloc() const {
86     return alloc_;
87   }
88
89   template <class U> struct rebind {
90     typedef StlAllocator<Alloc, U> other;
91   };
92
93   bool operator!=(const StlAllocator<Alloc, void>& other) const {
94     return alloc_ != other.alloc_;
95   }
96
97   bool operator==(const StlAllocator<Alloc, void>& other) const {
98     return alloc_ == other.alloc_;
99   }
100
101  private:
102   Alloc* alloc_;
103 };
104
105 template <class Alloc, class T>
106 class StlAllocator {
107  public:
108   typedef T value_type;
109   typedef T* pointer;
110   typedef const T* const_pointer;
111   typedef T& reference;
112   typedef const T& const_reference;
113
114   typedef ptrdiff_t difference_type;
115   typedef size_t size_type;
116
117   StlAllocator() : alloc_(nullptr) { }
118   explicit StlAllocator(Alloc* alloc) : alloc_(alloc) { }
119
120   template <class U> StlAllocator(const StlAllocator<Alloc, U>& other)
121     : alloc_(other.alloc()) { }
122
123   T* allocate(size_t n, const void* hint = nullptr) {
124     return static_cast<T*>(alloc_->allocate(n * sizeof(T)));
125   }
126
127   void deallocate(T* p, size_t n) {
128     alloc_->deallocate(p);
129   }
130
131   size_t max_size() const {
132     return std::numeric_limits<size_t>::max();
133   }
134
135   T* address(T& x) const {
136     return std::addressof(x);
137   }
138
139   const T* address(const T& x) const {
140     return std::addressof(x);
141   }
142
143   template <class... Args>
144   void construct(T* p, Args&&... args) {
145     new (p) T(std::forward<Args>(args)...);
146   }
147
148   void destroy(T* p) {
149     p->~T();
150   }
151
152   Alloc* alloc() const {
153     return alloc_;
154   }
155
156   template <class U> struct rebind {
157     typedef StlAllocator<Alloc, U> other;
158   };
159
160   bool operator!=(const StlAllocator<Alloc, T>& other) const {
161     return alloc_ != other.alloc_;
162   }
163
164   bool operator==(const StlAllocator<Alloc, T>& other) const {
165     return alloc_ == other.alloc_;
166   }
167
168  private:
169   Alloc* alloc_;
170 };
171
172 /**
173  * Helper function to obtain rebound allocators
174  *
175  * @author: Marcelo Juchem <marcelo@fb.com>
176  */
177 template <typename T, typename Allocator>
178 typename Allocator::template rebind<T>::other rebind_allocator(
179   Allocator const &allocator
180 ) {
181   return typename Allocator::template rebind<T>::other(allocator);
182 }
183
184 /*
185  * Helper classes/functions for creating a unique_ptr using a custom allocator
186  *
187  * @author: Marcelo Juchem <marcelo@fb.com>
188  */
189
190 // A deleter implementation based on std::default_delete,
191 // which uses a custom allocator to free memory
192 template <typename Allocator>
193 class allocator_delete {
194   typedef typename std::remove_reference<Allocator>::type allocator_type;
195
196 public:
197   allocator_delete() = default;
198
199   explicit allocator_delete(const allocator_type& allocator):
200     allocator_(allocator)
201   {}
202
203   explicit allocator_delete(allocator_type&& allocator):
204     allocator_(std::move(allocator))
205   {}
206
207   template <typename U>
208   allocator_delete(const allocator_delete<U>& other):
209     allocator_(other.get_allocator())
210   {}
211
212   allocator_type& get_allocator() const {
213     return allocator_;
214   }
215
216   void operator()(typename allocator_type::pointer p) const {
217     if (!p) {
218       return;
219     }
220
221     allocator_.destroy(p);
222     allocator_.deallocate(p, 1);
223   }
224
225 private:
226   mutable allocator_type allocator_;
227 };
228
229 template <typename T, typename Allocator>
230 class is_simple_allocator {
231   FOLLY_CREATE_HAS_MEMBER_FN_TRAITS(has_destroy, destroy);
232
233   typedef typename std::remove_const<
234     typename std::remove_reference<Allocator>::type
235   >::type allocator;
236   typedef typename std::remove_reference<T>::type value_type;
237   typedef value_type* pointer;
238
239 public:
240   constexpr static bool value = !has_destroy<allocator, void(pointer)>::value
241     && !has_destroy<allocator, void(void*)>::value;
242 };
243
244 template <typename T, typename Allocator>
245 struct as_stl_allocator {
246   typedef typename std::conditional<
247     is_simple_allocator<T, Allocator>::value,
248     folly::StlAllocator<
249       typename std::remove_reference<Allocator>::type,
250       typename std::remove_reference<T>::type
251     >,
252     typename std::remove_reference<Allocator>::type
253   >::type type;
254 };
255
256 template <typename T, typename Allocator>
257 typename std::enable_if<
258   is_simple_allocator<T, Allocator>::value,
259   folly::StlAllocator<
260     typename std::remove_reference<Allocator>::type,
261     typename std::remove_reference<T>::type
262   >
263 >::type make_stl_allocator(Allocator&& allocator) {
264   return folly::StlAllocator<
265     typename std::remove_reference<Allocator>::type,
266     typename std::remove_reference<T>::type
267   >(&allocator);
268 }
269
270 template <typename T, typename Allocator>
271 typename std::enable_if<
272   !is_simple_allocator<T, Allocator>::value,
273   typename std::remove_reference<Allocator>::type
274 >::type make_stl_allocator(Allocator&& allocator) {
275   return std::move(allocator);
276 }
277
278 /**
279  * AllocatorUniquePtr: a unique_ptr that supports both STL-style
280  * allocators and SimpleAllocator
281  *
282  * @author: Marcelo Juchem <marcelo@fb.com>
283  */
284
285 template <typename T, typename Allocator>
286 struct AllocatorUniquePtr {
287   typedef std::unique_ptr<T,
288     folly::allocator_delete<
289       typename std::conditional<
290         is_simple_allocator<T, Allocator>::value,
291         folly::StlAllocator<typename std::remove_reference<Allocator>::type, T>,
292         typename std::remove_reference<Allocator>::type
293       >::type
294     >
295   > type;
296 };
297
298 /**
299  * Functions to allocate a unique_ptr / shared_ptr, supporting both
300  * STL-style allocators and SimpleAllocator, analog to std::allocate_shared
301  *
302  * @author: Marcelo Juchem <marcelo@fb.com>
303  */
304
305 template <typename T, typename Allocator, typename ...Args>
306 typename AllocatorUniquePtr<T, Allocator>::type allocate_unique(
307   Allocator&& allocator, Args&&... args
308 ) {
309   auto stlAllocator = folly::make_stl_allocator<T>(
310     std::forward<Allocator>(allocator)
311   );
312   auto p = stlAllocator.allocate(1);
313
314   try {
315     stlAllocator.construct(p, std::forward<Args>(args)...);
316
317     return {p,
318       folly::allocator_delete<decltype(stlAllocator)>(std::move(stlAllocator))
319     };
320   } catch (...) {
321     stlAllocator.deallocate(p, 1);
322     throw;
323   }
324 }
325
326 template <typename T, typename Allocator, typename ...Args>
327 std::shared_ptr<T> allocate_shared(Allocator&& allocator, Args&&... args) {
328   return std::allocate_shared<T>(
329     folly::make_stl_allocator<T>(std::forward<Allocator>(allocator)),
330     std::forward<Args>(args)...
331   );
332 }
333
334 }  // namespace folly
335
336 #endif /* FOLLY_MEMORY_H_ */