1 /*
2 * Copyright 2014 Google Inc. All rights reserved.
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 FRUIT_META_BASICS_H
18 #define FRUIT_META_BASICS_H
19
20 #include <functional>
21
22 namespace fruit {
23 namespace impl {
24 namespace meta {
25
26 template <typename T>
27 struct Type {
28 using type = T;
29 };
30
31 template <bool b>
32 struct Bool {
33 static constexpr bool value = b;
34 };
35
36 template <int n>
37 struct Int {
38 static constexpr int value = n;
39 };
40
41 // This was added to workaround a bug in MSVC 2017 15.5, that crashes when expanding Indexes::value... in some cases
42 // (where Indexes is a template parameter pack of Int<...> types).
43 // TODO: Remove this once MSVC 2017 is fixed and the fix has been out for some time.
44 template <typename N>
getIntValue()45 constexpr int getIntValue() {
46 return N::value;
47 }
48
49 // None is used as "the nullptr of metaprogramming". E.g. when a function has no meaningful value to
50 // return, it can return None instead.
51 struct None {};
52
53 struct If {};
54
55 // PropagateError(E, X) evaluates E then X. The result is X's result, but if E returns an error,
56 // that's the result instead.
57 struct PropagateError {};
58
59 // Used to propagate an ErrorTag::apply<ErrorArgs...> up the instantiation chain, but without instantiating it right
60 // away, to allow shorter error stacktraces.
61 // Instantiating ErrorTag::apply<ErrorArgs...> must result in a static_assert error.
62 template <typename ErrorTag, typename... ErrorArgs>
63 struct Error {};
64
65 // Use as Catch(ExpressionThatMightThrow, ErrorTag, Handler)
66 // Handler(Error<ErrorTag, ErrorArgs...>) is called if ExpressionThatMightThrow throws ErrorTag.
67 struct Catch {};
68
69 // Use as CatchAll(ExpressionThatMightThrow, Handler)
70 // Handler(Error<ErrorTag, ErrorArgs...>) is called if ExpressionThatMightThrow throws any error.
71 struct CatchAll {};
72
73 // Call(F, Args...) is equivalent to F(Args...) in a metaexpression, except that Call(F, Args...)
74 // also works when F is a metaexpression.
75 struct Call {
76 template <typename F, typename... Args>
77 struct apply : public F::template apply<Args...> {};
78 };
79
80 // UnwrapType<Type<T>> is T.
81 template <typename WrappedType>
82 using UnwrapType = typename WrappedType::type;
83
84 // MSVC 14 has trouble specializing alias templates using expanded pack elements.
85 // This is a known issue:
86 // https://stackoverflow.com/questions/43411542/metaprogramming-failed-to-specialize-alias-template
87 // The workaround is just to use a struct directly.
88 // typename TypeUnwrapper<Type<T>>::type is T.
89 template <typename WrappedType>
90 struct TypeUnwrapper {
91 using type = UnwrapType<WrappedType>;
92 };
93
94 // Logical And with short-circuit evaluation.
95 struct And {
96 template <typename... MetaExprs>
97 struct apply {
98 using type = Bool<true>;
99 };
100
101 template <typename MetaExpr>
102 struct apply<MetaExpr> {
103 using type = MetaExpr;
104 };
105
106 template <typename MetaExpr, typename MetaExpr2>
107 struct apply<MetaExpr, MetaExpr2> {
108 using type = If(MetaExpr, MetaExpr2, Bool<false>);
109 };
110
111 template <typename MetaExpr, typename MetaExpr2, typename... MetaExprs>
112 struct apply<MetaExpr, MetaExpr2, MetaExprs...> {
113 using type = If(MetaExpr, If(MetaExpr2, And(MetaExprs...), Bool<false>), Bool<false>);
114 };
115 };
116
117 // Logical Or with short-circuit evaluation.
118 struct Or {
119 template <typename... MetaExprs>
120 struct apply {
121 using type = Bool<false>;
122 };
123
124 template <typename MetaExpr>
125 struct apply<MetaExpr> {
126 using type = MetaExpr;
127 };
128
129 template <typename MetaExpr, typename MetaExpr2>
130 struct apply<MetaExpr, MetaExpr2> {
131 using type = If(MetaExpr, Bool<true>, MetaExpr2);
132 };
133
134 template <typename MetaExpr, typename MetaExpr2, typename... MetaExprs>
135 struct apply<MetaExpr, MetaExpr2, MetaExprs...> {
136 using type = If(MetaExpr, Bool<true>, If(MetaExpr2, Bool<true>, Or(MetaExprs...)));
137 };
138 };
139
140 // Call(Call(DeferArgs(F), Args...), MoreArgs...)
141 //
142 // is equivalent to:
143 // Result = F(Args..., MoreArgs...)
144 //
145 // Note that you can't write:
146 // DeferArgs(F)(Args...)(MoreArgs...)
147 //
148 // Because Call must be used to call metafunctions that are metaexpressions.
149 struct DeferArgs {
150 template <typename F>
151 struct apply {
152 struct type {
153 template <typename... Args>
154 struct apply {
155 struct type {
156 template <typename... MoreArgs>
157 struct apply {
158 using type = F(Args..., MoreArgs...);
159 };
160 };
161 };
162 };
163 };
164 };
165
166 // Call(PartialCall(F, Args...), MoreArgs...)
167 //
168 // is equivalent to:
169 // Result = F(Args..., MoreArgs...)
170 //
171 // Note that you can't write:
172 // PartialCall(F, Args...)(MoreArgs...)
173 //
174 // Because Call must be used to call metafunctions that are metaexpressions.
175 struct PartialCall {
176 template <typename F, typename... Args>
177 struct apply {
178 struct type {
179 template <typename... MoreArgs>
180 struct apply {
181 using type = F(Args..., MoreArgs...);
182 };
183 };
184 };
185 };
186
187 struct IsSame {
188 template <typename T, typename U>
189 struct apply {
190 using type = Bool<false>;
191 };
192
193 template <typename T>
194 struct apply<T, T> {
195 using type = Bool<true>;
196 };
197 };
198
199 struct Not {
200 template <typename B>
201 struct apply {
202 using type = Bool<!B::value>;
203 };
204 };
205
206 struct IsNone {
207 template <typename T>
208 struct apply {
209 using type = Bool<false>;
210 };
211 };
212
213 template <>
214 struct IsNone::apply<None> {
215 using type = Bool<true>;
216 };
217
218 template <typename T>
219 using Id = T;
220
221 struct Identity {
222 template <typename T>
223 struct apply {
224 using type = T;
225 };
226 };
227
228 template <typename T>
229 struct DebugTypeHelper {
230 static_assert(sizeof(T*) * 0 != 0, "");
231 using type = T;
232 };
233
234 template <typename T>
235 using DebugType = typename DebugTypeHelper<T>::type;
236
237 } // namespace meta
238 } // namespace impl
239 } // namespace fruit
240
241 #endif // FRUIT_META_BASICS_H
242