1 /** Traits used by containers. 2 * 3 * TODO: add `isUnorderedContainer` and `isUnorderedRange` traits and used to 4 * forbid hash algorithms to operate on unordered containers (such as 5 * `flat_hashmap` and `flat_hashmap`) and their ranges. 6 */ 7 module nxt.container.traits; 8 9 public import nxt.gc_traits; 10 11 @safe: 12 13 /** True if the last reference of a `T` in the scope of its lifetime should be 14 * passed by move instead of by copy either because 15 * 16 * - it cannot be copied or 17 * - because it has an elaborate constructor or elaborate destructor that can be 18 * elided via a move. 19 * 20 * This excludes arrays and classes. 21 * 22 * Note that `__traits(isPOD, T)` implies 23 * `core.internal.traits.hasElaborateAssign!T || 24 * core.internal.traits.hasElaborateDestructor!T`. 25 * 26 * See_Also: `std.traits.hasElaborateMove`. 27 */ 28 enum bool shouldMove(T) = !__traits(isPOD, T); 29 30 /// 31 @safe pure unittest 32 { 33 static assert(!shouldMove!(char)); 34 static assert(!shouldMove!(int)); 35 static assert(!shouldMove!(string)); 36 static assert(!shouldMove!(int[])); 37 38 class C {} 39 static assert(!shouldMove!(C)); 40 41 struct POD {} 42 static assert(!shouldMove!(POD)); 43 44 struct SomeUncopyable { @disable this(this); } 45 static assert(shouldMove!(SomeUncopyable)); 46 47 struct WithDtor { ~this() nothrow {} } 48 static assert(shouldMove!(WithDtor)); 49 } 50 51 // TODO: this can be simplified for faster compilation 52 template ContainerElementType(ContainerType, ElementType) 53 { 54 import std.traits : isMutable, hasIndirections, PointerTarget, isPointer, 55 Unqual; 56 57 template ET(bool isConst, T) 58 { 59 static if (isPointer!ElementType) 60 { 61 enum PointerIsConst = is(ElementType == const); 62 enum PointerIsImmutable = is(ElementType == immutable); 63 enum DataIsConst = is(PointerTarget!ElementType == const); 64 enum DataIsImmutable = is(PointerTarget!ElementType == immutable); 65 static if (isConst) 66 { 67 static if (PointerIsConst) 68 alias ET = ElementType; 69 else static if (PointerIsImmutable) 70 alias ET = ElementType; 71 else 72 alias ET = const(PointerTarget!ElementType)*; 73 } 74 else 75 { 76 static assert(DataIsImmutable, 77 "An immutable container cannot reference const or mutable data"); 78 static if (PointerIsConst) 79 alias ET = immutable(PointerTarget!ElementType)*; 80 else 81 alias ET = ElementType; 82 } 83 } 84 else 85 { 86 static if (isConst) 87 { 88 static if (is(ElementType == immutable)) 89 alias ET = ElementType; 90 else 91 alias ET = const(Unqual!ElementType); 92 } 93 else 94 alias ET = immutable(Unqual!ElementType); 95 } 96 } 97 98 static if (isMutable!ContainerType) 99 alias ContainerElementType = ElementType; 100 else 101 { 102 static if (hasIndirections!ElementType) 103 alias ContainerElementType = ET!(is(ContainerType == const), ElementType); 104 else 105 alias ContainerElementType = ElementType; 106 } 107 } 108 109 /// Returns: `true` iff `T` is a template instance, `false` otherwise. 110 private template isTemplateInstance(T) 111 { 112 import std.traits : TemplateOf; 113 enum isTemplateInstance = is(typeof(TemplateOf!(T))); 114 } 115 116 /** Is `true` iff `T` is a set like container. */ 117 template isSet(T) 118 { 119 import std.range.primitives : hasLength; 120 enum isSet = (__traits(hasMember, T, "insert") && // TODO: assert O(1) 121 __traits(hasMember, T, "remove") && // TODO: assert O(1) 122 __traits(compiles, { auto _ = T.init.byElement; })); 123 } 124 125 /** Is `true` iff `T` is a set like container with elements of type `E`. */ 126 template isSetOf(T, E) 127 { 128 import std.range.primitives : hasLength; 129 enum isSetOf = (is(typeof(T.init.insert(E.init))) && // TODO: assert O(1) 130 is(typeof(T.init.remove(E.init))) && // TODO: assert O(1) 131 __traits(compiles, { auto _ = T.init.byElement; })); 132 } 133 134 /** Allocate an array of `T`-elements of length `length` using `allocator`. 135 */ 136 T[] makeInitZeroArray(T, alias allocator)(const size_t length) @trusted 137 { 138 version(none) // TODO: activate 139 { 140 // See: https://github.com/dlang/phobos/pull/6411 141 import std.experimental.allocator.gc_allocator : GCAllocator; 142 static if (__traits(hasMember, GCAllocator, "allocateZeroed")) 143 { 144 static assert(0, "Use std.experimental.allocator.package.make!(T) instead because it makes use of allocateZeroed."); 145 } 146 } 147 immutable byteCount = T.sizeof * length; 148 /* when possible prefer call to calloc before malloc+memset: 149 * https://stackoverflow.com/questions/2688466/why-mallocmemset-is-slower-than-calloc */ 150 static if (__traits(hasMember, allocator, "allocateZeroed")) 151 { 152 version(D_Coverage) {} else pragma(inline, true); 153 return cast(typeof(return))allocator.allocateZeroed(byteCount); 154 } 155 else 156 { 157 auto array = cast(typeof(return))allocator.allocate(byteCount); 158 import core.stdc.string : memset; 159 memset(array.ptr, 0, byteCount); 160 return array; 161 } 162 } 163 164 /** Variant of `hasElaborateDestructor` that also checks for destructor when `S` 165 * is a `class`. 166 * 167 * See_Also: https://github.com/dlang/phobos/pull/4119 168 */ 169 template hasElaborateDestructorNew(S) 170 { 171 static if (is(S == struct) || 172 is(S == class)) // check also class 173 { 174 static if (__traits(hasMember, S, "__dtor")) 175 enum bool hasElaborateDestructorNew = true; 176 else 177 { 178 import std.traits : FieldTypeTuple; 179 import std.meta : anySatisfy; 180 enum hasElaborateDestructorNew = anySatisfy!(.hasElaborateDestructorNew, FieldTypeTuple!S); 181 } 182 } 183 else 184 { 185 import std.traits : isStaticArray; 186 static if (isStaticArray!S && S.length) 187 enum bool hasElaborateDestructorNew = hasElaborateDestructorNew!(typeof(S.init[0])); 188 else 189 enum bool hasElaborateDestructorNew = false; 190 } 191 } 192 193 /** Is `true` iff `T` is repesented as a memory address. */ 194 template isAddress(T) 195 { 196 static if (is(T == class)) 197 enum isAddress = true; // a class is memory-wise just a pointer 198 else 199 { 200 import std.traits : isPointer; 201 enum isAddress = isPointer!T; 202 } 203 } 204 205 /// 206 @safe pure nothrow @nogc unittest 207 { 208 static assert( isAddress!(int*)); 209 static assert(!isAddress!(int)); 210 211 class C {} 212 static assert( isAddress!(C)); 213 214 struct S {} 215 static assert(!isAddress!(S)); 216 static assert( isAddress!(S*)); 217 }