• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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