Smart pointers instead of GC?

Adam D. Ruppe destructionator at gmail.com
Tue Feb 4 19:58:36 PST 2014


On Wednesday, 5 February 2014 at 00:49:25 UTC, Walter Bright 
wrote:
> What happens with this:
>
>     T identity(T t) { return t; }

My previous post was with regard to nullability. This one is 
about ownership.

The answer is the same: if you need this, use a template, 
otherwise, you always deal with a specific type (if you are 
allocating a new copy or need to store the reference somewhere 
outside the owner's scope) or a borrowed type:

GC!Object obj;
static assert(typeof(identity(obj) == GC!Object);

and so on, you can test this today, D already has all these 
options.


If you don't have a template, how do you approach this? Well, 
this is really nothing new for one:

class Foo {}

Foo foo = new Foo();
Object obj = foo; // legal

Object identity(Object obj) { return obj; }

foo = identity(foo); // won't compile without an explicit cast



When you work with interfaces, some information is lost. That's a 
pain sometimes, but it is a feature too - the function that works 
on the interface doesn't need to care about anything else.

With class inheritance, we can address this with covariant 
overrides.... but only of it is a member function (method).



So let's just accept that the free function loses data. You can't 
get a container back from a range in the general case.


So, what about the method:

class Foo {
    typeof(this) identity() { return this; }
}


What is typeof(this)? I'll tell you: Foo! Here's why:

*) The internal this pointer is never null, and thus never needs 
to be nullable, so that's out of consideration (see my last 
message)

*) The internal this pointer does *not* own its memory. Calling 
"delete this;" is (I think) undefined behavior in D: the class 
might exist on the stack, or be allocated with malloc, and 
calling freeing the memory inside a method is likely to lead to a 
crash when the invariant is called upon returning anyway!


Since the object does not own its own memory, it must always 
assume this is a borrowed reference. Therefore:

* Escaping `this` is prohibited.

Foo globalFoo;
class Foo {
    // illegal, escapes a reference to a borrowed reference
    void storeThis() { globalFoo = this; }
}

Consider that this is wrong if the class is made with 
std.conv.emplace (or the deprecated scope storage class) on a 
stack buffer.

This is silently wrong today, it can leave a dangling reference. 
(though since emplace is not @safe, we do have that protection 
against such problems... though since we're talking about 
avoiding the GC here, emplace is something we probably want 
anyway)


* Returning this is always a borrowed pointer, unless you 
explicitly make a copy.

class Foo {
    Foo identity() { return this; }
}

GC!Foo foo = new Foo(); // owned by the Gc

Foo foo2 = foo.identity(); // borrowed reference, NOT GC!Foo


Therefore, we cannot escape it to a global that way either. If we 
want that, we have to work with foo directly, bypassing 
foo.identity.

A clone method would create a new owned thing, so it returns 
something more specific:

class Foo {
    GC!Foo clone() { auto foo = new GC; /* copy methods */ return 
foo; }
}


A clone method might also be templated on an allocator, and can 
thus change the return type as needed by the allocator. This 
wouldn't be virtual... but it kinda has to be.

class Foo {
     virtual GC!Foo clone() {...}
}
class Bar : Foo {
     override RC!Foo clone() {...}
}

You wouldn't want that anyway, since if you called it through the 
foo interface, you wouldn't know how to free it (does it need to 
run a struct dtor? The GC? The caller needs to know.)


Gotta pick an allocation method in the base class if you want it 
to work as a virtual function. So... yeah do that or use a 
non-virtual template. You can make it work.

A clone method typically wouldn't even be inout anyway so meh.

* A class is responsible for its member references but not 
itself. When the class' dtor is called, it needs to call member 
struct dtors, but does not call free(this) - leave that to the 
outside allcoator. This is already reality today (the GC calls 
the dtor before releasing the memory).


More information about the Digitalmars-d mailing list