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_qualifier_h
11 #define FATAL_INCLUDE_fatal_type_qualifier_h
12 
13 #include <type_traits>
14 
15 namespace fatal {
16 
17 /**
18  * Member function const/volatile qualifiers category.
19  *
20  * `none`:  neither const nor volatile
21  * `c`:     const but not volatile
22  * `v`:     volatile but not const
23  * `cv`:    both const and volatile (guaranteed
24  *          to be the bitwise or of `c` and `v`)
25  *
26  * @author: Marcelo Juchem <marcelo@fb.com>
27  */
28 enum class cv_qualifier:
29   unsigned char
30 {
31   none = 0,
32   c = 1,
33   v = 2,
34   cv = c | v
35 };
36 
37 /**
38  * Bitwise and (&) operator for `cv_qualifier` to make it easy to
39  * check for the presence of either the const or volatile qualifier:
40  *
41  * Example:
42  *
43  *  void check_constness(cv_qualifier q) {
44  *    if (q & cv_qualifier::c) {
45  *      cout << "it is const, dunno about volatile";
46  *    }
47  *  }
48  *
49  * @author: Marcelo Juchem <marcelo@fb.com>
50  */
51 static constexpr bool operator &(cv_qualifier lhs, cv_qualifier rhs) {
52   using type = std::underlying_type<cv_qualifier>::type;
53   return static_cast<bool>(static_cast<type>(lhs) & static_cast<type>(rhs));
54 }
55 
56 enum class ref_qualifier:
57   unsigned char
58 {
59   none = 0,
60   lvalue = 1,
61   rvalue = 2
62 };
63 
64 /**
65  * Applies `std::add_const` to a type iff some other type is const.
66  *
67  * Example:
68  *
69  *  struct foo {};
70  *
71  *  // yields `foo`
72  *  using result1 = add_const_from<foo, int>::type;
73  *  using result1 = add_const_from_t<foo, int>;
74  *
75  *  // yields `foo const`
76  *  using result2 = add_const_from<foo, int const>::type;
77  *  using result2 = add_const_from_t<foo, int const>;
78  *
79  *  // yields `foo const`
80  *  using result3 = add_const_from<foo const, int const>::type;
81  *  using result3 = add_const_from_t<foo const, int const>;
82  *
83  * @author: Marcelo Juchem <marcelo@fb.com>
84  */
85 template <typename T, typename>
86 struct add_const_from {
87   using type = T;
88 };
89 
90 template <typename T, typename TFrom>
91 struct add_const_from<T, TFrom const> {
92   using type = typename std::add_const<T>::type;
93 };
94 
95 template <typename T, typename TFrom>
96 using add_const_from_t = typename add_const_from<T, TFrom>::type;
97 
98 /**
99  * Applies `std::add_volatile` to a type iff some other type is volatile.
100  *
101  * Example:
102  *
103  *  struct foo {};
104  *
105  *  // yields `foo`
106  *  using result1 = add_volatile_from<foo, int>::type;
107  *  using result1 = add_volatile_from_t<foo, int>;
108  *
109  *  // yields `foo const`
110  *  using result2 = add_volatile_from<foo, int volatile>::type;
111  *  using result2 = add_volatile_from_t<foo, int volatile>;
112  *
113  *  // yields `foo const`
114  *  using result3 = add_volatile_from<foo volatile, int volatile>::type;
115  *  using result3 = add_volatile_from_t<foo volatile, int volatile>;
116  *
117  * @author: Marcelo Juchem <marcelo@fb.com>
118  */
119 template <typename T, typename>
120 struct add_volatile_from {
121   using type = T;
122 };
123 
124 template <typename T, typename TFrom>
125 struct add_volatile_from<T, TFrom volatile> {
126   using type = typename std::add_volatile<T>::type;
127 };
128 
129 template <typename T, typename TFrom>
130 using add_volatile_from_t = typename add_volatile_from<T, TFrom>::type;
131 
132 /**
133  * Applies `std::add_const` to a type iff some other type is const.
134  * Applies `std::add_volatile` to a type iff some other type is volatile.
135  * Applies `std::add_cv` toa  type iff some other type is const volatile.
136  *
137  * Example:
138  *
139  *  struct foo {};
140  *
141  *  // yields `foo`
142  *  using result1 = add_cv_from<foo, int>::type;
143  *  using result1 = add_cv_from_t<foo, int>;
144  *
145  *  // yields `foo const`
146  *  using result2 = add_cv_from<foo, int const>::type;
147  *  using result2 = add_cv_from_t<foo, int const>;
148  *
149  *  // yields `foo const`
150  *  using result3 = add_cv_from<foo const, int const>::type;
151  *  using result3 = add_cv_from_t<foo const, int const>;
152  *
153  *  // yields `foo volatile`
154  *  using result4 = add_cv_from<foo, int volatile>::type;
155  *  using result4 = add_cv_from_t<foo, int volatile>;
156  *
157  *  // yields `foo volatile`
158  *  using result5 = add_cv_from<foo volatile, int volatile>::type;
159  *  using result5 = add_cv_from_t<foo volatile, int volatile>;
160  *
161  *  // yields `foo const volatile`
162  *  using result6 = add_cv_from<foo, int const volatile>::type;
163  *  using result6 = add_cv_from_t<foo, int const volatile>;
164  *
165  *  // yields `foo const volatile`
166  *  using result7 = add_cv_from<foo const volatile, int const volatile>::type;
167  *  using result7 = add_cv_from_t<foo const volatile, int const volatile>;
168  *
169  * @author: Marcelo Juchem <marcelo@fb.com>
170  */
171 template <typename T, typename>
172 struct add_cv_from {
173   using type = T;
174 };
175 
176 template <typename T, typename TFrom>
177 struct add_cv_from<T, TFrom const> {
178   using type = typename std::add_const<T>::type;
179 };
180 
181 template <typename T, typename TFrom>
182 struct add_cv_from<T, TFrom volatile> {
183   using type = typename std::add_volatile<T>::type;
184 };
185 
186 template <typename T, typename TFrom>
187 struct add_cv_from<T, TFrom const volatile> {
188   using type = typename std::add_cv<T>::type;
189 };
190 
191 template <typename T, typename TFrom>
192 using add_cv_from_t = typename add_cv_from<T, TFrom>::type;
193 
194 /**
195  * Given types `T` and `U`:
196  * - if `U` is not a reference, yield `T`
197  * - if `U` is an l-value reference, yield `std::add_lvalue_reference<T>::type`
198  * - if `U` is an r-value reference, yield `std::add_rvalue_reference<T>::type`
199  *
200  * Example:
201  *
202  *  struct foo {};
203  *
204  *  // yields `foo`
205  *  using result1 = add_reference_from<foo, int>::type;
206  *  using result1 = add_reference_from_t<foo, int>;
207  *
208  *  // yields `foo &&`
209  *  using result2 = add_reference_from<foo &&, int>::type;
210  *  using result2 = add_reference_from_t<foo &&, int>;
211  *
212  *  // yields `foo &`
213  *  using result3 = add_reference_from<foo, int &>::type;
214  *  using result3 = add_reference_from_t<foo, int &>;
215  *
216  *  // yields `foo &`
217  *  using result4 = add_reference_from<foo &&, int &>::type;
218  *  using result4 = add_reference_from_t<foo &&, int &>;
219  *
220  *  // yields `foo &&`
221  *  using result5 = add_reference_from<foo, int &&>::type;
222  *  using result5 = add_reference_from_t<foo, int &&>;
223  *
224  *  // yields `foo &`
225  *  using result6 = add_reference_from<foo &, int &&>::type;
226  *  using result6 = add_reference_from_t<foo &, int &&>;
227  *
228  * @author: Marcelo Juchem <marcelo@fb.com>
229  */
230 template <typename T, typename>
231 struct add_reference_from {
232   using type = T;
233 };
234 
235 template <typename T, typename TFrom>
236 struct add_reference_from<T, TFrom &> {
237   using type = typename std::add_lvalue_reference<T>::type;
238 };
239 
240 template <typename T, typename TFrom>
241 struct add_reference_from<T, TFrom &&> {
242   using type = typename std::add_rvalue_reference<T>::type;
243 };
244 
245 template <typename T, typename TFrom>
246 using add_reference_from_t = typename add_reference_from<T, TFrom>::type;
247 
248 /**
249  * Combine the effects of `add_cv_from` and `add_reference_from`.
250  *
251  * Example:
252  *
253  *  struct foo {};
254  *
255  *  // yields `foo`
256  *  using result_1 = add_cv_reference_from<foo, int>::type;
257  *  using result_1 = add_cv_reference_from_t<foo, int>;
258  *
259  *  // yields `foo const &`
260  *  using result_2 = add_cv_reference_from<foo, int const &>::type;
261  *  using result_2 = add_cv_reference_from_t<foo, int const &>;
262  *
263  *  // yields `foo volatile &&`
264  *  using result_3 = add_cv_reference_from<foo, int volatile &&>::type;
265  *  using result_3 = add_cv_reference_from_t<foo, int volatile &&>;
266  *
267  * @author: Marcelo Juchem <marcelo@fb.com>
268  */
269 template <typename T, typename>
270 struct add_cv_reference_from {
271   using type = T;
272 };
273 
274 template <typename T, typename TFrom>
275 struct add_cv_reference_from<T, TFrom const> {
276   using type = typename std::add_const<T>::type;
277 };
278 
279 template <typename T, typename TFrom>
280 struct add_cv_reference_from<T &, TFrom const> {
281   using type = typename std::add_lvalue_reference<
282     typename std::add_const<T>::type
283   >::type;
284 };
285 
286 template <typename T, typename TFrom>
287 struct add_cv_reference_from<T &&, TFrom const> {
288   using type = typename std::add_rvalue_reference<
289     typename std::add_const<T>::type
290   >::type;
291 };
292 
293 template <typename T, typename TFrom>
294 struct add_cv_reference_from<T, TFrom volatile> {
295   using type = typename std::add_volatile<T>::type;
296 };
297 
298 template <typename T, typename TFrom>
299 struct add_cv_reference_from<T &, TFrom volatile> {
300   using type = typename std::add_lvalue_reference<
301     typename std::add_volatile<T>::type
302   >::type;
303 };
304 
305 template <typename T, typename TFrom>
306 struct add_cv_reference_from<T &&, TFrom volatile> {
307   using type = typename std::add_rvalue_reference<
308     typename std::add_volatile<T>::type
309   >::type;
310 };
311 
312 template <typename T, typename TFrom>
313 struct add_cv_reference_from<T, TFrom &> {
314   using type = typename std::add_lvalue_reference<
315     typename add_cv_reference_from<T, TFrom>::type
316   >::type;
317 };
318 
319 template <typename T, typename TFrom>
320 struct add_cv_reference_from<T, TFrom &&> {
321   using type = typename std::add_rvalue_reference<
322     typename add_cv_reference_from<T, TFrom>::type
323   >::type;
324 };
325 
326 template <typename T, typename TFrom>
327 using add_cv_reference_from_t = typename add_cv_reference_from<T, TFrom>::type;
328 
329 } // namespace fatal {
330 
331 #endif // FATAL_INCLUDE_fatal_type_qualifier_h
332