1 /// Basic functions used in the tagion project 2 module tagion.basic.basic; 3 4 private import std.string : join; 5 private import std.traits; 6 import std.meta : AliasSeq; 7 8 enum this_dot = "this."; 9 import std.conv; 10 11 /++ 12 Returns: 13 a immuatble do 14 +/ 15 immutable(BUF) buf_idup(BUF)(immutable(Buffer) buffer) { 16 pragma(msg, "fixme(cbr): looks redundent"); 17 return cast(BUF)(buffer.idup); 18 } 19 20 template suffix(string name, size_t index) { 21 static if (index is 0) { 22 alias suffix = name; 23 } 24 else static if (name[index - 1]!is '.') { 25 alias suffix = suffix!(name, index - 1); 26 } 27 else { 28 enum cut_name = name[index .. $]; 29 alias suffix = cut_name; 30 } 31 } 32 33 /++ 34 Template function returns the suffux name after the last '.' 35 +/ 36 template basename(alias K) { 37 static if (is(K == string)) { 38 enum name = K; 39 } 40 else { 41 enum name = K.stringof; 42 } 43 enum basename = suffix!(name, name.length); 44 } 45 46 enum NameOf(alias nameType) = __traits(identifier, nameType); 47 48 /++ 49 Returns: 50 function name of the current function 51 +/ 52 mixin template FUNCTION_NAME() { 53 import tagion.basic.basic : basename; 54 55 enum __FUNCTION_NAME__ = basename!(__FUNCTION__)[0 .. $ - 1]; 56 } 57 58 /// 59 unittest { 60 enum name_another = "another"; 61 import std.algorithm.searching : countUntil; 62 63 struct Something { 64 mixin("int " ~ name_another ~ ";"); 65 void check() { 66 // Check that basename removes (this.) from the scope name space 67 static assert(this.another.stringof.countUntil('.') == this_dot.countUntil('.')); 68 static assert(basename!(this.another) == name_another); 69 } 70 } 71 72 Something something; 73 // check that basename work in global scope (not this.) 74 static assert(something.stringof.countUntil('.') == -1); 75 static assert(basename!(something.another) == name_another); 76 } 77 /++ 78 Builds and enum string out of a string array 79 +/ 80 template EnumText(string name, string[] list, bool first = true) { 81 static if (first) { 82 enum begin = "enum " ~ name ~ "{"; 83 alias EnumText = EnumText!(begin, list, false); 84 } 85 else static if (list.length > 0) { 86 enum k = list[0]; 87 enum code = name ~ k ~ " = " ~ '"' ~ k ~ '"' ~ ','; 88 alias EnumText = EnumText!(code, list[1 .. $], false); 89 } 90 else { 91 enum code = name ~ "}"; 92 alias EnumText = code; 93 } 94 } 95 96 /// 97 unittest { 98 enum list = ["red", "green", "blue"]; 99 mixin(EnumText!("Colour", list)); 100 static assert(Colour.red == list[0]); 101 static assert(Colour.green == list[1]); 102 static assert(Colour.blue == list[2]); 103 104 } 105 106 /++ 107 Calculates log2 108 Returns: 109 log2(n) 110 +/ 111 @trusted int log2(ulong n) { 112 if (n == 0) { 113 return -1; 114 } 115 import core.bitop : bsr; 116 117 return bsr(n); 118 } 119 120 /// 121 unittest { 122 // Undefined value returns -1 123 assert(log2(0) == -1); 124 assert(log2(17) == 4); 125 assert(log2(177) == 7); 126 assert(log2(0x1000_000_000) == 36); 127 128 } 129 130 /++ 131 Generate a temporary file name 132 +/ 133 @trusted 134 string tempfile() { 135 import std.file; 136 import std.path; 137 import std.random; 138 import std.range; 139 140 auto rnd = Random(unpredictableSeed); 141 return buildPath(tempDir, generate!(() => uniform('A', 'Z', rnd)).takeExactly(20).array); 142 } 143 144 @safe 145 void forceRemove(const(string) filename) { 146 import std.file : exists, remove; 147 148 if (filename.exists) { 149 filename.remove; 150 } 151 } 152 153 /++ 154 Returns: 155 true if the type T is one of types in the list TList 156 +/ 157 template isOneOf(T, TList...) { 158 static if (TList.length == 0) { 159 enum isOneOf = false; 160 } 161 else static if (is(T == TList[0])) { 162 enum isOneOf = true; 163 } 164 else { 165 alias isOneOf = isOneOf!(T, TList[1 .. $]); 166 } 167 } 168 169 /// 170 static unittest { 171 import std.meta; 172 173 alias Seq = AliasSeq!(long, int, ubyte); 174 static assert(isOneOf!(int, Seq)); 175 static assert(!isOneOf!(double, Seq)); 176 } 177 178 /++ 179 Finds the type in the TList which T can be typecast to 180 Returns: 181 void if not type is found 182 +/ 183 template CastTo(T, TList...) { 184 static if (TList.length is 0) { 185 alias CastTo = void; 186 } 187 else { 188 alias castT = TList[0]; 189 static if (is(T : castT)) { 190 alias CastTo = castT; 191 } 192 else { 193 alias CastTo = CastTo!(T, TList[1 .. $]); 194 } 195 } 196 } 197 198 /// 199 static unittest { 200 static assert(is(void == CastTo!(string, AliasSeq!(int, long, double)))); 201 static assert(is(double == CastTo!(float, AliasSeq!(int, long, double)))); 202 static assert(is(string == CastTo!(string, AliasSeq!(uint, string)))); 203 static assert(is(uint == CastTo!(ushort, AliasSeq!(uint, string)))); 204 static assert(is(uint == CastTo!(int, AliasSeq!(string, uint)))); 205 static assert(is(const(uint) == CastTo!(inout(uint), AliasSeq!(const(uint), const(string))))); 206 } 207 208 import std.typecons : Tuple; 209 210 alias FileNames = Tuple!(string, "tempdir", string, "filename", string, "fullpath"); 211 const(FileNames) fileId(T)(string ext, string prefix = null) @safe { 212 import std.array : join; 213 import std.file; 214 import std.path; 215 import std.process : environment, thisProcessID; 216 217 //import std.traits; 218 FileNames names; 219 names.tempdir = tempDir.buildPath(environment.get("USER")); 220 names.filename = setExtension([prefix, thisProcessID.to!string, T.stringof].join("_"), ext); 221 names.fullpath = buildPath(names.tempdir, names.filename); 222 names.tempdir.exists || names.tempdir.mkdir; 223 return names; 224 } 225 226 template EnumContinuousSequency(Enum) if (is(Enum == enum)) { 227 template Sequency(EList...) { 228 static if (EList.length is 1) { 229 enum Sequency = true; 230 } 231 else static if (EList[0] + 1 is EList[1]) { 232 enum Sequency = Sequency!(EList[1 .. $]); 233 } 234 else { 235 enum Sequency = false; 236 } 237 } 238 239 enum EnumContinuousSequency = Sequency!(EnumMembers!Enum); 240 } 241 242 /// 243 static unittest { 244 enum Count { 245 zero, 246 one, 247 two, 248 three 249 } 250 251 static assert(EnumContinuousSequency!Count); 252 253 enum NoCount { 254 zero, 255 one, 256 three = 3 257 } 258 259 static assert(!EnumContinuousSequency!NoCount); 260 261 enum OffsetCount { 262 one = 1, 263 two, 264 three 265 } 266 267 static assert(EnumContinuousSequency!OffsetCount); 268 } 269 270 /// isEqual is the same as `is()` function which can be used in template filters 271 enum isEqual(T1, T2) = is(T1 == T2); 272 //enum isUnqualEqual(T1, T2) = is(Unqual!T1 == T2); 273 274 unittest { 275 import std.meta : ApplyLeft, ApplyRight; 276 import std.traits : Unqual; 277 278 static assert(isEqual!(int, int)); 279 static assert(!isEqual!(int, long)); 280 alias U = immutable(int); 281 static assert(isEqual!(int, Unqual!U)); 282 alias Left = ApplyLeft!(isEqual, int); 283 static assert(Left!(Unqual!U)); 284 } 285 286 /// Calling any system functions. 287 template assumeTrusted(alias F) { 288 import std.traits; 289 290 static assert(isUnsafe!F); 291 292 auto assumeTrusted(Args...)(Args args) @trusted { 293 return F(args); 294 } 295 } 296 297 /// 298 @safe 299 unittest { 300 auto bar(int b) @system { 301 return b + 1; 302 } 303 304 const b = assumeTrusted!bar(5); 305 assert(b == 6); 306 307 // applicable to 0-ary function 308 static auto foo() @system { 309 return 3; 310 } 311 312 const a = assumeTrusted!foo; 313 assert(a == 3); 314 315 // // It can be used for alias 316 alias trustedBar = assumeTrusted!bar; 317 alias trustedFoo = assumeTrusted!foo; 318 // assert(is(typeof(trustedFoo) == function)); 319 320 import core.stdc.stdlib; 321 322 auto ptr = assumeTrusted!malloc(100); 323 assert(ptr !is null); 324 ptr.assumeTrusted!free; 325 326 ptr = assumeTrusted!calloc(10, 100); 327 ptr.assumeTrusted!free; 328 329 alias lambda = assumeTrusted!((int a) @system => a * 3); 330 331 assert(lambda(42) == 3 * 42); 332 333 { 334 import std.concurrency; 335 336 static void task() @safe { 337 const result = 2 * assumeTrusted!(receiveOnly!int); 338 assumeTrusted!({ ownerTid.send(result); }); 339 alias trusted_owner = assumeTrusted!(ownerTid); 340 alias trusted_send = assumeTrusted!(send!(string)); 341 trusted_send(trusted_owner, "Hello"); 342 } 343 344 auto tid = assumeTrusted!({ return spawn(&task); }); 345 assumeTrusted!({ send(tid, 21); }); 346 assert(assumeTrusted!(receiveOnly!(const(int))) == 21 * 2); 347 assert(assumeTrusted!(receiveOnly!(string)) == "Hello"); 348 } 349 } 350 351 protected template _staticSearchIndexOf(int index, alias find, L...) { 352 import std.meta : staticIndexOf; 353 354 static if (isType!find) { 355 enum _staticSearchIndexOf = staticIndexOf!(find, L); 356 } 357 else { 358 static if (L.length is index) { 359 enum _staticSearchIndexOf = -1; 360 } 361 else { 362 enum found = find!(L[index]); 363 static if (found) { 364 enum _staticSearchIndexOf = index; 365 } 366 else { 367 enum _staticSearchIndexOf = _staticSearchIndexOf!(index + 1, find, L); 368 } 369 } 370 } 371 } 372 373 /** 374 This template finds the index of find in the AliasSeq L. 375 If find is a type it works the same as traits.staticIndexOf, 376 but if func is a templeate function it will use this function as a filter 377 Returns: 378 First index where find has been found 379 If nothing has been found the template returns -1 380 */ 381 382 template staticSearchIndexOf(alias find, L...) { 383 enum staticSearchIndexOf = _staticSearchIndexOf!(0, find, L); 384 } 385 386 static unittest { 387 import std.traits : isFloatingPoint, isIntegral; 388 389 alias seq = AliasSeq!(string, int, long, char); 390 static assert(staticSearchIndexOf!(long, seq) is 2); 391 static assert(staticSearchIndexOf!(isIntegral, seq) is 1); 392 static assert(staticSearchIndexOf!(isFloatingPoint, seq) is -1); 393 } 394 395 enum unitdata = "unitdata"; 396 397 /** 398 * Used in unitttest local the path package/unitdata/filename 399 * Params: 400 * filename = name of the unitdata file 401 * file = defailt location of the module 402 * Returns: 403 * unittest data filename 404 */ 405 string unitfile(string filename, string file = __FILE__) @safe { 406 import std.path; 407 408 return buildPath(file.dirName, unitdata, filename); 409 } 410 411 /** 412 * Mangle of a callable symbol 413 * Params: 414 * T = callable symbol 415 * Returns: 416 * mangle of callable T 417 */ 418 template mangleFunc(alias T) if (isCallable!T) { 419 import core.demangle : mangle; 420 421 alias mangleFunc = mangle!(FunctionTypeOf!(T)); 422 } 423 424 bool isinit(T)(T x) pure nothrow @safe { 425 return x is T.init; 426 } 427 428 @safe 429 unittest { 430 class C { 431 } 432 433 C c; 434 assert(c.isinit); 435 c = new C; 436 assert(!c.isinit); 437 static struct S { 438 int x; 439 } 440 441 S s; 442 assert(s.isinit); 443 s = S(42); 444 assert(!s.isinit); 445 } 446 447 auto trusted(alias func, Args...)(auto ref Args args) @trusted { 448 return func(args); 449 } 450 451 auto scopedTrusted(alias func, Args...)(auto ref scope Args args) @trusted { 452 return func(args); 453 } 454 455 @safe 456 unittest { 457 import std.traits; 458 459 @system 460 int mul(int a, int b) { 461 return a * b; 462 } 463 464 static assert(!isSafe!mul); 465 assert(trusted!mul(1, 2) == 2); 466 assert(scopedTrusted!mul(1, 2) == 2); 467 }