1 /*
2 * Copyright (c) 2016, Facebook, Inc.
3 * All rights reserved.
4 *
5 * This source code is licensed under the BSD-style license found in the
6 * LICENSE file in the root directory of this source tree. An additional grant
7 * of patent rights can be found in the PATENTS file in the same directory.
8 */
9
10 #ifndef FATAL_INCLUDE_fatal_type_pointer_selector_h
11 #define FATAL_INCLUDE_fatal_type_pointer_selector_h
12
13 #include <memory>
14 #include <type_traits>
15 #include <utility>
16
17 namespace fatal {
18
19 /**
20 * Classes of pointers supported by `pointer_selector`,
21 * `pointer_selector_t` and `make_ptr`.
22 *
23 * @author: Marcelo Juchem <marcelo@fb.com>
24 */
25 namespace pointer_class {
26
27 struct raw;
28 struct unique;
29 struct shared;
30
31 } // namespace pointer_class {
32
33 /**
34 * Selects among several classes of pointers based on a parameter
35 * and provides the appropriate function to dynamically allocate
36 * an instance the pointee into the proper pointer class.
37 *
38 * This is a convenient mechanism for factories that don't want to
39 * force the user to commit to a single class of pointers.
40 *
41 * See also `pointer_selector_t` for a convenient alias and `make_ptr`
42 * for a convenient way of allocating the pointee.
43 *
44 * Example:
45 *
46 * struct Foo {
47 * Foo(int a, float b) { ... }
48 * };
49 *
50 * template <typename PtrClass>
51 * typename pointer_selector<PtrClass, Foo>::type my_factory(int a, float b) {
52 * return pointer_selector<PtrClass, Foo>::make(a, b);
53 * }
54 *
55 * // dynamically allocates `Foo` into an unique pointer
56 * auto instance = my_factory<pointer_class::unique>(a, b);
57 *
58 * @author: Marcelo Juchem <marcelo@fb.com>
59 */
60 template <typename PtrClass, typename T, typename... Args>
61 class pointer_selector {
62 using impl = typename PtrClass::template impl<T, Args...>;
63
64 public:
65 /**
66 * The type of the pointee.
67 *
68 * Example:
69 *
70 * // yields `int`
71 * using result1 = pointer_selector<pointer_class::raw, int>::element_type;
72 *
73 * // yields `int`
74 * using result2 = pointer_selector<
75 * pointer_class::unique, int
76 * >::element_type;
77 *
78 * @author: Marcelo Juchem <marcelo@fb.com>
79 */
80 using element_type = typename impl::element_type;
81
82 /**
83 * The raw pointer for the pointee.
84 *
85 * Example:
86 *
87 * // yields `int *`
88 * using result1 = pointer_selector<pointer_class::raw, int>::pointer;
89 *
90 * // yields `int *`
91 * using result2 = pointer_selector<pointer_class::unique, int>::pointer;
92 *
93 * @author: Marcelo Juchem <marcelo@fb.com>
94 */
95 using pointer = typename impl::pointer;
96
97 /**
98 * The pointer of the chosen class. This is either `element_type *`,
99 * `std::unique_ptr<element_type>`, `std::shared_ptr<element_type>` or
100 * `std::unique_ptr<element_type, CustomDeleter>`.
101 *
102 * Example:
103 *
104 * // yields `int *`
105 * using raw = pointer_selector<pointer_class::raw, int>::type;
106 *
107 * // yields `std::unique_ptr<int>`
108 * using unique = pointer_selector<pointer_class::unique, int>::type;
109 *
110 * // yields `std::unique_ptr<int, MyDeleter>`
111 * using unique_d = pointer_selector<
112 * pointer_class::unique, int, MyDeleter
113 * >::type;
114 *
115 * // yields `std::shared_ptr<int>`
116 * using shared = pointer_selector<pointer_class::shared, int>::type;
117 *
118 * @author: Marcelo Juchem <marcelo@fb.com>
119 */
120 using type = typename impl::type;
121
122 /**
123 * A boolean `std::integral_constant` telling whether the
124 * pointer of the chosen class automatically manages the
125 * lifetime of the pointee.
126 *
127 * When false, the pointee must be destroyed using explicit
128 * deletion.
129 *
130 * Example:
131 *
132 * // yields `false`
133 * pointer_selector<pointer_class::raw, int>::managed::value;
134 *
135 * // yields `true`
136 * pointer_selector<pointer_class::unique, int>::managed::value;
137 *
138 * @author: Marcelo Juchem <marcelo@fb.com>
139 */
140 using managed = typename impl::managed;
141
142 /**
143 * A convenient way to obtain a raw pointer out of the
144 * specified pointer class.
145 *
146 * @author: Marcelo Juchem <marcelo@fb.com>
147 */
get(type const & p)148 static pointer get(type const &p) { return impl::get(p); }
get(type const && p)149 static pointer get(type const &&p) { return impl::get(p); }
150
151 /**
152 * Allocates a raw, unique or shared pointer according to
153 * the specified pointer class.
154 *
155 * The parameters passed to this function are perfectly forwarded
156 * to the constructor of the pointee.
157 *
158 * Example:
159 *
160 * struct Foo {
161 * Foo(int a, float b) { ... }
162 * };
163 *
164 * template <typename PtrClass>
165 * typename pointer_selector<PtrClass, Foo>::type my_factory(
166 * int a, float b
167 * ) {
168 * return pointer_selector<PtrClass, Foo>::make(a, b);
169 * }
170 *
171 * // dynamically allocates `Foo` and returns a raw pointer to it
172 * auto raw = my_factory<pointer_class::raw>(a, b);
173 *
174 * // dynamically allocates `Foo` into an unique pointer
175 * auto unique = my_factory<pointer_class::unique>(a, b);
176 *
177 * // dynamically allocates `Foo` into a shared pointer
178 * auto shared = my_factory<pointer_class::shared>(a, b);
179 *
180 * @author: Marcelo Juchem <marcelo@fb.com>
181 */
182 template <typename... UArgs>
make(UArgs &&...args)183 static type make(UArgs &&...args) {
184 return impl::make(std::forward<UArgs>(args)...);
185 }
186
187 /**
188 * Deletes the pointee stored in the specified pointer class.
189 *
190 * NOTE:
191 * Unique and shared pointers don't need to be explicitly deleted.
192 * Raw pointers can be deleted using the `delete` keyword.
193 * Calling destroy on a shared pointer doesn't guarantee that the
194 * pointee will actually be deleted since there can be other
195 * shared pointers referencing it.
196 *
197 * This method is offered for convenience purposes for situations
198 * where explicit deletion is desired.
199 *
200 * @author: Marcelo Juchem <marcelo@fb.com>
201 */
destroy(type & p)202 static void destroy(type &p) { impl::destroy(p); }
destroy(type && p)203 static void destroy(type &&p) { impl::destroy(p); }
204 };
205
206 /**
207 * Alias for `pointer_selector<args>::type`.
208 *
209 * Selects among several classes of pointers based on a parameter.
210 *
211 * This is a convenient mechanism for factories that don't want to
212 * force the user to commit to a single class of pointers.
213 *
214 * See also `make_ptr` for a convenient way of allocating the pointee.
215 *
216 * Example:
217 *
218 * struct Foo {
219 * Foo(int a, float b) { ... }
220 * };
221 *
222 * template <typename PtrClass>
223 * pointer_selector_t<PtrClass, Foo> my_factory(int a, double b) {
224 * return make_ptr<PtrClass, Foo>(a, b);
225 * }
226 *
227 * // dynamically allocates `Foo` and returns a raw pointer to it
228 * auto raw = my_factory<pointer_class::raw>(a, b);
229 *
230 * // dynamically allocates `Foo` into an unique pointer
231 * auto unique = my_factory<pointer_class::unique>(a, b);
232 *
233 * // dynamically allocates `Foo` into a shared pointer
234 * auto shared = my_factory<pointer_class::shared>(a, b);
235 *
236 * // yields a `std::unique_ptr` to `Foo` using a custom deleter `MyDeleter`
237 * using my_ptr = pointer_selector_t<pointer_class::unique, Foo, MyDeleter>;
238 *
239 * @author: Marcelo Juchem <marcelo@fb.com>
240 */
241 template <typename PtrClass, typename T, typename... Args>
242 using pointer_selector_t = typename pointer_selector<
243 PtrClass, T, Args...
244 >::type;
245
246 /**
247 * Allocates a raw, unique or shared pointer of type `T` according
248 * to the specified `PtrClass` template parameter.
249 *
250 * The non-template parameters passed to this function are
251 * perfectly forwarded to the constructor of the pointee.
252 *
253 * See also `pointer_selector_t`.
254 *
255 * Example:
256 *
257 * struct Foo {
258 * Foo(int a, double b) { ... }
259 * };
260 *
261 * template <typename PtrClass>
262 * pointer_selector_t<PtrClass, Foo> my_factory(int a, double b) {
263 * return make_ptr<PtrClass, Foo>(a, b);
264 * }
265 *
266 * // dynamically allocates `Foo` and returns a raw pointer to it
267 * auto raw = my_factory<pointer_class::raw>(a, b);
268 *
269 * // dynamically allocates `Foo` into an unique pointer
270 * auto unique = my_factory<pointer_class::unique>(a, b);
271 *
272 * // dynamically allocates `Foo` into a shared pointer
273 * auto shared = my_factory<pointer_class::shared>(a, b);
274 *
275 * @author: Marcelo Juchem <marcelo@fb.com>
276 */
277 template <typename PtrClass, typename T, typename... Args, typename... UArgs>
make_ptr(UArgs &&...args)278 static pointer_selector_t<PtrClass, T, Args...> make_ptr(UArgs &&...args) {
279 return pointer_selector<PtrClass, T, Args...>::make(
280 std::forward<UArgs>(args)...
281 );
282 }
283
284 ////////////////////////////
285 // IMPLEMENTATION DETAILS //
286 ////////////////////////////
287
288 namespace pointer_class {
289
290 struct raw {
291 template <typename T>
292 struct impl {
293 using element_type = T;
294 using pointer = element_type *;
295 using type = pointer;
296 using managed = std::false_type;
297
getraw::impl298 static pointer get(type const &p) { return p; }
299
300 template <typename... UArgs>
makeraw::impl301 static type make(UArgs &&...args) {
302 return new element_type(std::forward<UArgs>(args)...);
303 }
304
destroyraw::impl305 static void destroy(type &p) { delete p; }
306 };
307 };
308
309 struct unique {
310 template <typename T, typename... Args>
311 struct impl {
312 using element_type = T;
313 using pointer = element_type *;
314 using type = std::unique_ptr<element_type, Args...>;
315 using managed = std::true_type;
316
getunique::impl317 static pointer get(type const &p) { return p.get(); }
318
319 template <typename... UArgs>
makeunique::impl320 static type make(UArgs &&...args) {
321 return type(new element_type(std::forward<UArgs>(args)...));
322 }
323
destroyunique::impl324 static void destroy(type &p) { p.reset(); }
325 };
326 };
327
328 struct shared {
329 template <typename T>
330 struct impl {
331 using element_type = T;
332 using pointer = element_type *;
333 using type = std::shared_ptr<element_type>;
334 using managed = std::true_type;
335
getshared::impl336 static pointer get(type const &p) { return p.get(); }
337
338 template <typename... UArgs>
makeshared::impl339 static type make(UArgs &&...args) {
340 return std::make_shared<element_type>(std::forward<UArgs>(args)...);
341 }
342
destroyshared::impl343 static void destroy(type &p) { p.reset(); }
344 };
345 };
346
347 } // namespace pointer_class {
348 } // namespace fatal {
349
350 #endif // FATAL_INCLUDE_fatal_type_pointer_selector_h
351