Skip to content

Instantly share code, notes, and snippets.

@run-dlang
Created March 12, 2021 01:41
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save run-dlang/c5805ebb9e9b9734e032ca5e81fcfa90 to your computer and use it in GitHub Desktop.
Save run-dlang/c5805ebb9e9b9734e032ca5e81fcfa90 to your computer and use it in GitHub Desktop.
Code shared from run.dlang.io.
module app;
enum useDIPLowering = true;
void main() @safe {
import std.stdio : writeln;
{
S a = true, b = false, c = b;
a.data = 1;
b.data = 2;
a.moveAssign(b);
c.data = 3;
writeln(a.data == 3);
}
writeln(netAllocCount == 0);
}
ptrdiff_t netAllocCount = 0;
void* malloc(size_t size) @system nothrow @nogc {
import core.stdc.stdlib : malloc;
void* ptr = malloc(size);
netAllocCount += int(ptr !is null);
return ptr;
}
void free(void* ptr) @system nothrow @nogc {
import core.stdc.stdlib : free;
netAllocCount -= int(ptr !is null);
free(ptr);
}
struct S {
private:
struct Data {
uint data;
int refCount = 1;
}
Data* ptr;
Data internal;
public:
bool isUnique() const pure @safe nothrow @nogc {
return (ptr is &internal); }
ref inout(uint) data() return inout pure @safe nothrow @nogc {
return ptr.data; }
// normal construction and assignment
this(bool unique) @trusted nothrow @nogc {
construct(unique); }
private void construct(bool unique) @system nothrow @nogc {
// @system since this must not be called by itself on an already-initialized object.
pragma(inline, false); // Prevent the inliner from accidentally fixing things.
if(unique) {
ptr = &internal;
internal = Data.init;
} else {
ptr = cast(Data*) malloc(size_t.sizeof * 2);
(*ptr) = Data.init;
}
}
ref typeof(this) opAssign(bool unique) return @trusted nothrow @nogc {
destruct(this);
construct(unique);
return this;
}
// copy construction
@disable this(this);
this(ref typeof(this) source) pure @trusted nothrow @nogc {
if(source.isUnique) {
ptr = &internal;
internal = source.internal;
} else {
ptr = source.ptr;
ptr.refCount += 1;
}
}
/* move construction and assignment (these must be called manually
and do not use the DIP syntax, since it's not implemented yet): */
void moveConstruct(ref S source) @system nothrow @nogc {
// @system since this must not be called by itself on an already-initialized object.
if(source.isUnique) {
ptr = &internal;
internal = source.internal;
} else
ptr = source.ptr;
}
void moveAssign(ref S source) @trusted nothrow @nogc {
static if(useDIPLowering) {
// destroy after (the DIP's proposal):
S oldDest = void;
oldDest.moveConstruct(this); // Move the old value to a temporary.
moveConstruct(source);
// Implicitly destroy the old value.
} else {
// conditionally move and destroy before (my proposal):
if(&source !is &this) {
destruct(this);
moveConstruct(source);
}
}
}
// destructor
~this() @safe nothrow @nogc {
destruct(this); }
private static void destruct(ref S s) @trusted nothrow @nogc {
pragma(inline, false); // Prevent the inliner from accidentally fixing things.
if(!s.isUnique) {
s.ptr.refCount -= 1;
if(s.ptr.refCount <= 0)
free(s.ptr);
}
s.ptr = null;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment