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 }