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