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: Template!Args, alias Template = attr, 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 // a stable, simple O(n) sort optimal for a small number of sort keys 203 T[] bucketSort(T)(T[] inputArray, int delegate(T) rankfn) 204 { 205 import std.algorithm : joiner; 206 import std.range : array; 207 208 T[][] buckets; 209 210 foreach (element; inputArray) 211 { 212 auto rank = rankfn(element); 213 214 if (rank >= buckets.length) 215 { 216 buckets.length = rank + 1; 217 } 218 219 buckets[rank] ~= element; 220 } 221 222 return buckets.joiner.array; 223 } 224 225 void sinkWrite(T)(scope void delegate(const(char)[]) sink, ref bool comma, string fmt, T arg) 226 { 227 static if (__traits(compiles, { import config.string : toString; })) 228 { 229 import config.string : customToString = toString; 230 } 231 else 232 { 233 void customToString(T)() 234 if (false) 235 { 236 } 237 } 238 239 import std.datetime : SysTime; 240 import std.format : formattedWrite; 241 import std.typecons : Nullable; 242 243 alias PlainT = typeof(cast() arg); 244 245 enum isNullable = is(PlainT: Template!Args, alias Template = Nullable, Args...); 246 247 static if (isNullable) 248 { 249 if (!arg.isNull) 250 { 251 sinkWrite(sink, comma, fmt, arg.get); 252 } 253 return; 254 } 255 else 256 { 257 static if (is(PlainT == SysTime)) 258 { 259 if (arg == SysTime.init) // crashes on toString 260 { 261 return; 262 } 263 } 264 265 if (comma) 266 { 267 sink(", "); 268 } 269 270 comma = true; 271 272 static if (__traits(compiles, customToString(arg, sink))) 273 { 274 struct TypeWrapper 275 { 276 void toString(scope void delegate(const(char)[]) sink) const 277 { 278 customToString(arg, sink); 279 } 280 } 281 sink.formattedWrite(fmt, TypeWrapper()); 282 } 283 else 284 { 285 sink.formattedWrite(fmt, arg); 286 } 287 } 288 }