Make containers work with non-copyable elements

It is the first step. The containers can be at least created with
non-copyable structs without compilation errors now.
Fix #69.
This commit is contained in:
2018-11-24 06:25:55 +01:00
parent 884dc30953
commit 49d7452b33
8 changed files with 215 additions and 89 deletions

View File

@@ -22,6 +22,7 @@ import tanya.memory;
import tanya.meta.trait;
import tanya.meta.transform;
import tanya.range;
version (unittest) import tanya.test.stub;
/**
* Random-access range for the $(D_PSYMBOL Array).
@@ -293,7 +294,9 @@ struct Array(T)
* init = Initial value to fill the array with.
* allocator = Allocator.
*/
this(size_t len, T init, shared Allocator allocator = defaultAllocator)
this()(size_t len,
auto ref T init,
shared Allocator allocator = defaultAllocator)
{
this(allocator);
reserve(len);
@@ -348,15 +351,19 @@ struct Array(T)
(() @trusted => allocator.deallocate(slice(capacity)))();
}
/**
* Copies the array.
*/
this(this)
static if (isCopyable!T)
{
auto buf = slice(this.length);
this.length_ = capacity_ = 0;
this.data = null;
insertBack(buf);
this(this)
{
auto buf = slice(this.length);
this.length_ = capacity_ = 0;
this.data = null;
insertBack(buf);
}
}
else
{
@disable this(this);
}
/**
@@ -1027,7 +1034,7 @@ struct Array(T)
}
/// ditto
Range opIndexAssign(Range value)
Range opIndexAssign()(Range value)
{
return opSliceAssign(value, 0, length);
}
@@ -1321,7 +1328,7 @@ struct Array(T)
}
/// ditto
Range opSliceAssign(Range value, size_t i, size_t j) @trusted
Range opSliceAssign()(Range value, size_t i, size_t j) @trusted
in
{
assert(i <= j);
@@ -1575,15 +1582,10 @@ struct Array(T)
assert(v7[].equal(v8[]));
}
// Destructor can destroy empty arrays
@nogc nothrow pure @safe unittest
{
static struct SWithDtor
{
~this() @nogc nothrow pure @safe
{
}
}
auto v = Array!SWithDtor(); // Destructor can destroy empty arrays.
auto v = Array!WithDtor();
}
@nogc nothrow pure @safe unittest
@@ -1594,7 +1596,6 @@ struct Array(T)
A a1, a2;
auto v1 = Array!A([a1, a2]);
// Issue 232: https://issues.caraus.io/issues/232.
static assert(is(Array!(A*)));
}
@@ -1679,3 +1680,9 @@ struct Array(T)
}
func(array);
}
// Can have non-copyable elements
@nogc nothrow pure @safe unittest
{
static assert(is(Array!NonCopyable));
}

View File

@@ -20,6 +20,7 @@ import tanya.memory.allocator;
import tanya.meta.trait;
import tanya.meta.transform;
import tanya.typecons;
version (unittest) import tanya.test.stub;
package struct SEntry(T)
{
@@ -59,12 +60,12 @@ package struct Bucket(K, V = void)
}
BucketStatus status = BucketStatus.empty;
this(ref K key)
this()(ref K key)
{
this.key = key;
}
@property void key(ref K key)
@property void key()(ref K key)
{
this.key() = key;
this.status = BucketStatus.used;
@@ -170,7 +171,7 @@ package struct HashArray(alias hasher, K, V = void)
.swap(this.length, data.length);
}
void opAssign(ref typeof(this) that)
void opAssign()(ref typeof(this) that)
{
this.array = that.array;
this.lengthIndex = that.lengthIndex;
@@ -326,3 +327,13 @@ package struct HashArray(alias hasher, K, V = void)
return false;
}
}
// Can be constructed with non-copyable key/values
@nogc nothrow pure @safe unittest
{
static assert(is(Bucket!NonCopyable));
static assert(is(Bucket!(NonCopyable, NonCopyable)));
static assert(is(HashArray!((ref NonCopyable) => 0U, NonCopyable)));
static assert(is(HashArray!((ref NonCopyable) => 0U, NonCopyable, NonCopyable)));
}

View File

@@ -22,6 +22,7 @@ import tanya.memory;
import tanya.meta.trait;
import tanya.meta.transform;
import tanya.range.primitive;
version (unittest) import tanya.test.stub;
/**
* Bidirectional range whose element type is a tuple of a key and the
@@ -68,7 +69,7 @@ struct Range(T)
return this.dataRange.empty();
}
@property void popFront()
void popFront()
in
{
assert(!empty);
@@ -87,7 +88,7 @@ struct Range(T)
while (!empty && dataRange.front.status != BucketStatus.used);
}
@property void popBack()
void popBack()
in
{
assert(!empty);
@@ -759,7 +760,7 @@ if (isHashFunction!(hasher, Key))
*
* Returns: The number of the inserted elements with a unique key.
*/
size_t insert(ref KeyValue keyValue)
size_t insert()(ref KeyValue keyValue)
{
auto e = ((ref v) @trusted => &this.data.insert(v))(keyValue.key);
size_t inserted;
@@ -773,7 +774,7 @@ if (isHashFunction!(hasher, Key))
}
/// ditto
size_t insert(KeyValue keyValue)
size_t insert()(KeyValue keyValue)
{
auto e = ((ref v) @trusted => &this.data.insert(v))(keyValue.key);
size_t inserted;
@@ -1197,3 +1198,16 @@ if (isHashFunction!(hasher, Key))
static assert(is(typeof("asdf" in HashTable!(String, int)())));
static assert(is(typeof(HashTable!(String, int)()["asdf"])));
}
// Can have non-copyable keys and elements
@nogc nothrow pure @safe unittest
{
@NonCopyable @Hashable
static struct S
{
mixin StructStub;
}
static assert(is(HashTable!(S, int)));
static assert(is(HashTable!(int, S)));
static assert(is(HashTable!(S, S)));
}

View File

@@ -23,6 +23,7 @@ import tanya.meta.trait;
import tanya.meta.transform;
import tanya.range.array;
import tanya.range.primitive;
version (unittest) import tanya.test.stub;
/**
* Forward range for the $(D_PSYMBOL SList).
@@ -155,8 +156,9 @@ struct SList(T)
* init = Initial value to fill the list with.
* allocator = Allocator.
*/
this(size_t len, T init, shared Allocator allocator = defaultAllocator)
@trusted
this()(size_t len,
auto ref T init,
shared Allocator allocator = defaultAllocator)
{
this(allocator);
if (len == 0)
@@ -182,7 +184,18 @@ struct SList(T)
/// ditto
this(size_t len, shared Allocator allocator = defaultAllocator)
{
this(len, T.init, allocator);
this(allocator);
if (len == 0)
{
return;
}
Entry* next = this.head = allocator.make!Entry();
foreach (i; 1 .. len)
{
next.next = allocator.make!Entry();
next = next.next;
}
}
///
@@ -271,14 +284,18 @@ struct SList(T)
clear();
}
/**
* Copies the list.
*/
this(this)
static if (isCopyable!T)
{
auto list = typeof(this)(this[], this.allocator);
this.head = list.head;
list.head = null;
this(this)
{
auto list = typeof(this)(this[], this.allocator);
this.head = list.head;
list.head = null;
}
}
else
{
@disable this(this);
}
///
@@ -512,7 +529,7 @@ struct SList(T)
}
/// ditto
size_t insertBefore(Range r, ref T el) @trusted
size_t insertBefore()(Range r, ref T el) @trusted
in
{
assert(checkRangeBelonging(r));
@@ -1120,8 +1137,9 @@ struct DList(T)
* init = Initial value to fill the list with.
* allocator = Allocator.
*/
this(size_t len, T init, shared Allocator allocator = defaultAllocator)
@trusted
this()(size_t len,
auto ref T init,
shared Allocator allocator = defaultAllocator)
{
this(allocator);
if (len == 0)
@@ -1150,7 +1168,20 @@ struct DList(T)
/// ditto
this(size_t len, shared Allocator allocator = defaultAllocator)
{
this(len, T.init, allocator);
this(allocator);
if (len == 0)
{
return;
}
Entry* next = this.head = allocator.make!Entry();
foreach (i; 1 .. len)
{
next.next = allocator.make!Entry();
next.next.prev = next;
next = next.next;
}
this.tail = next;
}
///
@@ -1242,15 +1273,19 @@ struct DList(T)
clear();
}
/**
* Copies the list.
*/
this(this)
static if (isCopyable!T)
{
auto list = typeof(this)(this[], this.allocator);
this.head = list.head;
this.tail = list.tail;
list.head = list .tail = null;
this(this)
{
auto list = typeof(this)(this[], this.allocator);
this.head = list.head;
this.tail = list.tail;
list.head = list .tail = null;
}
}
else
{
@disable this(this);
}
///
@@ -1641,7 +1676,7 @@ struct DList(T)
}
/// ditto
size_t insertBefore(Range r, ref T el) @trusted
size_t insertBefore()(Range r, ref T el) @trusted
in
{
assert(checkRangeBelonging(r));
@@ -1758,7 +1793,7 @@ struct DList(T)
}
/// ditto
size_t insertAfter(Range r, ref T el) @trusted
size_t insertAfter()(Range r, ref T el) @trusted
in
{
assert(checkRangeBelonging(r));
@@ -2355,3 +2390,10 @@ struct DList(T)
assert(!l1.remove(r).empty);
assert(l1 == l2);
}
// Can have non-copyable elements
@nogc nothrow pure @safe unittest
{
static assert(is(SList!NonCopyable));
static assert(is(DList!NonCopyable));
}

View File

@@ -22,6 +22,7 @@ import tanya.memory;
import tanya.meta.trait;
import tanya.meta.transform;
import tanya.range.primitive;
version (unittest) import tanya.test.stub;
/**
* Bidirectional range that iterates over the $(D_PSYMBOL Set)'s values.
@@ -67,7 +68,7 @@ struct Range(T)
return this.dataRange.empty();
}
@property void popFront()
void popFront()
in
{
assert(!empty);
@@ -86,7 +87,7 @@ struct Range(T)
while (!empty && dataRange.front.status != BucketStatus.used);
}
@property void popBack()
void popBack()
in
{
assert(!empty);
@@ -459,7 +460,7 @@ if (isHashFunction!(hasher, T))
*
* Returns: Amount of new elements inserted.
*/
size_t insert(ref T value)
size_t insert()(ref T value)
{
auto e = ((ref v) @trusted => &this.data.insert(v))(value);
if (e.status != BucketStatus.used)
@@ -470,7 +471,7 @@ if (isHashFunction!(hasher, T))
return 0;
}
size_t insert(T value)
size_t insert()(T value)
{
auto e = ((ref v) @trusted => &this.data.insert(v))(value);
if (e.status != BucketStatus.used)
@@ -773,3 +774,14 @@ if (isHashFunction!(hasher, T))
{
static assert(is(Set!(int, (const ref x) => cast(size_t) x)));
}
// Can have non-copyable elements
@nogc nothrow pure @safe unittest
{
@NonCopyable @Hashable
static struct S
{
mixin StructStub;
}
static assert(is(Set!S));
}