]> git.proxmox.com Git - ceph.git/blob - ceph/src/jaegertracing/thrift/lib/d/src/thrift/internal/codegen.d
buildsys: switch source download to quincy
[ceph.git] / ceph / src / jaegertracing / thrift / lib / d / src / thrift / internal / codegen.d
1 /*
2 * Licensed to the Apache Software Foundation (ASF) under one
3 * or more contributor license agreements. See the NOTICE file
4 * distributed with this work for additional information
5 * regarding copyright ownership. The ASF licenses this file
6 * to you under the Apache License, Version 2.0 (the
7 * "License"); you may not use this file except in compliance
8 * with the License. You may obtain a copy of the License at
9 *
10 * http://www.apache.org/licenses/LICENSE-2.0
11 *
12 * Unless required by applicable law or agreed to in writing,
13 * software distributed under the License is distributed on an
14 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15 * KIND, either express or implied. See the License for the
16 * specific language governing permissions and limitations
17 * under the License.
18 */
19
20 module thrift.internal.codegen;
21
22 import std.algorithm : canFind;
23 import std.traits : InterfacesTuple, isSomeFunction, isSomeString;
24 import std.typetuple : staticIndexOf, staticMap, NoDuplicates, TypeTuple;
25 import thrift.codegen.base;
26
27 /**
28 * Removes all type qualifiers from T.
29 *
30 * In contrast to std.traits.Unqual, FullyUnqual also removes qualifiers from
31 * array elements (e.g. immutable(byte[]) -> byte[], not immutable(byte)[]),
32 * excluding strings (string isn't reduced to char[]).
33 */
34 template FullyUnqual(T) {
35 static if (is(T _ == const(U), U)) {
36 alias FullyUnqual!U FullyUnqual;
37 } else static if (is(T _ == immutable(U), U)) {
38 alias FullyUnqual!U FullyUnqual;
39 } else static if (is(T _ == shared(U), U)) {
40 alias FullyUnqual!U FullyUnqual;
41 } else static if (is(T _ == U[], U) && !isSomeString!T) {
42 alias FullyUnqual!(U)[] FullyUnqual;
43 } else static if (is(T _ == V[K], K, V)) {
44 alias FullyUnqual!(V)[FullyUnqual!K] FullyUnqual;
45 } else {
46 alias T FullyUnqual;
47 }
48 }
49
50 /**
51 * true if null can be assigned to the passed type, false if not.
52 */
53 template isNullable(T) {
54 enum isNullable = __traits(compiles, { T t = null; });
55 }
56
57 template isStruct(T) {
58 enum isStruct = is(T == struct);
59 }
60
61 template isException(T) {
62 enum isException = is(T : Exception);
63 }
64
65 template isEnum(T) {
66 enum isEnum = is(T == enum);
67 }
68
69 /**
70 * Aliases itself to T.name.
71 */
72 template GetMember(T, string name) {
73 mixin("alias T." ~ name ~ " GetMember;");
74 }
75
76 /**
77 * Aliases itself to typeof(symbol).
78 */
79 template TypeOf(alias symbol) {
80 alias typeof(symbol) TypeOf;
81 }
82
83 /**
84 * Aliases itself to the type of the T member called name.
85 */
86 alias Compose!(TypeOf, GetMember) MemberType;
87
88 /**
89 * Returns the field metadata array for T if any, or an empty array otherwise.
90 */
91 template getFieldMeta(T) if (isStruct!T || isException!T) {
92 static if (is(typeof(T.fieldMeta) == TFieldMeta[])) {
93 enum getFieldMeta = T.fieldMeta;
94 } else {
95 enum TFieldMeta[] getFieldMeta = [];
96 }
97 }
98
99 /**
100 * Merges the field metadata array for D with the passed array.
101 */
102 template mergeFieldMeta(T, alias fieldMetaData = cast(TFieldMeta[])null) {
103 // Note: We don't use getFieldMeta here to avoid bug if it is instantiated
104 // from TIsSetFlags, see comment there.
105 static if (is(typeof(T.fieldMeta) == TFieldMeta[])) {
106 enum mergeFieldMeta = T.fieldMeta ~ fieldMetaData;
107 } else {
108 enum TFieldMeta[] mergeFieldMeta = fieldMetaData;
109 }
110 }
111
112 /**
113 * Returns the field requirement level for T.name.
114 */
115 template memberReq(T, string name, alias fieldMetaData = cast(TFieldMeta[])null) {
116 enum memberReq = memberReqImpl!(T, name, fieldMetaData).result;
117 }
118
119 private {
120 import std.algorithm : find;
121 // DMD @@BUG@@: Missing import leads to failing build without error
122 // message in unittest/debug/thrift/codegen/async_client.
123 import std.array : empty, front;
124
125 template memberReqImpl(T, string name, alias fieldMetaData) {
126 enum meta = find!`a.name == b`(mergeFieldMeta!(T, fieldMetaData), name);
127 static if (meta.empty || meta.front.req == TReq.AUTO) {
128 static if (isNullable!(MemberType!(T, name))) {
129 enum result = TReq.OPTIONAL;
130 } else {
131 enum result = TReq.REQUIRED;
132 }
133 } else {
134 enum result = meta.front.req;
135 }
136 }
137 }
138
139
140 template notIgnored(T, string name, alias fieldMetaData = cast(TFieldMeta[])null) {
141 enum notIgnored = memberReq!(T, name, fieldMetaData) != TReq.IGNORE;
142 }
143
144 /**
145 * Returns the method metadata array for T if any, or an empty array otherwise.
146 */
147 template getMethodMeta(T) if (isService!T) {
148 static if (is(typeof(T.methodMeta) == TMethodMeta[])) {
149 enum getMethodMeta = T.methodMeta;
150 } else {
151 enum TMethodMeta[] getMethodMeta = [];
152 }
153 }
154
155
156 /**
157 * true if T.name is a member variable. Exceptions include methods, static
158 * members, artifacts like package aliases, …
159 */
160 template isValueMember(T, string name) {
161 static if (!is(MemberType!(T, name))) {
162 enum isValueMember = false;
163 } else static if (
164 is(MemberType!(T, name) == void) ||
165 isSomeFunction!(MemberType!(T, name)) ||
166 __traits(compiles, { return mixin("T." ~ name); }())
167 ) {
168 enum isValueMember = false;
169 } else {
170 enum isValueMember = true;
171 }
172 }
173
174 /**
175 * Returns a tuple containing the names of the fields of T, not including
176 * inherited fields. If a member is marked as TReq.IGNORE, it is not included
177 * as well.
178 */
179 template FieldNames(T, alias fieldMetaData = cast(TFieldMeta[])null) {
180 alias StaticFilter!(
181 All!(
182 doesNotReadMembers,
183 PApply!(isValueMember, T),
184 PApply!(notIgnored, T, PApplySkip, fieldMetaData)
185 ),
186 __traits(derivedMembers, T)
187 ) FieldNames;
188 }
189
190 /*
191 * true if the passed member name is not a method generated by the
192 * TStructHelpers template that in its implementations queries the struct
193 * members.
194 *
195 * Kludge used internally to break a cycle caused a DMD forward reference
196 * regression, see THRIFT-2130.
197 */
198 enum doesNotReadMembers(string name) = !["opEquals", "thriftOpEqualsImpl",
199 "toString", "thriftToStringImpl"].canFind(name);
200
201 template derivedMembers(T) {
202 alias TypeTuple!(__traits(derivedMembers, T)) derivedMembers;
203 }
204
205 template AllMemberMethodNames(T) if (isService!T) {
206 alias NoDuplicates!(
207 FilterMethodNames!(
208 T,
209 staticMap!(
210 derivedMembers,
211 TypeTuple!(T, InterfacesTuple!T)
212 )
213 )
214 ) AllMemberMethodNames;
215 }
216
217 template FilterMethodNames(T, MemberNames...) {
218 alias StaticFilter!(
219 CompilesAndTrue!(
220 Compose!(isSomeFunction, TypeOf, PApply!(GetMember, T))
221 ),
222 MemberNames
223 ) FilterMethodNames;
224 }
225
226 /**
227 * Returns a type tuple containing only the elements of T for which the
228 * eponymous template predicate pred is true.
229 *
230 * Example:
231 * ---
232 * alias StaticFilter!(isIntegral, int, string, long, float[]) Filtered;
233 * static assert(is(Filtered == TypeTuple!(int, long)));
234 * ---
235 */
236 template StaticFilter(alias pred, T...) {
237 static if (T.length == 0) {
238 alias TypeTuple!() StaticFilter;
239 } else static if (pred!(T[0])) {
240 alias TypeTuple!(T[0], StaticFilter!(pred, T[1 .. $])) StaticFilter;
241 } else {
242 alias StaticFilter!(pred, T[1 .. $]) StaticFilter;
243 }
244 }
245
246 /**
247 * Binds the first n arguments of a template to a particular value (where n is
248 * the number of arguments passed to PApply).
249 *
250 * The passed arguments are always applied starting from the left. However,
251 * the special PApplySkip marker template can be used to indicate that an
252 * argument should be skipped, so that e.g. the first and third argument
253 * to a template can be fixed, but the second and remaining arguments would
254 * still be left undefined.
255 *
256 * Skipping a number of parameters, but not providing enough arguments to
257 * assign all of them during instantiation of the resulting template is an
258 * error.
259 *
260 * Example:
261 * ---
262 * struct Foo(T, U, V) {}
263 * alias PApply!(Foo, int, long) PartialFoo;
264 * static assert(is(PartialFoo!float == Foo!(int, long, float)));
265 *
266 * alias PApply!(Test, int, PApplySkip, float) SkippedTest;
267 * static assert(is(SkippedTest!long == Test!(int, long, float)));
268 * ---
269 */
270 template PApply(alias Target, T...) {
271 template PApply(U...) {
272 alias Target!(PApplyMergeArgs!(ConfinedTuple!T, U).Result) PApply;
273 }
274 }
275
276 /// Ditto.
277 template PApplySkip() {}
278
279 private template PApplyMergeArgs(alias Preset, Args...) {
280 static if (Preset.length == 0) {
281 alias Args Result;
282 } else {
283 enum nextSkip = staticIndexOf!(PApplySkip, Preset.Tuple);
284 static if (nextSkip == -1) {
285 alias TypeTuple!(Preset.Tuple, Args) Result;
286 } else static if (Args.length == 0) {
287 // Have to use a static if clause instead of putting the condition
288 // directly into the assert to avoid DMD trying to access Args[0]
289 // nevertheless below.
290 static assert(false,
291 "PArgsSkip encountered, but no argument left to bind.");
292 } else {
293 alias TypeTuple!(
294 Preset.Tuple[0 .. nextSkip],
295 Args[0],
296 PApplyMergeArgs!(
297 ConfinedTuple!(Preset.Tuple[nextSkip + 1 .. $]),
298 Args[1 .. $]
299 ).Result
300 ) Result;
301 }
302 }
303 }
304
305 unittest {
306 struct Test(T, U, V) {}
307 alias PApply!(Test, int, long) PartialTest;
308 static assert(is(PartialTest!float == Test!(int, long, float)));
309
310 alias PApply!(Test, int, PApplySkip, float) SkippedTest;
311 static assert(is(SkippedTest!long == Test!(int, long, float)));
312
313 alias PApply!(Test, int, PApplySkip, PApplySkip) TwoSkipped;
314 static assert(!__traits(compiles, TwoSkipped!long));
315 }
316
317
318 /**
319 * Composes a number of templates. The result is a template equivalent to
320 * all the passed templates evaluated from right to left, akin to the
321 * mathematical function composition notation: Instantiating Compose!(A, B, C)
322 * is the same as instantiating A!(B!(C!(…))).
323 *
324 * This is especially useful for creating a template to use with staticMap/
325 * StaticFilter, as demonstrated below.
326 *
327 * Example:
328 * ---
329 * template AllMethodNames(T) {
330 * alias StaticFilter!(
331 * CompilesAndTrue!(
332 * Compose!(isSomeFunction, TypeOf, PApply!(GetMember, T))
333 * ),
334 * __traits(allMembers, T)
335 * ) AllMethodNames;
336 * }
337 *
338 * pragma(msg, AllMethodNames!Object);
339 * ---
340 */
341 template Compose(T...) {
342 static if (T.length == 0) {
343 template Compose(U...) {
344 alias U Compose;
345 }
346 } else {
347 template Compose(U...) {
348 alias Instantiate!(T[0], Instantiate!(.Compose!(T[1 .. $]), U)) Compose;
349 }
350 }
351 }
352
353 /**
354 * Instantiates the given template with the given list of parameters.
355 *
356 * Used to work around syntactic limiations of D with regard to instantiating
357 * a template from a type tuple (e.g. T[0]!(...) is not valid) or a template
358 * returning another template (e.g. Foo!(Bar)!(Baz) is not allowed).
359 */
360 template Instantiate(alias Template, Params...) {
361 alias Template!Params Instantiate;
362 }
363
364 /**
365 * Combines several template predicates using logical AND, i.e. instantiating
366 * All!(a, b, c) with parameters P for some templates a, b, c is equivalent to
367 * a!P && b!P && c!P.
368 *
369 * The templates are evaluated from left to right, aborting evaluation in a
370 * shurt-cut manner if a false result is encountered, in which case the latter
371 * instantiations do not need to compile.
372 */
373 template All(T...) {
374 static if (T.length == 0) {
375 template All(U...) {
376 enum All = true;
377 }
378 } else {
379 template All(U...) {
380 static if (Instantiate!(T[0], U)) {
381 alias Instantiate!(.All!(T[1 .. $]), U) All;
382 } else {
383 enum All = false;
384 }
385 }
386 }
387 }
388
389 /**
390 * Combines several template predicates using logical OR, i.e. instantiating
391 * Any!(a, b, c) with parameters P for some templates a, b, c is equivalent to
392 * a!P || b!P || c!P.
393 *
394 * The templates are evaluated from left to right, aborting evaluation in a
395 * shurt-cut manner if a true result is encountered, in which case the latter
396 * instantiations do not need to compile.
397 */
398 template Any(T...) {
399 static if (T.length == 0) {
400 template Any(U...) {
401 enum Any = false;
402 }
403 } else {
404 template Any(U...) {
405 static if (Instantiate!(T[0], U)) {
406 enum Any = true;
407 } else {
408 alias Instantiate!(.Any!(T[1 .. $]), U) Any;
409 }
410 }
411 }
412 }
413
414 template ConfinedTuple(T...) {
415 alias T Tuple;
416 enum length = T.length;
417 }
418
419 /*
420 * foreach (Item; Items) {
421 * List = Operator!(Item, List);
422 * }
423 * where Items is a ConfinedTuple and List is a type tuple.
424 */
425 template ForAllWithList(alias Items, alias Operator, List...) if (
426 is(typeof(Items.length) : size_t)
427 ){
428 static if (Items.length == 0) {
429 alias List ForAllWithList;
430 } else {
431 alias .ForAllWithList!(
432 ConfinedTuple!(Items.Tuple[1 .. $]),
433 Operator,
434 Operator!(Items.Tuple[0], List)
435 ) ForAllWithList;
436 }
437 }
438
439 /**
440 * Wraps the passed template predicate so it returns true if it compiles and
441 * evaluates to true, false it it doesn't compile or evaluates to false.
442 */
443 template CompilesAndTrue(alias T) {
444 template CompilesAndTrue(U...) {
445 static if (is(typeof(T!U) : bool)) {
446 enum bool CompilesAndTrue = T!U;
447 } else {
448 enum bool CompilesAndTrue = false;
449 }
450 }
451 }