1 /*
2  * Copyright (C) 2018 The Android Open Source Project
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 IORAP_COMMON_TYPE_H
18 #define IORAP_COMMON_TYPE_H
19 
20 #include <cstdint>
21 #include <tuple>
22 
23 namespace iorap {
24 namespace introspect {
25 
26 /*
27  * Simple types-as-value abstractions.
28  *
29  * Allow types to be passed as regular function parameters instead of using 'template' type
30  * parameters.
31  *
32  * This enables the following, more concise, pattern:
33  * ----------------------------
34  *
35  * Traditional metaprogramming with template parameters:
36  *
37  *   template <typename ... Args>
38  *   struct get_num_params {
39  *     static constexpr size_t value = sizeof...(Args);
40  *   };
41  *
42  *  typename get_num_params<decltype("hello"), decltype("world")>::value == 2
43  *  typename get_num_params<decltype(int), decltype(int), decltype(int), decltype(int)>::value == 4
44  *
45  * Alternative metaprogramming with values:
46  *
47  *   constexpr auto get_num_params = [](auto&&... val) { return sizeof...(val); };
48  *
49  *   get_num_params("hello", "world") == 2
50  *   get_num_params(0,0,0,0) == 4
51  */
52 
53 /*
54  * A fully instantiated type wrapper.
55  *
56  * basic_type<T> is intended for overloading functions between different basic_types.
57  * type_c<T> is intended for instantiating new type wrappers as a short-hand (and not requiring
58  * typename).
59  *
60  * For basic_type<T> in particular it allows one to overload on basic_type<T> to handle specific
61  * types, and there's no requirement that T be constexpr, be default constructible, and no
62  * template specializations is necessary.
63  *
64  *   void foo(basic_type<int>) {
65  *     printf("int");
66  *   }
67  *
68  *   template <typename T>
69  *   void foo(basic_type<T>) {
70  *     printf("everything else");
71  *   }
72  *
73  * as opposed to this verbosity
74  *
75  *   template <typename T>
76  *   struct foo {
77  *     void operator() {
78  *       printf("everything else");
79  *     }
80  *   };
81  *
82  *   template <>
83  *   struct foo<int> {
84  *     void operator() {
85  *       printf("int");
86  *     }
87  *   };
88  *
89  * OR this super-hack which works in rare situations
90  *
91  *   void foo(int) {
92  *     printf("int");
93  *   }
94  *
95  *   template <typename T>
96  *   void foo(T&&) {
97  *     printf("everything else")
98  *   }
99  *
100  * Note that invoking the last foo(T&&) is particularly challenging. declval<T> fails at compilation
101  * with a static_assert, so a real value has to be constructed that is immediately discarded.
102  */
103 template <typename T>
104 struct basic_type {
105   using type = T;
106 };
107 
108 template <typename T>
109 struct type_impl {
110   struct _ : basic_type<T> { };
111 };
112 
113 template <typename T>
114 using type = basic_type<T>;  // typename type_impl<T>::_;  // subclass of basic_type<T>
115 // TODO: why doesn't using type_impl::_ work with ADL?
116 
117 template <typename T>
118 using type_t = type<T>;
119 
120 template <typename T>
121 constexpr auto type_c = type<T>{};
122 
123 template <auto X>
124 struct value_constant {
125   static constexpr auto value = X;
126 };
127 
128 template <int X>
129 constexpr auto int_c = value_constant<X>{};
130 
131 template <typename T>
132 constexpr bool dependent_false_v = false;
133 
134 // Emit a static_assert(false) if the else branch in an 'if constexpr' is taken.
135 // Needs a type as the first parameter.
136 #define STATIC_FAIL(T, msg) static_assert(::iorap::introspect::dependent_false_v<T>, msg)
137 // Emit a static_assert(false) if an else branch in an 'if constexpr' is taken, used with
138 // (e.g. auto) values instead of types.
139 #define STATIC_FAIL_DT(var, msg) STATIC_FAIL(decltype(var), msg)
140 
141 template <size_t i, typename Tuple, typename F>
for_each_impl(Tuple && t,F && f)142 static constexpr void for_each_impl(Tuple&& t, F&& f) {
143   if constexpr (i == std::tuple_size<std::decay_t<Tuple>>::value) {
144     return;
145   } else {
146     f(std::get<i>(std::forward<Tuple>(t)));
147     for_each_impl<i+1>(std::forward<Tuple>(t), std::forward<F>(f));
148   }
149 }
150 
151 // for each Tuple<a1,a2,...,aN> invoke { f(a1); f(a2); ... ; f(aN); }
152 template <typename Tuple, typename F>
for_each(Tuple && t,F && f)153 static constexpr void for_each(Tuple&& t, F&& f) {
154   return for_each_impl<0u>(std::forward<Tuple>(t), std::forward<F>(f));
155 }
156 
157 // Perfect forwarding for structured binding.
158 //
159 // Example:
160 //   auto&& [a,b] = whatever;
161 //   return aliasing_forward<T>(a);
162 template <typename T, typename U>
decltype(auto)163 constexpr decltype(auto) aliasing_forward(U&& val) noexcept {
164   if constexpr (std::is_lvalue_reference_v<T>) {
165     return val;
166   } else {
167     return std::move(val);
168   }
169 }
170 
171 
172 }  // namespace introspect
173 }  // namespace iorap
174 
175 #endif  // IORAP_COMMON_TYPE_H
176