Make Unique.get and RefCounted.get return inout
Also revert the renaming of Scoped to Unique. And rename the whole module to memory.smartref.
This commit is contained in:
parent
b723d763c8
commit
70e96c62b3
847
source/tanya/memory/smartref.d
Normal file
847
source/tanya/memory/smartref.d
Normal file
@ -0,0 +1,847 @@
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
/**
|
||||
* Smart pointers.
|
||||
*
|
||||
* Copyright: Eugene Wissner 2016-2017.
|
||||
* License: $(LINK2 https://www.mozilla.org/en-US/MPL/2.0/,
|
||||
* Mozilla Public License, v. 2.0).
|
||||
* Authors: $(LINK2 mailto:info@caraus.de, Eugene Wissner)
|
||||
*/
|
||||
module tanya.memory.smartref;
|
||||
|
||||
import core.exception;
|
||||
import std.algorithm.comparison;
|
||||
import std.algorithm.mutation;
|
||||
import std.conv;
|
||||
import std.range;
|
||||
import std.traits;
|
||||
import tanya.memory;
|
||||
|
||||
package template Payload(T)
|
||||
{
|
||||
static if (is(T == class) || is(T == interface) || isArray!T)
|
||||
{
|
||||
alias Payload = T;
|
||||
}
|
||||
else
|
||||
{
|
||||
alias Payload = T*;
|
||||
}
|
||||
}
|
||||
|
||||
package final class RefCountedStore(T)
|
||||
{
|
||||
T payload;
|
||||
size_t counter = 1;
|
||||
|
||||
size_t opUnary(string op)()
|
||||
if (op == "--" || op == "++")
|
||||
in
|
||||
{
|
||||
assert(counter > 0);
|
||||
}
|
||||
body
|
||||
{
|
||||
mixin("return " ~ op ~ "counter;");
|
||||
}
|
||||
|
||||
int opCmp(const size_t counter)
|
||||
{
|
||||
if (this.counter > counter)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
else if (this.counter < counter)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
else
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
int opEquals(const size_t counter)
|
||||
{
|
||||
return this.counter == counter;
|
||||
}
|
||||
}
|
||||
|
||||
package void separateDeleter(T)(RefCountedStore!T storage,
|
||||
shared Allocator allocator)
|
||||
{
|
||||
allocator.dispose(storage.payload);
|
||||
allocator.dispose(storage);
|
||||
}
|
||||
|
||||
package void unifiedDeleter(T)(RefCountedStore!T storage,
|
||||
shared Allocator allocator)
|
||||
{
|
||||
auto ptr1 = finalize(storage);
|
||||
auto ptr2 = finalize(storage.payload);
|
||||
allocator.deallocate(ptr1.ptr[0 .. ptr1.length + ptr2.length]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Reference-counted object containing a $(D_PARAM T) value as payload.
|
||||
* $(D_PSYMBOL RefCounted) keeps track of all references of an object, and
|
||||
* when the reference count goes down to zero, frees the underlying store.
|
||||
*
|
||||
* Params:
|
||||
* T = Type of the reference-counted value.
|
||||
*/
|
||||
struct RefCounted(T)
|
||||
{
|
||||
private alias Storage = RefCountedStore!(Payload!T);
|
||||
|
||||
private Storage storage;
|
||||
private void function(Storage storage,
|
||||
shared Allocator allocator) @nogc deleter;
|
||||
|
||||
invariant
|
||||
{
|
||||
assert(storage is null || allocator_ !is null);
|
||||
assert(storage is null || deleter !is null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Takes ownership over $(D_PARAM value), setting the counter to 1.
|
||||
* $(D_PARAM value) may be a pointer, an object or a dynamic array.
|
||||
*
|
||||
* Params:
|
||||
* value = Value whose ownership is taken over.
|
||||
* allocator = Allocator used to destroy the $(D_PARAM value) and to
|
||||
* allocate/deallocate internal storage.
|
||||
*
|
||||
* Precondition: $(D_INLINECODE allocator !is null)
|
||||
*/
|
||||
this()(auto ref Payload!T value,
|
||||
shared Allocator allocator = defaultAllocator)
|
||||
{
|
||||
this(allocator);
|
||||
this.storage = allocator.make!Storage();
|
||||
this.deleter = &separateDeleter!(Payload!T);
|
||||
|
||||
move(value, this.storage.payload);
|
||||
static if (__traits(isRef, value))
|
||||
{
|
||||
value = null;
|
||||
}
|
||||
}
|
||||
|
||||
/// Ditto.
|
||||
this(shared Allocator allocator)
|
||||
in
|
||||
{
|
||||
assert(allocator !is null);
|
||||
}
|
||||
body
|
||||
{
|
||||
this.allocator_ = allocator;
|
||||
}
|
||||
|
||||
/**
|
||||
* Increases the reference counter by one.
|
||||
*/
|
||||
this(this)
|
||||
{
|
||||
if (count != 0)
|
||||
{
|
||||
++this.storage;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Decreases the reference counter by one.
|
||||
*
|
||||
* If the counter reaches 0, destroys the owned object.
|
||||
*/
|
||||
~this()
|
||||
{
|
||||
if (this.storage !is null && !(this.storage.counter && --this.storage))
|
||||
{
|
||||
deleter(this.storage, allocator);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Takes ownership over $(D_PARAM rhs). Initializes this
|
||||
* $(D_PSYMBOL RefCounted) if needed.
|
||||
*
|
||||
* If it is the last reference of the previously owned object,
|
||||
* it will be destroyed.
|
||||
*
|
||||
* To reset $(D_PSYMBOL RefCounted) assign $(D_KEYWORD null).
|
||||
*
|
||||
* If the allocator wasn't set before, $(D_PSYMBOL defaultAllocator) will
|
||||
* be used. If you need a different allocator, create a new
|
||||
* $(D_PSYMBOL RefCounted) and assign it.
|
||||
*
|
||||
* Params:
|
||||
* rhs = New object.
|
||||
*
|
||||
* Returns: $(D_KEYWORD this).
|
||||
*/
|
||||
ref typeof(this) opAssign()(auto ref Payload!T rhs)
|
||||
{
|
||||
if (this.storage is null)
|
||||
{
|
||||
this.storage = allocator.make!Storage();
|
||||
this.deleter = &separateDeleter!(Payload!T);
|
||||
}
|
||||
else if (this.storage > 1)
|
||||
{
|
||||
--this.storage;
|
||||
this.storage = allocator.make!Storage();
|
||||
this.deleter = &separateDeleter!(Payload!T);
|
||||
}
|
||||
else
|
||||
{
|
||||
finalize(this.storage.payload);
|
||||
this.storage.payload = Payload!T.init;
|
||||
}
|
||||
move(rhs, this.storage.payload);
|
||||
return this;
|
||||
}
|
||||
|
||||
/// Ditto.
|
||||
ref typeof(this) opAssign(typeof(null))
|
||||
{
|
||||
if (this.storage is null)
|
||||
{
|
||||
return this;
|
||||
}
|
||||
else if (this.storage > 1)
|
||||
{
|
||||
--this.storage;
|
||||
}
|
||||
else
|
||||
{
|
||||
deleter(this.storage, allocator);
|
||||
}
|
||||
this.storage = null;
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
/// Ditto.
|
||||
ref typeof(this) opAssign(typeof(this) rhs)
|
||||
{
|
||||
swap(this.allocator_, rhs.allocator_);
|
||||
swap(this.storage, rhs.storage);
|
||||
swap(this.deleter, rhs.deleter);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns: Reference to the owned object.
|
||||
*
|
||||
* Precondition: $(D_INLINECODE cound > 0).
|
||||
*/
|
||||
inout(Payload!T) get() inout pure nothrow @safe @nogc
|
||||
in
|
||||
{
|
||||
assert(count > 0, "Attempted to access an uninitialized reference.");
|
||||
}
|
||||
body
|
||||
{
|
||||
return storage.payload;
|
||||
}
|
||||
|
||||
version (D_Ddoc)
|
||||
{
|
||||
/**
|
||||
* Params:
|
||||
* op = Operation.
|
||||
*
|
||||
* Dereferences the pointer. It is defined only for pointers, not for
|
||||
* reference types like classes, that can be accessed directly.
|
||||
*
|
||||
* Returns: Reference to the pointed value.
|
||||
*/
|
||||
ref inout(T) opUnary(string op)() inout
|
||||
if (op == "*");
|
||||
}
|
||||
else static if (isPointer!(Payload!T))
|
||||
{
|
||||
ref inout(T) opUnary(string op)() inout
|
||||
if (op == "*")
|
||||
{
|
||||
return *storage.payload;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns: Whether this $(D_PSYMBOL RefCounted) already has an internal
|
||||
* storage.
|
||||
*/
|
||||
@property bool isInitialized() const
|
||||
{
|
||||
return storage !is null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns: The number of $(D_PSYMBOL RefCounted) instances that share
|
||||
* ownership over the same pointer (including $(D_KEYWORD this)).
|
||||
* If this $(D_PSYMBOL RefCounted) isn't initialized, returns `0`.
|
||||
*/
|
||||
@property size_t count() const
|
||||
{
|
||||
return storage is null ? 0 : storage.counter;
|
||||
}
|
||||
|
||||
mixin DefaultAllocator;
|
||||
alias get this;
|
||||
}
|
||||
|
||||
///
|
||||
unittest
|
||||
{
|
||||
auto rc = RefCounted!int(defaultAllocator.make!int(5), defaultAllocator);
|
||||
auto val = rc.get();
|
||||
|
||||
*val = 8;
|
||||
assert(*rc.storage.payload == 8);
|
||||
|
||||
val = null;
|
||||
assert(rc.storage.payload !is null);
|
||||
assert(*rc.storage.payload == 8);
|
||||
|
||||
*rc = 9;
|
||||
assert(*rc.storage.payload == 9);
|
||||
}
|
||||
|
||||
version (unittest)
|
||||
{
|
||||
private class A
|
||||
{
|
||||
uint *destroyed;
|
||||
|
||||
this(ref uint destroyed) @nogc
|
||||
{
|
||||
this.destroyed = &destroyed;
|
||||
}
|
||||
|
||||
~this() @nogc
|
||||
{
|
||||
++(*destroyed);
|
||||
}
|
||||
}
|
||||
|
||||
private struct B
|
||||
{
|
||||
int prop;
|
||||
@disable this();
|
||||
this(int param1) @nogc
|
||||
{
|
||||
prop = param1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private @nogc unittest
|
||||
{
|
||||
uint destroyed;
|
||||
auto a = defaultAllocator.make!A(destroyed);
|
||||
|
||||
assert(destroyed == 0);
|
||||
{
|
||||
auto rc = RefCounted!A(a, defaultAllocator);
|
||||
assert(rc.count == 1);
|
||||
|
||||
void func(RefCounted!A rc) @nogc
|
||||
{
|
||||
assert(rc.count == 2);
|
||||
}
|
||||
func(rc);
|
||||
|
||||
assert(rc.count == 1);
|
||||
}
|
||||
assert(destroyed == 1);
|
||||
|
||||
RefCounted!int rc;
|
||||
assert(rc.count == 0);
|
||||
rc = defaultAllocator.make!int(8);
|
||||
assert(rc.count == 1);
|
||||
}
|
||||
|
||||
private @nogc unittest
|
||||
{
|
||||
auto rc = RefCounted!int(defaultAllocator);
|
||||
assert(!rc.isInitialized);
|
||||
assert(rc.allocator is defaultAllocator);
|
||||
}
|
||||
|
||||
private @nogc unittest
|
||||
{
|
||||
auto rc = defaultAllocator.refCounted!int(5);
|
||||
assert(rc.count == 1);
|
||||
|
||||
void func(RefCounted!int rc) @nogc
|
||||
{
|
||||
assert(rc.count == 2);
|
||||
rc = null;
|
||||
assert(!rc.isInitialized);
|
||||
assert(rc.count == 0);
|
||||
}
|
||||
|
||||
assert(rc.count == 1);
|
||||
func(rc);
|
||||
assert(rc.count == 1);
|
||||
|
||||
rc = null;
|
||||
assert(!rc.isInitialized);
|
||||
assert(rc.count == 0);
|
||||
}
|
||||
|
||||
private unittest
|
||||
{
|
||||
auto rc = defaultAllocator.refCounted!int(5);
|
||||
assert(*rc == 5);
|
||||
|
||||
void func(RefCounted!int rc) @nogc
|
||||
{
|
||||
assert(rc.count == 2);
|
||||
rc = defaultAllocator.refCounted!int(4);
|
||||
assert(*rc == 4);
|
||||
assert(rc.count == 1);
|
||||
}
|
||||
func(rc);
|
||||
assert(*rc == 5);
|
||||
}
|
||||
|
||||
private unittest
|
||||
{
|
||||
static assert(is(typeof(RefCounted!int.storage.payload) == int*));
|
||||
static assert(is(typeof(RefCounted!A.storage.payload) == A));
|
||||
|
||||
static assert(is(RefCounted!B));
|
||||
static assert(is(RefCounted!A));
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a new object of type $(D_PARAM T) and wraps it in a
|
||||
* $(D_PSYMBOL RefCounted) using $(D_PARAM args) as the parameter list for
|
||||
* the constructor of $(D_PARAM T).
|
||||
*
|
||||
* This function is more efficient than the using of $(D_PSYMBOL RefCounted)
|
||||
* directly, since it allocates only ones (the internal storage and the
|
||||
* object).
|
||||
*
|
||||
* Params:
|
||||
* T = Type of the constructed object.
|
||||
* A = Types of the arguments to the constructor of $(D_PARAM T).
|
||||
* allocator = Allocator.
|
||||
* args = Constructor arguments of $(D_PARAM T).
|
||||
*
|
||||
* Returns: Newly created $(D_PSYMBOL RefCounted!T).
|
||||
*
|
||||
* Precondition: $(D_INLINECODE allocator !is null)
|
||||
*/
|
||||
RefCounted!T refCounted(T, A...)(shared Allocator allocator, auto ref A args)
|
||||
if (!is(T == interface) && !isAbstractClass!T
|
||||
&& !isAssociativeArray!T && !isArray!T)
|
||||
in
|
||||
{
|
||||
assert(allocator !is null);
|
||||
}
|
||||
body
|
||||
{
|
||||
auto rc = typeof(return)(allocator);
|
||||
|
||||
const storageSize = alignedSize(stateSize!(RefCounted!T.Storage));
|
||||
const size = alignedSize(stateSize!T + storageSize);
|
||||
|
||||
auto mem = (() @trusted => allocator.allocate(size))();
|
||||
if (mem is null)
|
||||
{
|
||||
onOutOfMemoryError();
|
||||
}
|
||||
scope (failure)
|
||||
{
|
||||
() @trusted { allocator.deallocate(mem); }();
|
||||
}
|
||||
rc.storage = emplace!((RefCounted!T.Storage))(mem[0 .. storageSize]);
|
||||
|
||||
static if (is(T == class))
|
||||
{
|
||||
rc.storage.payload = emplace!T(mem[storageSize .. $], args);
|
||||
}
|
||||
else
|
||||
{
|
||||
auto ptr = (() @trusted => (cast(T*) mem[storageSize .. $].ptr))();
|
||||
rc.storage.payload = emplace!T(ptr, args);
|
||||
}
|
||||
rc.deleter = &unifiedDeleter!(Payload!T);
|
||||
return rc;
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a new array with $(D_PARAM size) elements and wraps it in a
|
||||
* $(D_PSYMBOL RefCounted).
|
||||
*
|
||||
* Params:
|
||||
* T = Array type.
|
||||
* size = Array size.
|
||||
* allocator = Allocator.
|
||||
*
|
||||
* Returns: Newly created $(D_PSYMBOL RefCounted!T).
|
||||
*
|
||||
* Precondition: $(D_INLINECODE allocator !is null
|
||||
* && size <= size_t.max / ElementType!T.sizeof)
|
||||
*/
|
||||
RefCounted!T refCounted(T)(shared Allocator allocator, const size_t size)
|
||||
@trusted
|
||||
if (isArray!T)
|
||||
in
|
||||
{
|
||||
assert(allocator !is null);
|
||||
assert(size <= size_t.max / ElementType!T.sizeof);
|
||||
}
|
||||
body
|
||||
{
|
||||
auto payload = allocator.resize!(ElementType!T)(null, size);
|
||||
return RefCounted!T(payload, allocator);
|
||||
}
|
||||
|
||||
///
|
||||
unittest
|
||||
{
|
||||
auto rc = defaultAllocator.refCounted!int(5);
|
||||
assert(rc.count == 1);
|
||||
|
||||
void func(RefCounted!int param) @nogc
|
||||
{
|
||||
if (param.count == 2)
|
||||
{
|
||||
func(param);
|
||||
}
|
||||
else
|
||||
{
|
||||
assert(param.count == 3);
|
||||
}
|
||||
}
|
||||
func(rc);
|
||||
|
||||
assert(rc.count == 1);
|
||||
}
|
||||
|
||||
private @nogc unittest
|
||||
{
|
||||
struct E
|
||||
{
|
||||
}
|
||||
auto b = defaultAllocator.refCounted!B(15);
|
||||
static assert(is(typeof(b.storage.payload) == B*));
|
||||
static assert(is(typeof(b.prop) == int));
|
||||
static assert(!is(typeof(defaultAllocator.refCounted!B())));
|
||||
|
||||
static assert(is(typeof(defaultAllocator.refCounted!E())));
|
||||
static assert(!is(typeof(defaultAllocator.refCounted!E(5))));
|
||||
{
|
||||
auto rc = defaultAllocator.refCounted!B(3);
|
||||
assert(rc.get().prop == 3);
|
||||
}
|
||||
{
|
||||
auto rc = defaultAllocator.refCounted!E();
|
||||
assert(rc.count);
|
||||
}
|
||||
}
|
||||
|
||||
private @nogc unittest
|
||||
{
|
||||
auto rc = defaultAllocator.refCounted!(int[])(5);
|
||||
assert(rc.length == 5);
|
||||
}
|
||||
|
||||
private @nogc unittest
|
||||
{
|
||||
auto p1 = defaultAllocator.make!int(5);
|
||||
auto p2 = p1;
|
||||
auto rc = RefCounted!int(p1, defaultAllocator);
|
||||
|
||||
assert(p1 is null);
|
||||
assert(rc.get() is p2);
|
||||
}
|
||||
|
||||
private @nogc unittest
|
||||
{
|
||||
static bool destroyed = false;
|
||||
|
||||
struct F
|
||||
{
|
||||
~this() @nogc
|
||||
{
|
||||
destroyed = true;
|
||||
}
|
||||
}
|
||||
{
|
||||
auto rc = defaultAllocator.refCounted!F();
|
||||
}
|
||||
assert(destroyed);
|
||||
}
|
||||
|
||||
/**
|
||||
* $(D_PSYMBOL Unique) stores an object that gets destroyed at the end of its scope.
|
||||
*
|
||||
* Params:
|
||||
* T = Value type.
|
||||
*/
|
||||
struct Unique(T)
|
||||
{
|
||||
private Payload!T payload;
|
||||
|
||||
invariant
|
||||
{
|
||||
assert(payload is null || allocator_ !is null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Takes ownership over $(D_PARAM value), setting the counter to 1.
|
||||
* $(D_PARAM value) may be a pointer, an object or a dynamic array.
|
||||
*
|
||||
* Params:
|
||||
* value = Value whose ownership is taken over.
|
||||
* allocator = Allocator used to destroy the $(D_PARAM value) and to
|
||||
* allocate/deallocate internal storage.
|
||||
*
|
||||
* Precondition: $(D_INLINECODE allocator !is null)
|
||||
*/
|
||||
this()(auto ref Payload!T value,
|
||||
shared Allocator allocator = defaultAllocator)
|
||||
{
|
||||
this(allocator);
|
||||
|
||||
move(value, this.payload);
|
||||
static if (__traits(isRef, value))
|
||||
{
|
||||
value = null;
|
||||
}
|
||||
}
|
||||
|
||||
/// Ditto.
|
||||
this(shared Allocator allocator)
|
||||
in
|
||||
{
|
||||
assert(allocator !is null);
|
||||
}
|
||||
body
|
||||
{
|
||||
this.allocator_ = allocator;
|
||||
}
|
||||
|
||||
/**
|
||||
* $(D_PSYMBOL Unique) is noncopyable.
|
||||
*/
|
||||
@disable this(this);
|
||||
|
||||
/**
|
||||
* Destroys the owned object.
|
||||
*/
|
||||
~this()
|
||||
{
|
||||
if (this.payload !is null)
|
||||
{
|
||||
allocator.dispose(this.payload);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialized this $(D_PARAM Unique) and takes ownership over
|
||||
* $(D_PARAM rhs).
|
||||
*
|
||||
* To reset $(D_PSYMBOL Unique) assign $(D_KEYWORD null).
|
||||
*
|
||||
* If the allocator wasn't set before, $(D_PSYMBOL defaultAllocator) will
|
||||
* be used. If you need a different allocator, create a new
|
||||
* $(D_PSYMBOL Unique) and assign it.
|
||||
*
|
||||
* Params:
|
||||
* rhs = New object.
|
||||
*
|
||||
* Returns: $(D_KEYWORD this).
|
||||
*/
|
||||
ref typeof(this) opAssign()(auto ref Payload!T rhs)
|
||||
{
|
||||
allocator.dispose(this.payload);
|
||||
move(rhs, this.payload);
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
/// Ditto.
|
||||
ref typeof(this) opAssign(typeof(null))
|
||||
{
|
||||
allocator.dispose(this.payload);
|
||||
return this;
|
||||
}
|
||||
|
||||
/// Ditto.
|
||||
ref typeof(this) opAssign(typeof(this) rhs)
|
||||
{
|
||||
swap(this.allocator_, rhs.allocator_);
|
||||
swap(this.payload, rhs.payload);
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns: Reference to the owned object.
|
||||
*/
|
||||
inout(Payload!T) get() inout pure nothrow @safe @nogc
|
||||
{
|
||||
return payload;
|
||||
}
|
||||
|
||||
version (D_Ddoc)
|
||||
{
|
||||
/**
|
||||
* Params:
|
||||
* op = Operation.
|
||||
*
|
||||
* Dereferences the pointer. It is defined only for pointers, not for
|
||||
* reference types like classes, that can be accessed directly.
|
||||
*
|
||||
* Returns: Reference to the pointed value.
|
||||
*/
|
||||
ref inout(T) opUnary(string op)() inout
|
||||
if (op == "*");
|
||||
}
|
||||
else static if (isPointer!(Payload!T))
|
||||
{
|
||||
ref inout(T) opUnary(string op)() inout
|
||||
if (op == "*")
|
||||
{
|
||||
return *payload;
|
||||
}
|
||||
}
|
||||
|
||||
mixin DefaultAllocator;
|
||||
alias get this;
|
||||
}
|
||||
|
||||
///
|
||||
@nogc unittest
|
||||
{
|
||||
auto p = defaultAllocator.make!int(5);
|
||||
auto s = Unique!int(p, defaultAllocator);
|
||||
assert(p is null);
|
||||
assert(*s == 5);
|
||||
}
|
||||
|
||||
///
|
||||
@nogc unittest
|
||||
{
|
||||
static bool destroyed = false;
|
||||
|
||||
struct F
|
||||
{
|
||||
~this() @nogc
|
||||
{
|
||||
destroyed = true;
|
||||
}
|
||||
}
|
||||
{
|
||||
auto s = Unique!F(defaultAllocator.make!F(), defaultAllocator);
|
||||
}
|
||||
assert(destroyed);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a new object of type $(D_PARAM T) and wraps it in a
|
||||
* $(D_PSYMBOL Unique) using $(D_PARAM args) as the parameter list for
|
||||
* the constructor of $(D_PARAM T).
|
||||
*
|
||||
* Params:
|
||||
* T = Type of the constructed object.
|
||||
* A = Types of the arguments to the constructor of $(D_PARAM T).
|
||||
* allocator = Allocator.
|
||||
* args = Constructor arguments of $(D_PARAM T).
|
||||
*
|
||||
* Returns: Newly created $(D_PSYMBOL Unique!T).
|
||||
*
|
||||
* Precondition: $(D_INLINECODE allocator !is null)
|
||||
*/
|
||||
Unique!T unique(T, A...)(shared Allocator allocator, auto ref A args)
|
||||
if (!is(T == interface) && !isAbstractClass!T
|
||||
&& !isAssociativeArray!T && !isArray!T)
|
||||
in
|
||||
{
|
||||
assert(allocator !is null);
|
||||
}
|
||||
body
|
||||
{
|
||||
auto payload = allocator.make!(T, shared Allocator, A)(args);
|
||||
return Unique!T(payload, allocator);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a new array with $(D_PARAM size) elements and wraps it in a
|
||||
* $(D_PSYMBOL Unique).
|
||||
*
|
||||
* Params:
|
||||
* T = Array type.
|
||||
* size = Array size.
|
||||
* allocator = Allocator.
|
||||
*
|
||||
* Returns: Newly created $(D_PSYMBOL Unique!T).
|
||||
*
|
||||
* Precondition: $(D_INLINECODE allocator !is null
|
||||
* && size <= size_t.max / ElementType!T.sizeof)
|
||||
*/
|
||||
Unique!T unique(T)(shared Allocator allocator, const size_t size)
|
||||
@trusted
|
||||
if (isArray!T)
|
||||
in
|
||||
{
|
||||
assert(allocator !is null);
|
||||
assert(size <= size_t.max / ElementType!T.sizeof);
|
||||
}
|
||||
body
|
||||
{
|
||||
auto payload = allocator.resize!(ElementType!T)(null, size);
|
||||
return Unique!T(payload, allocator);
|
||||
}
|
||||
|
||||
private unittest
|
||||
{
|
||||
static assert(is(typeof(defaultAllocator.unique!B(5))));
|
||||
static assert(is(typeof(defaultAllocator.unique!(int[])(5))));
|
||||
}
|
||||
|
||||
private unittest
|
||||
{
|
||||
auto s = defaultAllocator.unique!int(5);
|
||||
assert(*s == 5);
|
||||
|
||||
s = null;
|
||||
assert(s is null);
|
||||
}
|
||||
|
||||
private unittest
|
||||
{
|
||||
auto s = defaultAllocator.unique!int(5);
|
||||
assert(*s == 5);
|
||||
|
||||
s = defaultAllocator.unique!int(4);
|
||||
assert(*s == 4);
|
||||
}
|
||||
|
||||
private @nogc unittest
|
||||
{
|
||||
auto p1 = defaultAllocator.make!int(5);
|
||||
auto p2 = p1;
|
||||
auto rc = Unique!int(p1, defaultAllocator);
|
||||
|
||||
assert(p1 is null);
|
||||
assert(rc.get() is p2);
|
||||
}
|
||||
|
||||
private @nogc unittest
|
||||
{
|
||||
auto rc = Unique!int(defaultAllocator);
|
||||
assert(rc.allocator is defaultAllocator);
|
||||
}
|
@ -3,11 +3,17 @@
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
/**
|
||||
* Smart pointers.
|
||||
*
|
||||
* $(RED Deprecated. Use $(D_PSYMBOL tanya.memory.smartref) instead.
|
||||
* This module will be removed in 0.8.0.)
|
||||
*
|
||||
* Copyright: Eugene Wissner 2016-2017.
|
||||
* License: $(LINK2 https://www.mozilla.org/en-US/MPL/2.0/,
|
||||
* Mozilla Public License, v. 2.0).
|
||||
* Authors: $(LINK2 mailto:info@caraus.de, Eugene Wissner)
|
||||
*/
|
||||
deprecated("Use tanya.memory.smartref instead")
|
||||
module tanya.memory.types;
|
||||
|
||||
import core.exception;
|
||||
@ -17,318 +23,10 @@ import std.conv;
|
||||
import std.range;
|
||||
import std.traits;
|
||||
import tanya.memory;
|
||||
|
||||
private template Payload(T)
|
||||
{
|
||||
static if (is(T == class) || is(T == interface) || isArray!T)
|
||||
{
|
||||
alias Payload = T;
|
||||
}
|
||||
else
|
||||
{
|
||||
alias Payload = T*;
|
||||
}
|
||||
}
|
||||
|
||||
final class RefCountedStore(T)
|
||||
{
|
||||
T payload;
|
||||
size_t counter = 1;
|
||||
|
||||
size_t opUnary(string op)()
|
||||
if (op == "--" || op == "++")
|
||||
in
|
||||
{
|
||||
assert(counter > 0);
|
||||
}
|
||||
body
|
||||
{
|
||||
mixin("return " ~ op ~ "counter;");
|
||||
}
|
||||
|
||||
int opCmp(const size_t counter)
|
||||
{
|
||||
if (this.counter > counter)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
else if (this.counter < counter)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
else
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
int opEquals(const size_t counter)
|
||||
{
|
||||
return this.counter == counter;
|
||||
}
|
||||
}
|
||||
|
||||
private void separateDeleter(T)(RefCountedStore!T storage,
|
||||
shared Allocator allocator)
|
||||
{
|
||||
allocator.dispose(storage.payload);
|
||||
allocator.dispose(storage);
|
||||
}
|
||||
|
||||
private void unifiedDeleter(T)(RefCountedStore!T storage,
|
||||
shared Allocator allocator)
|
||||
{
|
||||
auto ptr1 = finalize(storage);
|
||||
auto ptr2 = finalize(storage.payload);
|
||||
allocator.deallocate(ptr1.ptr[0 .. ptr1.length + ptr2.length]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Reference-counted object containing a $(D_PARAM T) value as payload.
|
||||
* $(D_PSYMBOL RefCounted) keeps track of all references of an object, and
|
||||
* when the reference count goes down to zero, frees the underlying store.
|
||||
*
|
||||
* Params:
|
||||
* T = Type of the reference-counted value.
|
||||
*/
|
||||
struct RefCounted(T)
|
||||
{
|
||||
private alias Storage = RefCountedStore!(Payload!T);
|
||||
|
||||
private Storage storage;
|
||||
private void function(Storage storage,
|
||||
shared Allocator allocator) @nogc deleter;
|
||||
|
||||
invariant
|
||||
{
|
||||
assert(storage is null || allocator_ !is null);
|
||||
assert(storage is null || deleter !is null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Takes ownership over $(D_PARAM value), setting the counter to 1.
|
||||
* $(D_PARAM value) may be a pointer, an object or a dynamic array.
|
||||
*
|
||||
* Params:
|
||||
* value = Value whose ownership is taken over.
|
||||
* allocator = Allocator used to destroy the $(D_PARAM value) and to
|
||||
* allocate/deallocate internal storage.
|
||||
*
|
||||
* Precondition: $(D_INLINECODE allocator !is null)
|
||||
*/
|
||||
this()(auto ref Payload!T value,
|
||||
shared Allocator allocator = defaultAllocator)
|
||||
{
|
||||
this(allocator);
|
||||
this.storage = allocator.make!Storage();
|
||||
this.deleter = &separateDeleter!(Payload!T);
|
||||
|
||||
move(value, this.storage.payload);
|
||||
static if (__traits(isRef, value))
|
||||
{
|
||||
value = null;
|
||||
}
|
||||
}
|
||||
|
||||
/// Ditto.
|
||||
this(shared Allocator allocator)
|
||||
in
|
||||
{
|
||||
assert(allocator !is null);
|
||||
}
|
||||
body
|
||||
{
|
||||
this.allocator_ = allocator;
|
||||
}
|
||||
|
||||
/**
|
||||
* Increases the reference counter by one.
|
||||
*/
|
||||
this(this)
|
||||
{
|
||||
if (count != 0)
|
||||
{
|
||||
++this.storage;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Decreases the reference counter by one.
|
||||
*
|
||||
* If the counter reaches 0, destroys the owned object.
|
||||
*/
|
||||
~this()
|
||||
{
|
||||
if (this.storage !is null && !(this.storage.counter && --this.storage))
|
||||
{
|
||||
deleter(this.storage, allocator);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Takes ownership over $(D_PARAM rhs). Initializes this
|
||||
* $(D_PSYMBOL RefCounted) if needed.
|
||||
*
|
||||
* If it is the last reference of the previously owned object,
|
||||
* it will be destroyed.
|
||||
*
|
||||
* To reset $(D_PSYMBOL RefCounted) assign $(D_KEYWORD null).
|
||||
*
|
||||
* If the allocator wasn't set before, $(D_PSYMBOL defaultAllocator) will
|
||||
* be used. If you need a different allocator, create a new
|
||||
* $(D_PSYMBOL RefCounted) and assign it.
|
||||
*
|
||||
* Params:
|
||||
* rhs = New object.
|
||||
*
|
||||
* Returns: $(D_KEYWORD this).
|
||||
*/
|
||||
ref typeof(this) opAssign()(auto ref Payload!T rhs)
|
||||
{
|
||||
if (this.storage is null)
|
||||
{
|
||||
this.storage = allocator.make!Storage();
|
||||
this.deleter = &separateDeleter!(Payload!T);
|
||||
}
|
||||
else if (this.storage > 1)
|
||||
{
|
||||
--this.storage;
|
||||
this.storage = allocator.make!Storage();
|
||||
this.deleter = &separateDeleter!(Payload!T);
|
||||
}
|
||||
else
|
||||
{
|
||||
finalize(this.storage.payload);
|
||||
this.storage.payload = Payload!T.init;
|
||||
}
|
||||
move(rhs, this.storage.payload);
|
||||
return this;
|
||||
}
|
||||
|
||||
/// Ditto.
|
||||
ref typeof(this) opAssign(typeof(null))
|
||||
{
|
||||
if (this.storage is null)
|
||||
{
|
||||
return this;
|
||||
}
|
||||
else if (this.storage > 1)
|
||||
{
|
||||
--this.storage;
|
||||
}
|
||||
else
|
||||
{
|
||||
deleter(this.storage, allocator);
|
||||
}
|
||||
this.storage = null;
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
/// Ditto.
|
||||
ref typeof(this) opAssign(typeof(this) rhs)
|
||||
{
|
||||
swap(this.allocator_, rhs.allocator_);
|
||||
swap(this.storage, rhs.storage);
|
||||
swap(this.deleter, rhs.deleter);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns: Reference to the owned object.
|
||||
*
|
||||
* Precondition: $(D_INLINECODE cound > 0).
|
||||
*/
|
||||
Payload!T get() pure nothrow @safe @nogc
|
||||
in
|
||||
{
|
||||
assert(count > 0, "Attempted to access an uninitialized reference.");
|
||||
}
|
||||
body
|
||||
{
|
||||
return storage.payload;
|
||||
}
|
||||
|
||||
version (D_Ddoc)
|
||||
{
|
||||
/**
|
||||
* Params:
|
||||
* op = Operation.
|
||||
*
|
||||
* Dereferences the pointer. It is defined only for pointers, not for
|
||||
* reference types like classes, that can be accessed directly.
|
||||
*
|
||||
* Returns: Reference to the pointed value.
|
||||
*/
|
||||
ref T opUnary(string op)()
|
||||
if (op == "*");
|
||||
}
|
||||
else static if (isPointer!(Payload!T))
|
||||
{
|
||||
ref T opUnary(string op)()
|
||||
if (op == "*")
|
||||
{
|
||||
return *storage.payload;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns: Whether this $(D_PSYMBOL RefCounted) already has an internal
|
||||
* storage.
|
||||
*/
|
||||
@property bool isInitialized() const
|
||||
{
|
||||
return storage !is null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns: The number of $(D_PSYMBOL RefCounted) instances that share
|
||||
* ownership over the same pointer (including $(D_KEYWORD this)).
|
||||
* If this $(D_PSYMBOL RefCounted) isn't initialized, returns `0`.
|
||||
*/
|
||||
@property size_t count() const
|
||||
{
|
||||
return storage is null ? 0 : storage.counter;
|
||||
}
|
||||
|
||||
mixin DefaultAllocator;
|
||||
alias get this;
|
||||
}
|
||||
|
||||
///
|
||||
unittest
|
||||
{
|
||||
auto rc = RefCounted!int(defaultAllocator.make!int(5), defaultAllocator);
|
||||
auto val = rc.get();
|
||||
|
||||
*val = 8;
|
||||
assert(*rc.storage.payload == 8);
|
||||
|
||||
val = null;
|
||||
assert(rc.storage.payload !is null);
|
||||
assert(*rc.storage.payload == 8);
|
||||
|
||||
*rc = 9;
|
||||
assert(*rc.storage.payload == 9);
|
||||
}
|
||||
public import tanya.memory.smartref : RefCounted, Payload;
|
||||
|
||||
version (unittest)
|
||||
{
|
||||
private class A
|
||||
{
|
||||
uint *destroyed;
|
||||
|
||||
this(ref uint destroyed) @nogc
|
||||
{
|
||||
this.destroyed = &destroyed;
|
||||
}
|
||||
|
||||
~this() @nogc
|
||||
{
|
||||
++(*destroyed);
|
||||
}
|
||||
}
|
||||
|
||||
private struct B
|
||||
{
|
||||
int prop;
|
||||
@ -340,255 +38,13 @@ version (unittest)
|
||||
}
|
||||
}
|
||||
|
||||
private @nogc unittest
|
||||
{
|
||||
uint destroyed;
|
||||
auto a = defaultAllocator.make!A(destroyed);
|
||||
|
||||
assert(destroyed == 0);
|
||||
{
|
||||
auto rc = RefCounted!A(a, defaultAllocator);
|
||||
assert(rc.count == 1);
|
||||
|
||||
void func(RefCounted!A rc) @nogc
|
||||
{
|
||||
assert(rc.count == 2);
|
||||
}
|
||||
func(rc);
|
||||
|
||||
assert(rc.count == 1);
|
||||
}
|
||||
assert(destroyed == 1);
|
||||
|
||||
RefCounted!int rc;
|
||||
assert(rc.count == 0);
|
||||
rc = defaultAllocator.make!int(8);
|
||||
assert(rc.count == 1);
|
||||
}
|
||||
|
||||
private @nogc unittest
|
||||
{
|
||||
auto rc = RefCounted!int(defaultAllocator);
|
||||
assert(!rc.isInitialized);
|
||||
assert(rc.allocator is defaultAllocator);
|
||||
}
|
||||
|
||||
private @nogc unittest
|
||||
{
|
||||
auto rc = defaultAllocator.refCounted!int(5);
|
||||
assert(rc.count == 1);
|
||||
|
||||
void func(RefCounted!int rc) @nogc
|
||||
{
|
||||
assert(rc.count == 2);
|
||||
rc = null;
|
||||
assert(!rc.isInitialized);
|
||||
assert(rc.count == 0);
|
||||
}
|
||||
|
||||
assert(rc.count == 1);
|
||||
func(rc);
|
||||
assert(rc.count == 1);
|
||||
|
||||
rc = null;
|
||||
assert(!rc.isInitialized);
|
||||
assert(rc.count == 0);
|
||||
}
|
||||
|
||||
private unittest
|
||||
{
|
||||
auto rc = defaultAllocator.refCounted!int(5);
|
||||
assert(*rc == 5);
|
||||
|
||||
void func(RefCounted!int rc) @nogc
|
||||
{
|
||||
assert(rc.count == 2);
|
||||
rc = defaultAllocator.refCounted!int(4);
|
||||
assert(*rc == 4);
|
||||
assert(rc.count == 1);
|
||||
}
|
||||
func(rc);
|
||||
assert(*rc == 5);
|
||||
}
|
||||
|
||||
private unittest
|
||||
{
|
||||
static assert(is(typeof(RefCounted!int.storage.payload) == int*));
|
||||
static assert(is(typeof(RefCounted!A.storage.payload) == A));
|
||||
|
||||
static assert(is(RefCounted!B));
|
||||
static assert(is(RefCounted!A));
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a new object of type $(D_PARAM T) and wraps it in a
|
||||
* $(D_PSYMBOL RefCounted) using $(D_PARAM args) as the parameter list for
|
||||
* the constructor of $(D_PARAM T).
|
||||
*
|
||||
* This function is more efficient than the using of $(D_PSYMBOL RefCounted)
|
||||
* directly, since it allocates only ones (the internal storage and the
|
||||
* object).
|
||||
*
|
||||
* Params:
|
||||
* T = Type of the constructed object.
|
||||
* A = Types of the arguments to the constructor of $(D_PARAM T).
|
||||
* allocator = Allocator.
|
||||
* args = Constructor arguments of $(D_PARAM T).
|
||||
*
|
||||
* Returns: Newly created $(D_PSYMBOL RefCounted!T).
|
||||
*
|
||||
* Precondition: $(D_INLINECODE allocator !is null)
|
||||
*/
|
||||
RefCounted!T refCounted(T, A...)(shared Allocator allocator, auto ref A args)
|
||||
if (!is(T == interface) && !isAbstractClass!T
|
||||
&& !isAssociativeArray!T && !isArray!T)
|
||||
in
|
||||
{
|
||||
assert(allocator !is null);
|
||||
}
|
||||
body
|
||||
{
|
||||
auto rc = typeof(return)(allocator);
|
||||
|
||||
const storageSize = alignedSize(stateSize!(RefCounted!T.Storage));
|
||||
const size = alignedSize(stateSize!T + storageSize);
|
||||
|
||||
auto mem = (() @trusted => allocator.allocate(size))();
|
||||
if (mem is null)
|
||||
{
|
||||
onOutOfMemoryError();
|
||||
}
|
||||
scope (failure)
|
||||
{
|
||||
() @trusted { allocator.deallocate(mem); }();
|
||||
}
|
||||
rc.storage = emplace!((RefCounted!T.Storage))(mem[0 .. storageSize]);
|
||||
|
||||
static if (is(T == class))
|
||||
{
|
||||
rc.storage.payload = emplace!T(mem[storageSize .. $], args);
|
||||
}
|
||||
else
|
||||
{
|
||||
auto ptr = (() @trusted => (cast(T*) mem[storageSize .. $].ptr))();
|
||||
rc.storage.payload = emplace!T(ptr, args);
|
||||
}
|
||||
rc.deleter = &unifiedDeleter!(Payload!T);
|
||||
return rc;
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a new array with $(D_PARAM size) elements and wraps it in a
|
||||
* $(D_PSYMBOL RefCounted).
|
||||
*
|
||||
* Params:
|
||||
* T = Array type.
|
||||
* size = Array size.
|
||||
* allocator = Allocator.
|
||||
*
|
||||
* Returns: Newly created $(D_PSYMBOL RefCounted!T).
|
||||
*
|
||||
* Precondition: $(D_INLINECODE allocator !is null
|
||||
* && size <= size_t.max / ElementType!T.sizeof)
|
||||
*/
|
||||
RefCounted!T refCounted(T)(shared Allocator allocator, const size_t size)
|
||||
@trusted
|
||||
if (isArray!T)
|
||||
in
|
||||
{
|
||||
assert(allocator !is null);
|
||||
assert(size <= size_t.max / ElementType!T.sizeof);
|
||||
}
|
||||
body
|
||||
{
|
||||
auto payload = allocator.resize!(ElementType!T)(null, size);
|
||||
return RefCounted!T(payload, allocator);
|
||||
}
|
||||
|
||||
///
|
||||
unittest
|
||||
{
|
||||
auto rc = defaultAllocator.refCounted!int(5);
|
||||
assert(rc.count == 1);
|
||||
|
||||
void func(RefCounted!int param) @nogc
|
||||
{
|
||||
if (param.count == 2)
|
||||
{
|
||||
func(param);
|
||||
}
|
||||
else
|
||||
{
|
||||
assert(param.count == 3);
|
||||
}
|
||||
}
|
||||
func(rc);
|
||||
|
||||
assert(rc.count == 1);
|
||||
}
|
||||
|
||||
private @nogc unittest
|
||||
{
|
||||
struct E
|
||||
{
|
||||
}
|
||||
auto b = defaultAllocator.refCounted!B(15);
|
||||
static assert(is(typeof(b.storage.payload) == B*));
|
||||
static assert(is(typeof(b.prop) == int));
|
||||
static assert(!is(typeof(defaultAllocator.refCounted!B())));
|
||||
|
||||
static assert(is(typeof(defaultAllocator.refCounted!E())));
|
||||
static assert(!is(typeof(defaultAllocator.refCounted!E(5))));
|
||||
{
|
||||
auto rc = defaultAllocator.refCounted!B(3);
|
||||
assert(rc.get().prop == 3);
|
||||
}
|
||||
{
|
||||
auto rc = defaultAllocator.refCounted!E();
|
||||
assert(rc.count);
|
||||
}
|
||||
}
|
||||
|
||||
private @nogc unittest
|
||||
{
|
||||
auto rc = defaultAllocator.refCounted!(int[])(5);
|
||||
assert(rc.length == 5);
|
||||
}
|
||||
|
||||
private @nogc unittest
|
||||
{
|
||||
auto p1 = defaultAllocator.make!int(5);
|
||||
auto p2 = p1;
|
||||
auto rc = RefCounted!int(p1, defaultAllocator);
|
||||
|
||||
assert(p1 is null);
|
||||
assert(rc.get() is p2);
|
||||
}
|
||||
|
||||
private @nogc unittest
|
||||
{
|
||||
static bool destroyed = false;
|
||||
|
||||
struct F
|
||||
{
|
||||
~this() @nogc
|
||||
{
|
||||
destroyed = true;
|
||||
}
|
||||
}
|
||||
{
|
||||
auto rc = defaultAllocator.refCounted!F();
|
||||
}
|
||||
assert(destroyed);
|
||||
}
|
||||
|
||||
/**
|
||||
* $(D_PSYMBOL Unique) stores an object that gets destroyed at the end of its scope.
|
||||
* $(D_PSYMBOL Scoped) stores an object that gets destroyed at the end of its scope.
|
||||
*
|
||||
* Params:
|
||||
* T = Value type.
|
||||
*/
|
||||
struct Unique(T)
|
||||
struct Scoped(T)
|
||||
{
|
||||
private Payload!T payload;
|
||||
|
||||
@ -632,7 +88,7 @@ struct Unique(T)
|
||||
}
|
||||
|
||||
/**
|
||||
* $(D_PSYMBOL Unique) is noncopyable.
|
||||
* $(D_PSYMBOL Scoped) is noncopyable.
|
||||
*/
|
||||
@disable this(this);
|
||||
|
||||
@ -648,14 +104,14 @@ struct Unique(T)
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialized this $(D_PARAM Unique) and takes ownership over
|
||||
* Initialized this $(D_PARAM Scoped) and takes ownership over
|
||||
* $(D_PARAM rhs).
|
||||
*
|
||||
* To reset $(D_PSYMBOL Unique) assign $(D_KEYWORD null).
|
||||
* To reset $(D_PSYMBOL Scoped) assign $(D_KEYWORD null).
|
||||
*
|
||||
* If the allocator wasn't set before, $(D_PSYMBOL defaultAllocator) will
|
||||
* be used. If you need a different allocator, create a new
|
||||
* $(D_PSYMBOL Unique) and assign it.
|
||||
* $(D_PSYMBOL Scoped) and assign it.
|
||||
*
|
||||
* Params:
|
||||
* rhs = New object.
|
||||
@ -725,7 +181,7 @@ struct Unique(T)
|
||||
@nogc unittest
|
||||
{
|
||||
auto p = defaultAllocator.make!int(5);
|
||||
auto s = Unique!int(p, defaultAllocator);
|
||||
auto s = Scoped!int(p, defaultAllocator);
|
||||
assert(p is null);
|
||||
assert(*s == 5);
|
||||
}
|
||||
@ -743,14 +199,14 @@ struct Unique(T)
|
||||
}
|
||||
}
|
||||
{
|
||||
auto s = Unique!F(defaultAllocator.make!F(), defaultAllocator);
|
||||
auto s = Scoped!F(defaultAllocator.make!F(), defaultAllocator);
|
||||
}
|
||||
assert(destroyed);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a new object of type $(D_PARAM T) and wraps it in a
|
||||
* $(D_PSYMBOL Unique) using $(D_PARAM args) as the parameter list for
|
||||
* $(D_PSYMBOL Scoped) using $(D_PARAM args) as the parameter list for
|
||||
* the constructor of $(D_PARAM T).
|
||||
*
|
||||
* Params:
|
||||
@ -759,11 +215,11 @@ struct Unique(T)
|
||||
* allocator = Allocator.
|
||||
* args = Constructor arguments of $(D_PARAM T).
|
||||
*
|
||||
* Returns: Newly created $(D_PSYMBOL Unique!T).
|
||||
* Returns: Newly created $(D_PSYMBOL Scoped!T).
|
||||
*
|
||||
* Precondition: $(D_INLINECODE allocator !is null)
|
||||
*/
|
||||
Unique!T unique(T, A...)(shared Allocator allocator, auto ref A args)
|
||||
Scoped!T scoped(T, A...)(shared Allocator allocator, auto ref A args)
|
||||
if (!is(T == interface) && !isAbstractClass!T
|
||||
&& !isAssociativeArray!T && !isArray!T)
|
||||
in
|
||||
@ -773,24 +229,24 @@ in
|
||||
body
|
||||
{
|
||||
auto payload = allocator.make!(T, shared Allocator, A)(args);
|
||||
return Unique!T(payload, allocator);
|
||||
return Scoped!T(payload, allocator);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a new array with $(D_PARAM size) elements and wraps it in a
|
||||
* $(D_PSYMBOL Unique).
|
||||
* $(D_PSYMBOL Scoped).
|
||||
*
|
||||
* Params:
|
||||
* T = Array type.
|
||||
* size = Array size.
|
||||
* allocator = Allocator.
|
||||
*
|
||||
* Returns: Newly created $(D_PSYMBOL Unique!T).
|
||||
* Returns: Newly created $(D_PSYMBOL Scoped!T).
|
||||
*
|
||||
* Precondition: $(D_INLINECODE allocator !is null
|
||||
* && size <= size_t.max / ElementType!T.sizeof)
|
||||
*/
|
||||
Unique!T unique(T)(shared Allocator allocator, const size_t size)
|
||||
Scoped!T scoped(T)(shared Allocator allocator, const size_t size)
|
||||
@trusted
|
||||
if (isArray!T)
|
||||
in
|
||||
@ -801,18 +257,18 @@ in
|
||||
body
|
||||
{
|
||||
auto payload = allocator.resize!(ElementType!T)(null, size);
|
||||
return Unique!T(payload, allocator);
|
||||
return Scoped!T(payload, allocator);
|
||||
}
|
||||
|
||||
private unittest
|
||||
{
|
||||
static assert(is(typeof(defaultAllocator.unique!B(5))));
|
||||
static assert(is(typeof(defaultAllocator.unique!(int[])(5))));
|
||||
static assert(is(typeof(defaultAllocator.scoped!B(5))));
|
||||
static assert(is(typeof(defaultAllocator.scoped!(int[])(5))));
|
||||
}
|
||||
|
||||
private unittest
|
||||
{
|
||||
auto s = defaultAllocator.unique!int(5);
|
||||
auto s = defaultAllocator.scoped!int(5);
|
||||
assert(*s == 5);
|
||||
|
||||
s = null;
|
||||
@ -821,10 +277,10 @@ private unittest
|
||||
|
||||
private unittest
|
||||
{
|
||||
auto s = defaultAllocator.unique!int(5);
|
||||
auto s = defaultAllocator.scoped!int(5);
|
||||
assert(*s == 5);
|
||||
|
||||
s = defaultAllocator.unique!int(4);
|
||||
s = defaultAllocator.scoped!int(4);
|
||||
assert(*s == 4);
|
||||
}
|
||||
|
||||
@ -832,7 +288,7 @@ private @nogc unittest
|
||||
{
|
||||
auto p1 = defaultAllocator.make!int(5);
|
||||
auto p2 = p1;
|
||||
auto rc = Unique!int(p1, defaultAllocator);
|
||||
auto rc = Scoped!int(p1, defaultAllocator);
|
||||
|
||||
assert(p1 is null);
|
||||
assert(rc.get() is p2);
|
||||
@ -840,17 +296,6 @@ private @nogc unittest
|
||||
|
||||
private @nogc unittest
|
||||
{
|
||||
auto rc = Unique!int(defaultAllocator);
|
||||
auto rc = Scoped!int(defaultAllocator);
|
||||
assert(rc.allocator is defaultAllocator);
|
||||
}
|
||||
|
||||
/**
|
||||
* $(RED Deprecated. Use $(D_PSYMBOL Unique) and $(D_PSYMBOL unique) instead.
|
||||
* These aliases will be removed in 0.8.0.)
|
||||
*/
|
||||
deprecated("Use Unique instead")
|
||||
alias Scoped = Unique;
|
||||
|
||||
/// Ditto.
|
||||
deprecated("Use unique instead")
|
||||
alias scoped = unique;
|
||||
|
Loading…
Reference in New Issue
Block a user