October 07, 2015
On Tuesday, 6 October 2015 at 17:03:07 UTC, bitwise wrote:
> On Tuesday, 6 October 2015 at 06:45:47 UTC, Jonathan M Davis wrote:
>> On Monday, 5 October 2015 at 23:08:37 UTC, bitwise wrote:
>>> Well, again that has it's pros and cons. This is why I just want a normal language solution like DIP74.
>>
>> They're not the same thing at all. scoped is supposed to put the class on the stack, not the heap. And it's not ref-counted. It's so that you can create a class object in place, use it, and throw it away without doing any heap allocation. Essentially, it allows you to use a class as if it were a non-copyable struct. Even if we end up with ref-counting supported in the language, it doesn't obviate the need for scoped classes. They're for different use cases.
>>
>> - Jonathan M Davis
>
>
> On Monday, 5 October 2015 at 18:18:15 UTC, bitwise wrote:
>> The deterministic destruction is actually what I'm after.
>
> For my purposes, they are pretty much the same.
>
> So again, I'll paste the same example:
>
> class Texture { }
> class Texture2D : Texture {
>     this() { /* load texture... */ }
>     ~this { /* free texture */ }     // OOPS, when, if ever, will this be called?
> }
>
> Memory is not only thing that has to be cleaned up.
>
>     Bit

import std.stdio;
import std.typecons;

class Texture {
    void bar() {
        writeln("Texture.bar");
    }
}

class Texture2D : Texture {
    this() { writeln("this()"); }
    ~this() { writeln("~this()"); }
    override void bar() {
        writeln("Texture2D.bar");
    }
}

void foo(Texture texture) {
}

void main() {
    auto texture = scoped!Texture2D();
    texture.bar();
    foo(texture);
}


RefCounted isn't implemented for classes, but there's no reason why it shouldn't work.


Really, I don't get why everyone wants to have builtin refcounting, when all that's required is a working way to make escape-proof references.
October 07, 2015
On Wednesday, 7 October 2015 at 09:49:27 UTC, Marc Schütz wrote:
> On Tuesday, 6 October 2015 at 17:03:07 UTC, bitwise wrote:
>> On Tuesday, 6 October 2015 at 06:45:47 UTC, Jonathan M Davis wrote:
>>> On Monday, 5 October 2015 at 23:08:37 UTC, bitwise wrote:
>>>> Well, again that has it's pros and cons. This is why I just want a normal language solution like DIP74.
>>>
>>> They're not the same thing at all. scoped is supposed to put the class on the stack, not the heap. And it's not ref-counted. It's so that you can create a class object in place, use it, and throw it away without doing any heap allocation. Essentially, it allows you to use a class as if it were a non-copyable struct. Even if we end up with ref-counting supported in the language, it doesn't obviate the need for scoped classes. They're for different use cases.
>>>
>>> - Jonathan M Davis
>>
>>
>> On Monday, 5 October 2015 at 18:18:15 UTC, bitwise wrote:
>>> The deterministic destruction is actually what I'm after.
>>
>> For my purposes, they are pretty much the same.
>>
>> So again, I'll paste the same example:
>>
>> class Texture { }
>> class Texture2D : Texture {
>>     this() { /* load texture... */ }
>>     ~this { /* free texture */ }     // OOPS, when, if ever, will this be called?
>> }
>>
>> Memory is not only thing that has to be cleaned up.
>>
>>     Bit
>
> import std.stdio;
> import std.typecons;
>
> class Texture {
>     void bar() {
>         writeln("Texture.bar");
>     }
> }
>
> class Texture2D : Texture {
>     this() { writeln("this()"); }
>     ~this() { writeln("~this()"); }
>     override void bar() {
>         writeln("Texture2D.bar");
>     }
> }
>
> void foo(Texture texture) {
> }
>
> void main() {
>     auto texture = scoped!Texture2D();
>     texture.bar();
>     foo(texture);
> }
>
>
> RefCounted isn't implemented for classes, but there's no reason why it shouldn't work.
>
>
> Really, I don't get why everyone wants to have builtin refcounting, when all that's required is a working way to make escape-proof references.

Because there is no guarantee that others, who use your code, get it right and use those constructs.
October 07, 2015
On Wednesday, 7 October 2015 at 10:03:44 UTC, Namespace wrote:
> On Wednesday, 7 October 2015 at 09:49:27 UTC, Marc Schütz wrote:
>> Really, I don't get why everyone wants to have builtin refcounting, when all that's required is a working way to make escape-proof references.
>
> Because there is no guarantee that others, who use your code, get it right and use those constructs.

Language-supported ref-counting wouldn't fix that. As long as you're allowed to put a ref-counted object inside of a GC-managed object, it's possible that the GC will ultimately managed the lifetime of your ref-counted object - or even that it will never be destroyed, because it simply isn't collected prior to the program shutting down. And it's not like we're going to make it illegal to put a ref-counted object inside of a GC-managed object. That would be needlessly restrictive. Ultimately, programmers simply have to be smart about what they do with objects on the GC heap if deterministic destruction is required.

Having ref-counting built into the language will allow us to make it more efficient and provide some safety guarantees that can't necessarily be provided in a struct, but it doesn't make it so that no one can misuse ref-counted objects.

Ultimately, the largest benefit to having ref-counting built into the language will probably be that we can the have exceptions be reference counted - and maybe even make it so that they're malloced normally so that they can be used in @nogc code. Most of the benefits of ref-counting can already be done with structs.

- Jonathan M Davis
October 07, 2015
> Language-supported ref-counting wouldn't fix that. As long as you're allowed to put a ref-counted object inside of a GC-managed object, it's possible that the GC will ultimately managed the lifetime of your ref-counted object - or even that it will never be destroyed, because it simply isn't collected prior to the program shutting down. And it's not like we're going to make it illegal to put a ref-counted object inside of a GC-managed object. That would be needlessly restrictive. Ultimately, programmers simply have to be smart about what they do with objects on the GC heap if deterministic destruction is required.
>
> Having ref-counting built into the language will allow us to make it more efficient and provide some safety guarantees that can't necessarily be provided in a struct, but it doesn't make it so that no one can misuse ref-counted objects.
>
> Ultimately, the largest benefit to having ref-counting built into the language will probably be that we can the have exceptions be reference counted - and maybe even make it so that they're malloced normally so that they can be used in @nogc code. Most of the benefits of ref-counting can already be done with structs.
>
> - Jonathan M Davis

Sure, there are, of course, still ways to avoid the guarantees. But it's more likely that others write A a = new A("Hello"); instead Unique!A a = unique!A("Hello"); so we increase the probability that others get it right.
October 07, 2015
On Wednesday, 7 October 2015 at 10:50:35 UTC, Namespace wrote:
>> Language-supported ref-counting wouldn't fix that. As long as you're allowed to put a ref-counted object inside of a GC-managed object, it's possible that the GC will ultimately managed the lifetime of your ref-counted object - or even that it will never be destroyed, because it simply isn't collected prior to the program shutting down. And it's not like we're going to make it illegal to put a ref-counted object inside of a GC-managed object. That would be needlessly restrictive. Ultimately, programmers simply have to be smart about what they do with objects on the GC heap if deterministic destruction is required.
>>
>> Having ref-counting built into the language will allow us to make it more efficient and provide some safety guarantees that can't necessarily be provided in a struct, but it doesn't make it so that no one can misuse ref-counted objects.
>>
>> Ultimately, the largest benefit to having ref-counting built into the language will probably be that we can the have exceptions be reference counted - and maybe even make it so that they're malloced normally so that they can be used in @nogc code. Most of the benefits of ref-counting can already be done with structs.
>>
>> - Jonathan M Davis
>
> Sure, there are, of course, still ways to avoid the guarantees. But it's more likely that others write A a = new A("Hello"); instead Unique!A a = unique!A("Hello"); so we increase the probability that others get it right.

Well, except that then it's less obvious that an object is ref-counted and less likely that the programmer using it will realize that the object expects to have a deterministic lifetime. So, it might actually make the situation worse and make it so that programmers are more likely to get wrong. I don't think that it's clear at all that the situation will be better with regards to programmers getting it right if it's in the language. Maybe it will be, but maybe it won't.

- Jonathan M Davis
October 07, 2015
> Well, except that then it's less obvious that an object is ref-counted and less likely that the programmer using it will realize that the object expects to have a deterministic lifetime. So, it might actually make the situation worse and make it so that programmers are more likely to get wrong. I don't think that it's clear at all that the situation will be better with regards to programmers getting it right if it's in the language. Maybe it will be, but maybe it won't.
>
> - Jonathan M Davis

Well then, there is another solution: enable inheritance for structs as well. Then we have polymorphie and deterministic lifetimes. Of course we cannot expect too much magic. But an example:

----
struct A {
    int hello() {
        return 42;
    }
}

struct B : A {
    override int hello() {
        return 23;
    }
}

void foo(A* a) {
    writeln(a.hello()); // prints 23
}

void main() {
    A* b = new B();
    foo(b);
}
----

That shouldn't be too complex, since it follows the rules of C++: http://cpp.sh/9r6k
October 07, 2015
On Wednesday, 7 October 2015 at 10:03:44 UTC, Namespace wrote:
> Because there is no guarantee that others, who use your code, get it right and use those constructs.

The obvious way would be to make the constructor private and only provide a factory method returning a Scoped!Texture2D. I just tried that, but ran into problems with the construction (probably a bug in emplace). Don't have time to investigate this now. Maybe you can also return a Unique!Texture2D, but that one doesn't have an alias this...
October 07, 2015
On Wednesday, 7 October 2015 at 11:27:49 UTC, Marc Schütz wrote:
> On Wednesday, 7 October 2015 at 10:03:44 UTC, Namespace wrote:
>> Because there is no guarantee that others, who use your code, get it right and use those constructs.
>
> The obvious way would be to make the constructor private and only provide a factory method returning a Scoped!Texture2D. I just tried that, but ran into problems with the construction (probably a bug in emplace). Don't have time to investigate this now. Maybe you can also return a Unique!Texture2D, but that one doesn't have an alias this...

Something like this?

----
import std.stdio;

struct Scoped(T) if (is(T == class)) {
    private void[__traits(classInstanceSize, T)] _buf = void;

    this(Args...)(auto ref Args args) {
        _buf = typeid(T).init[];
        this.get().__ctor(args);
    }

    @disable
    this(this);

    ~this() {
        .destroy(this.get());
    }

    @nogc
    private inout(T) get() inout pure nothrow {
        return cast(T) _buf.ptr;
    }

    auto opDispatch(string method, Args...)(auto ref Args args) {
        return mixin("this.get()." ~ method ~ "(args)");
    }
}

class A {
    private string name;

    private this(string name) {
       this.name = name;
       writeln("Creating A");
    }
	
	static auto scoped(Args...)(auto ref Args args) {
		return Scoped!(typeof(this))(args);
	}

    ~this() {
       writeln("Destroying A");
    }

    void hello() {
       writeln("Hello, ", this.name);
    }
}

void main() {
	auto a = A.scoped("Foo");
	a.hello();
}
----

Kinda ugly, but it works.
October 07, 2015
On Wednesday, 7 October 2015 at 10:44:50 UTC, Jonathan M Davis wrote:
> Having ref-counting built into the language will allow us to make it more efficient and provide some safety guarantees that can't necessarily be provided in a struct, but it doesn't make it so that no one can misuse ref-counted objects.

I doubt that it can gain much performance in contrast to a well-designed scope-like feature. In particular, elision of inc/dec pairs is practically free in such a system.

>
> Ultimately, the largest benefit to having ref-counting built into the language will probably be that we can the have exceptions be reference counted [...]

Why not allow throwing structs if they subtype Throwable (via alias this)? This is less whacky than it sounds at first: During the throwing process, no postblit's don't have to be called, because throwing always just moves. Destruction and copying can only take place in catch blocks, where it's already supported. There would probably just be minor changes necessary to druntime and the compiler.
October 07, 2015
On Wednesday, 7 October 2015 at 05:13:06 UTC, Walter Bright wrote:
> On 10/6/2015 7:57 PM, bitwise wrote:
>> Again though, if I have to restate what I've been arguing for as simply as
>> possible, it's that I want to use RAII and polymorphism at the same time, as a
>> natural language solution. DIP74 would satisfy everything I'm asking for.
>
> Yes. We need to do that.

I think that every nanometer this one gets moved up the todo list will be a big win.

    Bit