1 module optional.bolts;
2 
3 
4 /**
5 https://github.com/aliak00/bolts
6 
7 MIT License
8 
9 Copyright (c) 2018 Ali Akhtarzada
10 
11 Permission is hereby granted, free of charge, to any person obtaining a copy
12 of this software and associated documentation files (the "Software"), to deal
13 in the Software without restriction, including without limitation the rights
14 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
15 copies of the Software, and to permit persons to whom the Software is
16 furnished to do so, subject to the following conditions:
17 
18 The above copyright notice and this permission notice shall be included in all
19 copies or substantial portions of the Software.
20 
21 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
22 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
23 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
24 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
25 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
26 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
27 SOFTWARE.
28 **/
29 
30 /*
31     Returns true if the passed in function is an n-ary function over the next n parameter arguments
32 
33     Parameter arguments can be any compile time entity that can be typed. The first
34 */
35 
36 import core.internal.traits : allSat = allSatisfy;
37 alias allSatisfy = allSat;
38 
39 template isFunctionOver(T...) {
40     import std.meta: Alias;
41     import std.traits: isSomeFunction, Parameters;
42 
43     alias Types = TypesOf!T;
44 
45     static if (Types.length >= 1) {
46         alias DesiredParams = AliasPack!(Types[1 .. $]);
47         static if (isSomeFunction!(Types[0])) {
48             alias ExpectedParams = AliasPack!(Parameters!(Types[0]));
49             static if (DesiredParams.length == ExpectedParams.length) {
50                 static if (DesiredParams.length == 0) {
51                     enum isFunctionOver = true;
52                 } else {
53                     alias Pairs = staticZip!(ExpectedParams, DesiredParams);
54                     // If is(DesiredType : ExpectedType)
55                     enum AreSame(alias Pair) = is(Pair.Unpack[1] : Pair.Unpack[0]);
56                     enum isFunctionOver = allSatisfy!(AreSame, Pairs.Unpack);
57                 }
58             } else {
59                 enum isFunctionOver = false;
60             }
61         } else static if (is(Types[0] == void)) {
62             // We're going to assume the first arg is a function literal ala lambda
63             // And try and see if calling it with the init values of the desired
64             // params works
65             alias F = T[0];
66             alias Val(T) = Alias!(T.init);
67             enum isFunctionOver = __traits(compiles, { F(staticMap!(Val, DesiredParams.Unpack)); });
68         } else {
69             enum isFunctionOver = false;
70         }
71     } else {
72         enum isFunctionOver = false;
73     }
74 }
75 
76 alias AliasSeq(TList...) = TList;
77 
78 /**
79     Same as an AliasSeq that does not auto expand.
80 
81     You can get to the provided compile time sequence (AliasSeq) by accessing the `.Unpack` member.
82     And if you want a recursive expansion there's `UnpackDeep` for that. Also a convenience
83     `.equals!(otherAliasPack)` is provided.
84 
85     See_Also:
86         - https://forum.dlang.org/post/mnobngrzdmqbxomulpts@forum.dlang.org
87 */
88 template AliasPack(T...) {
89     alias Unpack = T;
90     enum length = Unpack.length;
91 
92     private template UnpackDeepImpl(U...) {
93         static if (U.length) {
94             import std.traits: isInstanceOf;
95             static if (isInstanceOf!(AliasPack, U[0])) {
96                 alias Head = UnpackDeepImpl!(U[0].Unpack);
97             } else {
98                 import std.meta: Alias;
99                 alias Head = Alias!(U[0]);
100             }
101             alias UnpackDeepImpl = AliasSeq!(Head, UnpackDeepImpl!(U[1 .. $]));
102         } else {
103             alias UnpackDeepImpl = AliasSeq!();
104         }
105     }
106 
107     alias UnpackDeep = UnpackDeepImpl!T;
108 
109     template equals(U...) {
110         static if (T.length == U.length) {
111             static if (T.length == 0) {
112                 enum equals = true;
113             } else {
114                 enum equals = isSame!(T[0], U[0]) && AliasPack!(T[1 .. $]).equals!(U[1 .. $]);
115             }
116         } else {
117             enum equals = false;
118         }
119     }
120 
121     static if (length > 0) {
122         import std.meta: Alias;
123         alias Head = Alias!(T[0]);
124     } else {
125         alias Head = void;
126     }
127 
128     static if (length > 1) {
129         alias Tail = AliasPack!(T[1 .. $]);
130     } else {
131         alias Tail = AliasPack!();
132     }
133 }
134 
135 
136 /**
137     Zips sequences of `AliasPack`s together into an AliasPack of AliasPacks.
138 
139     See_Also:
140         - https://forum.dlang.org/post/mnobngrzdmqbxomulpts@forum.dlang.org
141 */
142 template staticZip(Seqs...) {
143     import std.traits: isInstanceOf;
144 
145     private enum isPack(alias T) = isInstanceOf!(AliasPack, T);
146 
147     static assert(
148         Seqs.length >= 2 && allSatisfy!(isPack, Seqs),
149         "Must have 2 or more arguments of type AliasPack"
150     );
151 
152     enum len = Seqs[0].length;
153     static foreach (Seq; Seqs[1 .. $]) {
154         static assert(
155             Seq.length == len,
156             "All arguments to staticZip must have the same length"
157         );
158     }
159 
160     alias Head(alias P) = P.Head;
161     alias Tail(alias P) = P.Tail;
162 
163     static if (len == 0) {
164         alias staticZip = AliasPack!();
165     } else {
166         alias staticZip = AliasPack!(
167             AliasPack!(staticMap!(Head, Seqs)),
168             staticZip!(staticMap!(Tail, Seqs)).Unpack
169         );
170     }
171 }
172 
173 
174 /**
175 Evaluates to `AliasSeq!(fun!(args[0]), fun!(args[1]), ..., fun!(args[$ - 1]))`.
176  */
177 template staticMap(alias fun, args...)
178 {
179     version (__staticMap_simplest_but_buggy)
180     {
181         // @@@ BUG @@@
182         // The following straightforward implementation exposes a bug.
183         // See issue https://issues.dlang.org/show_bug.cgi?id=22421 and unittest below.
184         alias staticMap = AliasSeq!();
185         static foreach (arg; args)
186              staticMap = AliasSeq!(staticMap, fun!arg);
187     }
188     else version (__staticMap_simple_but_slow)
189     {
190         // This has a performance bug. Appending to the staticMap seems to be quadratic.
191         alias staticMap = AliasSeq!();
192         static foreach (i; 0 .. args.length)
193             staticMap = AliasSeq!(staticMap, fun!(args[i]));
194     }
195     else // Current best-of-breed implementation imitates quicksort
196     {
197         static if (args.length <= 8)
198         {
199             alias staticMap = AliasSeq!();
200             static foreach (i; 0 .. args.length)
201                 staticMap = AliasSeq!(staticMap, fun!(args[i]));
202         }
203         else
204         {
205             alias staticMap = AliasSeq!(staticMap!(fun, args[0 .. $ / 2]), staticMap!(fun, args[$ / 2 .. $]));
206         }
207     }
208 }
209 
210 
211 /**
212     Returns the types of all values given.
213 
214     $(OD isFunction!T then the typeof the address is taken if possible)
215     $(OD If typeof(T) can be taken it is)
216     $(OD Else it is appended on as is)
217 
218     Returns:
219         AliasSeq of the resulting types
220 */
221 template TypesOf(Values...) {
222     import std.meta: AliasSeq;
223     import std.traits: isExpressions, isFunction;
224     static if (Values.length) {
225         static if (isFunction!(Values[0]) && is(typeof(&Values[0]) F)) {
226             alias T = F;
227         } else static if (is(typeof(Values[0]))) {
228             alias T = typeof(Values[0]);
229         } else {
230             alias T = Values[0];
231         }
232         alias TypesOf = AliasSeq!(T, TypesOf!(Values[1..$]));
233     } else {
234         alias TypesOf = AliasSeq!();
235     }
236 }
237 
238 
239 /**
240     Returns true if a and b are the same thing, or false if not. Both a and b can be types, literals, or symbols.
241 
242     $(LI If both are types: `is(a == b)`)
243     $(LI If both are literals: `a == b`)
244     $(LI Else: `__traits(isSame, a, b)`)
245 */
246 template isSame(ab...) if (ab.length == 2) {
247 
248     private static template expectType(T) {}
249     private static template expectBool(bool b) {}
250 
251     static if (__traits(compiles, expectType!(ab[0]), expectType!(ab[1]))) {
252         enum isSame = is(ab[0] == ab[1]);
253     } else static if (!__traits(compiles, expectType!(ab[0]))
254         && !__traits(compiles, expectType!(ab[1]))
255         &&  __traits(compiles, expectBool!(ab[0] == ab[1]))
256     ) {
257         static if (!__traits(compiles, &ab[0]) || !__traits(compiles, &ab[1]))
258             enum isSame = (ab[0] == ab[1]);
259         else
260             enum isSame = __traits(isSame, ab[0], ab[1]);
261     } else {
262         enum isSame = __traits(isSame, ab[0], ab[1]);
263     }
264 }