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
10 * http://www.apache.org/licenses/LICENSE-2.0
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
20 module thrift.internal.codegen;
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;
28 * Removes all type qualifiers from T.
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[]).
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;
51 * true if null can be assigned to the passed type, false if not.
53 template isNullable(T) {
54 enum isNullable = __traits(compiles, { T t = null; });
57 template isStruct(T) {
58 enum isStruct = is(T == struct);
61 template isException(T) {
62 enum isException = is(T : Exception);
66 enum isEnum = is(T == enum);
70 * Aliases itself to T.name.
72 template GetMember(T, string name) {
73 mixin("alias T." ~ name ~ " GetMember;");
77 * Aliases itself to typeof(symbol).
79 template TypeOf(alias symbol) {
80 alias typeof(symbol) TypeOf;
84 * Aliases itself to the type of the T member called name.
86 alias Compose!(TypeOf, GetMember) MemberType;
89 * Returns the field metadata array for T if any, or an empty array otherwise.
91 template getFieldMeta(T) if (isStruct!T || isException!T) {
92 static if (is(typeof(T.fieldMeta) == TFieldMeta[])) {
93 enum getFieldMeta = T.fieldMeta;
95 enum TFieldMeta[] getFieldMeta = [];
100 * Merges the field metadata array for D with the passed array.
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;
108 enum TFieldMeta[] mergeFieldMeta = fieldMetaData;
113 * Returns the field requirement level for T.name.
115 template memberReq(T, string name, alias fieldMetaData = cast(TFieldMeta[])null) {
116 enum memberReq = memberReqImpl!(T, name, fieldMetaData).result;
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;
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;
131 enum result = TReq.REQUIRED;
134 enum result = meta.front.req;
140 template notIgnored(T, string name, alias fieldMetaData = cast(TFieldMeta[])null) {
141 enum notIgnored = memberReq!(T, name, fieldMetaData) != TReq.IGNORE;
145 * Returns the method metadata array for T if any, or an empty array otherwise.
147 template getMethodMeta(T) if (isService!T) {
148 static if (is(typeof(T.methodMeta) == TMethodMeta[])) {
149 enum getMethodMeta = T.methodMeta;
151 enum TMethodMeta[] getMethodMeta = [];
157 * true if T.name is a member variable. Exceptions include methods, static
158 * members, artifacts like package aliases, …
160 template isValueMember(T, string name) {
161 static if (!is(MemberType!(T, name))) {
162 enum isValueMember = false;
164 is(MemberType!(T, name) == void) ||
165 isSomeFunction!(MemberType!(T, name)) ||
166 __traits(compiles, { return mixin("T." ~ name); }())
168 enum isValueMember = false;
170 enum isValueMember = true;
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
179 template FieldNames(T, alias fieldMetaData = cast(TFieldMeta[])null) {
183 PApply!(isValueMember, T),
184 PApply!(notIgnored, T, PApplySkip, fieldMetaData)
186 __traits(derivedMembers, T)
191 * true if the passed member name is not a method generated by the
192 * TStructHelpers template that in its implementations queries the struct
195 * Kludge used internally to break a cycle caused a DMD forward reference
196 * regression, see THRIFT-2130.
198 enum doesNotReadMembers(string name) = !["opEquals", "thriftOpEqualsImpl",
199 "toString", "thriftToStringImpl"].canFind(name);
201 template derivedMembers(T) {
202 alias TypeTuple!(__traits(derivedMembers, T)) derivedMembers;
205 template AllMemberMethodNames(T) if (isService!T) {
211 TypeTuple!(T, InterfacesTuple!T)
214 ) AllMemberMethodNames;
217 template FilterMethodNames(T, MemberNames...) {
220 Compose!(isSomeFunction, TypeOf, PApply!(GetMember, T))
227 * Returns a type tuple containing only the elements of T for which the
228 * eponymous template predicate pred is true.
232 * alias StaticFilter!(isIntegral, int, string, long, float[]) Filtered;
233 * static assert(is(Filtered == TypeTuple!(int, long)));
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;
242 alias StaticFilter!(pred, T[1 .. $]) StaticFilter;
247 * Binds the first n arguments of a template to a particular value (where n is
248 * the number of arguments passed to PApply).
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.
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
262 * struct Foo(T, U, V) {}
263 * alias PApply!(Foo, int, long) PartialFoo;
264 * static assert(is(PartialFoo!float == Foo!(int, long, float)));
266 * alias PApply!(Test, int, PApplySkip, float) SkippedTest;
267 * static assert(is(SkippedTest!long == Test!(int, long, float)));
270 template PApply(alias Target, T...) {
271 template PApply(U...) {
272 alias Target!(PApplyMergeArgs!(ConfinedTuple!T, U).Result) PApply;
277 template PApplySkip() {}
279 private template PApplyMergeArgs(alias Preset, Args...) {
280 static if (Preset.length == 0) {
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.
291 "PArgsSkip encountered, but no argument left to bind.");
294 Preset.Tuple[0 .. nextSkip],
297 ConfinedTuple!(Preset.Tuple[nextSkip + 1 .. $]),
306 struct Test(T, U, V) {}
307 alias PApply!(Test, int, long) PartialTest;
308 static assert(is(PartialTest!float == Test!(int, long, float)));
310 alias PApply!(Test, int, PApplySkip, float) SkippedTest;
311 static assert(is(SkippedTest!long == Test!(int, long, float)));
313 alias PApply!(Test, int, PApplySkip, PApplySkip) TwoSkipped;
314 static assert(!__traits(compiles, TwoSkipped!long));
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!(…))).
324 * This is especially useful for creating a template to use with staticMap/
325 * StaticFilter, as demonstrated below.
329 * template AllMethodNames(T) {
330 * alias StaticFilter!(
332 * Compose!(isSomeFunction, TypeOf, PApply!(GetMember, T))
334 * __traits(allMembers, T)
338 * pragma(msg, AllMethodNames!Object);
341 template Compose(T...) {
342 static if (T.length == 0) {
343 template Compose(U...) {
347 template Compose(U...) {
348 alias Instantiate!(T[0], Instantiate!(.Compose!(T[1 .. $]), U)) Compose;
354 * Instantiates the given template with the given list of parameters.
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).
360 template Instantiate(alias Template, Params...) {
361 alias Template!Params Instantiate;
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
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.
374 static if (T.length == 0) {
380 static if (Instantiate!(T[0], U)) {
381 alias Instantiate!(.All!(T[1 .. $]), U) All;
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
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.
399 static if (T.length == 0) {
405 static if (Instantiate!(T[0], U)) {
408 alias Instantiate!(.Any!(T[1 .. $]), U) Any;
414 template ConfinedTuple(T...) {
416 enum length = T.length;
420 * foreach (Item; Items) {
421 * List = Operator!(Item, List);
423 * where Items is a ConfinedTuple and List is a type tuple.
425 template ForAllWithList(alias Items, alias Operator, List...) if (
426 is(typeof(Items.length) : size_t)
428 static if (Items.length == 0) {
429 alias List ForAllWithList;
431 alias .ForAllWithList!(
432 ConfinedTuple!(Items.Tuple[1 .. $]),
434 Operator!(Items.Tuple[0], List)
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.
443 template CompilesAndTrue(alias T) {
444 template CompilesAndTrue(U...) {
445 static if (is(typeof(T!U) : bool)) {
446 enum bool CompilesAndTrue = T!U;
448 enum bool CompilesAndTrue = false;