1 module boilerplate.util; 2 3 import std.meta; 4 import std.traits; 5 6 enum needToDup(T) = isArray!(T) && !DeepConst!(T); 7 8 enum DeepConst(T) = __traits(compiles, (const T x) { T y = x; }); 9 10 @("needToDup correctly handles common types") 11 @nogc nothrow pure @safe unittest 12 { 13 int integerField; 14 int[] integerArrayField; 15 16 static assert(!needToDup!(typeof(integerField))); 17 static assert(needToDup!(typeof(integerArrayField))); 18 } 19 20 @("needToDup correctly handles const types") 21 @nogc nothrow pure @safe unittest 22 { 23 const(int)[] constIntegerArrayField; 24 string stringField; 25 26 static assert(!needToDup!(typeof(constIntegerArrayField))); 27 static assert(!needToDup!(typeof(stringField))); 28 } 29 30 @("doesn't add write-only properties to NormalMembers") 31 unittest 32 { 33 struct Test 34 { 35 @property void foo(int i) { } 36 mixin GenNormalMemberTuple; 37 static assert(is(NormalMemberTuple == AliasSeq!()), 38 "write-only properties should not appear in NormalMembers because they have no type" 39 ); 40 } 41 } 42 43 @("doesn't add read properties to NormalMembers if includeFunctions is false") 44 unittest 45 { 46 struct Test 47 { 48 @property int foo() { return 0; } 49 int bar() { return 0; } 50 mixin GenNormalMemberTuple; 51 static assert(is(NormalMemberTuple == AliasSeq!()), 52 "read properties should not appear in NormalMembers if includeFunctions is false" 53 ); 54 } 55 } 56 57 /** 58 * Generate AliasSeq of "normal" members - ie. no templates, no alias, no enum, only fields 59 * (and functions if includeFunctions is true). 60 */ 61 mixin template GenNormalMemberTuple(bool includeFunctions = false) 62 { 63 import boilerplate.util : GenNormalMembersCheck, GenNormalMembersImpl; 64 import std.meta : AliasSeq; 65 66 mixin(`alias NormalMemberTuple = ` ~ GenNormalMembersImpl([__traits(derivedMembers, typeof(this))], 67 mixin(GenNormalMembersCheck([__traits(derivedMembers, typeof(this))], includeFunctions))) ~ `;`); 68 } 69 70 string GenNormalMembersCheck(string[] members, bool includeFunctions) 71 { 72 import std.format : format; 73 import std.string : join; 74 75 string code = "["; 76 foreach (i, member; members) 77 { 78 if (i > 0) 79 { 80 code ~= ", "; // don't .map.join because this is compile performance critical code 81 } 82 83 if (member != "this") 84 { 85 string check = `__traits(compiles, &typeof(this).init.` ~ member ~ `)` 86 ~ ` && __traits(compiles, typeof(typeof(this).init.` ~ member ~ `))`; 87 88 if (!includeFunctions) 89 { 90 check ~= ` && !is(typeof(typeof(this).` ~ member ~ `) == function)` 91 ~ ` && !is(typeof(&typeof(this).init.` ~ member ~ `) == delegate)`; 92 } 93 94 code ~= check; 95 } 96 else 97 { 98 code ~= `false`; 99 } 100 } 101 code ~= "]"; 102 103 return code; 104 } 105 106 string GenNormalMembersImpl(string[] members, bool[] compiles) 107 { 108 import std.string : join; 109 110 string[] names; 111 112 foreach (i, member; members) 113 { 114 if (member != "this" && compiles[i]) 115 { 116 names ~= "\"" ~ member ~ "\""; 117 } 118 } 119 120 return "AliasSeq!(" ~ names.join(", ") ~ ")"; 121 } 122 123 template getOverloadLike(Aggregate, string Name, Type) 124 { 125 alias Overloads = AliasSeq!(__traits(getOverloads, Aggregate, Name)); 126 enum FunctionMatchesType(alias Fun) = is(typeof(Fun) == Type); 127 alias MatchingOverloads = Filter!(FunctionMatchesType, Overloads); 128 129 static assert(MatchingOverloads.length == 1); 130 131 alias getOverloadLike = MatchingOverloads[0]; 132 } 133 134 template udaIndex(alias attr, attributes...) 135 { 136 enum udaIndex = helper(); 137 138 ptrdiff_t helper() 139 { 140 if (!__ctfe) 141 { 142 return 0; 143 } 144 static if (attributes.length) 145 { 146 foreach (i, attrib; attributes) 147 { 148 enum lastAttrib = i == attributes.length - 1; 149 150 static if (__traits(isTemplate, attr)) 151 { 152 static if (is(attrib: attr!Args, Args...)) 153 { 154 return i; 155 } 156 else static if (lastAttrib) 157 { 158 return -1; 159 } 160 } 161 else static if (__traits(compiles, is(typeof(attrib) == typeof(attr)) && attrib == attr)) 162 { 163 static if (is(typeof(attrib) == typeof(attr)) && attrib == attr) 164 { 165 return i; 166 } 167 else static if (lastAttrib) 168 { 169 return -1; 170 } 171 } 172 else static if (__traits(compiles, is(attrib == attr))) 173 { 174 static if (is(attrib == attr)) 175 { 176 return i; 177 } 178 else static if (lastAttrib) 179 { 180 return -1; 181 } 182 } 183 else static if (lastAttrib) 184 { 185 return -1; 186 } 187 } 188 } 189 else 190 { 191 return -1; 192 } 193 } 194 } 195 196 string isStatic(string field) 197 { 198 return `__traits(getOverloads, typeof(this), "` ~ field ~ `").length == 0` 199 ~ ` && __traits(compiles, &this.` ~ field ~ `)`; 200 } 201 202 string isUnsafe(string field) 203 { 204 return isStatic(field) ~ ` && !__traits(compiles, () @safe { return this.` ~ field ~ `; })`; 205 } 206 207 // a stable, simple O(n) sort optimal for a small number of sort keys 208 T[] bucketSort(T)(T[] inputArray, int delegate(T) rankfn) 209 { 210 import std.algorithm : joiner; 211 import std.range : array; 212 213 T[][] buckets; 214 215 foreach (element; inputArray) 216 { 217 auto rank = rankfn(element); 218 219 if (rank >= buckets.length) 220 { 221 buckets.length = rank + 1; 222 } 223 224 buckets[rank] ~= element; 225 } 226 227 return buckets.joiner.array; 228 } 229 230 void sinkWrite(T)(scope void delegate(const(char)[]) sink, ref bool comma, string fmt, T arg) 231 { 232 static if (__traits(compiles, { import config.string : toString; })) 233 { 234 import config.string : customToString = toString; 235 } 236 else 237 { 238 void customToString(T)() 239 if (false) 240 { 241 } 242 } 243 244 import std.datetime : SysTime; 245 import std.format : formattedWrite; 246 import std.typecons : Nullable; 247 248 alias PlainT = typeof(cast() arg); 249 250 enum isNullable = is(PlainT: Nullable!Args, Args...); 251 252 static if (isNullable) 253 { 254 if (!arg.isNull) 255 { 256 sinkWrite(sink, comma, fmt, arg.get); 257 } 258 return; 259 } 260 else 261 { 262 static if (is(PlainT == SysTime)) 263 { 264 if (arg == SysTime.init) // crashes on toString 265 { 266 return; 267 } 268 } 269 270 if (comma) 271 { 272 sink(", "); 273 } 274 275 comma = true; 276 277 static if (__traits(compiles, customToString(arg, sink))) 278 { 279 struct TypeWrapper 280 { 281 void toString(scope void delegate(const(char)[]) sink) const 282 { 283 customToString(arg, sink); 284 } 285 } 286 sink.formattedWrite(fmt, TypeWrapper()); 287 } 288 else 289 { 290 sink.formattedWrite(fmt, arg); 291 } 292 } 293 }