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