October 05, 2015
On Monday, 5 October 2015 at 17:27:30 UTC, Jonathan M Davis wrote:
> On Sunday, 4 October 2015 at 18:02:21 UTC, bitwise wrote:
>> There are several things in phobos that are classes. This goes against the nogc goal, so what's the plan?
>
> Now, Walter and Andrei have talked about adding some sort of reference counting to the language so that we can support a class hierarchy that's specifically reference-counted (exceptions in particular would be a target for that) - though that doesn't necessarily mean that they won't use the GC, just that their destruction will be deterministic. And std.allocator should make it easier to use classes without the GC. So, the situation with classes and the GC will be improving.

> - Jonathan M Davis

The deterministic destruction is actually what I'm after.

A ubiquitous use case in graphics apps:

class Texture { }
class Texture2D : Texture
{
    this() { /* load texture... */ }
    ~this { /* free texture */ }     // OOPS, when, if ever, will this be called?
}

I think there is some kind of RC(T) coming down the pipe, but I would prefer a natural solution like the one provided by DIP74. No awkward syntax, no type obfuscation.

As you said, I believe the GC performance issues can be mitigated well enough using allocators and such, so I'm not that concerned about it.

One thing not mentioned on DIP74 is @nogc. I wouldn't want my RC class to end up nested in a regular GC class, but it appears that DIP74 still GC allocates the RC class. So will RC classes, based on this DIP, be allowed in a @nogc context?

I think the solution to the above question is as simple as allowing RC classes to have something like "static T opNew(){}".

     Bit

October 05, 2015
On Monday, 5 October 2015 at 18:18:15 UTC, bitwise wrote:
> I think the solution to the above question is as simple as allowing RC classes to have something like "static T opNew(){}".
>
>      Bit

User-defined `new` is already in the language, but it's deprecated.
October 05, 2015
On Monday, 5 October 2015 at 17:19:09 UTC, Gary Willoughby wrote:
> This can be shortened to:
>
> import std.stdio;
> import std.typecons;
>
> class A
> {
> 	string name;
>
> 	this(string name)
> 	{
> 		this.name = name;
> 	}
>
> 	void hello()
> 	{
> 		writeln("Hello, ", this.name);
> 	}
> }
>
> void main()
> {
> 	auto a = scoped!A("Foo");
> 	a.hello();
> }

There's a critical flaw in `scoped`. Observe:

import std.stdio;
import std.typecons;

class A
{
	string name;

	this(string name)
	{
		this.name = name;
		writeln("Creating A");
	}

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

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

void main()
{
	auto a1 = scoped!A("Foo");
	a1.hello();

	A a2 = scoped!A("Foo");
	a2.hello();
}


The output:

Creating A
Hello, Foo
Creating A
Destroying A
Destroying A
object.Error: Access Violation
October 05, 2015
On Monday, 5 October 2015 at 19:02:59 UTC, Meta wrote:
> On Monday, 5 October 2015 at 18:18:15 UTC, bitwise wrote:
>> I think the solution to the above question is as simple as allowing RC classes to have something like "static T opNew(){}".
>>
>>      Bit
>
> User-defined `new` is already in the language, but it's deprecated.

I can't find much info on it, but it seems like it used the old C++ operator overloading syntax. For that reason, I guess it should be removed. I think ref counted classes are a good reason to bring it back with the proper syntax though "static T opNew()"

    Bit
October 05, 2015
On Monday, 5 October 2015 at 19:07:20 UTC, Meta wrote:
> On Monday, 5 October 2015 at 17:19:09 UTC, Gary Willoughby wrote:
>> This can be shortened to:
>>
>> import std.stdio;
>> import std.typecons;
>>
>> class A
>> {
>> 	string name;
>>
>> 	this(string name)
>> 	{
>> 		this.name = name;
>> 	}
>>
>> 	void hello()
>> 	{
>> 		writeln("Hello, ", this.name);
>> 	}
>> }
>>
>> void main()
>> {
>> 	auto a = scoped!A("Foo");
>> 	a.hello();
>> }
>
> There's a critical flaw in `scoped`. Observe:
>
> import std.stdio;
> import std.typecons;
>
> class A
> {
> 	string name;
>
> 	this(string name)
> 	{
> 		this.name = name;
> 		writeln("Creating A");
> 	}
>
> 	~this()
> 	{
> 		writeln("Destroying A");
> 	}
>
> 	void hello()
> 	{
> 		writeln("Hello, ", this.name);
> 	}
> }
>
> void main()
> {
> 	auto a1 = scoped!A("Foo");
> 	a1.hello();
>
> 	A a2 = scoped!A("Foo");
> 	a2.hello();
> }
>
>
> The output:
>
> Creating A
> Hello, Foo
> Creating A
> Destroying A
> Destroying A
> object.Error: Access Violation

also:

// Error: can only synchronize on class objects, not 'Scoped'
auto a1 = scoped!A("Foo");
synchronized(a1) {}

and also:

// Error: template main.foo cannot deduce function from argument types !()(Scoped)
void foo(T)() if(is(T == A)) { }
void main(string[] args) {
    auto a1 = scoped!A("Foo");
    foo(a1);
}

    Bit

October 05, 2015
On Monday, 5 October 2015 at 19:43:54 UTC, bitwise wrote:
> also:
>
> // Error: can only synchronize on class objects, not 'Scoped'
> auto a1 = scoped!A("Foo");
> synchronized(a1) {}
>
> and also:
>
> // Error: template main.foo cannot deduce function from argument types !()(Scoped)
> void foo(T)() if(is(T == A)) { }
> void main(string[] args) {
>     auto a1 = scoped!A("Foo");
>     foo(a1);
> }
>
>     Bit

This is more an issue with `alias this` than it is with `scoped`. Really, the issue I outlined is also due to `alias this` and its implicit conversion.
October 05, 2015
On Monday, 5 October 2015 at 19:07:20 UTC, Meta wrote:
> On Monday, 5 October 2015 at 17:19:09 UTC, Gary Willoughby wrote:
>> This can be shortened to:
>>
>> import std.stdio;
>> import std.typecons;
>>
>> class A
>> {
>> 	string name;
>>
>> 	this(string name)
>> 	{
>> 		this.name = name;
>> 	}
>>
>> 	void hello()
>> 	{
>> 		writeln("Hello, ", this.name);
>> 	}
>> }
>>
>> void main()
>> {
>> 	auto a = scoped!A("Foo");
>> 	a.hello();
>> }
>
> There's a critical flaw in `scoped`. Observe:
>
> import std.stdio;
> import std.typecons;
>
> class A
> {
> 	string name;
>
> 	this(string name)
> 	{
> 		this.name = name;
> 		writeln("Creating A");
> 	}
>
> 	~this()
> 	{
> 		writeln("Destroying A");
> 	}
>
> 	void hello()
> 	{
> 		writeln("Hello, ", this.name);
> 	}
> }
>
> void main()
> {
> 	auto a1 = scoped!A("Foo");
> 	a1.hello();
>
> 	A a2 = scoped!A("Foo");
> 	a2.hello();
> }
>
>
> The output:
>
> Creating A
> Hello, Foo
> Creating A
> Destroying A
> Destroying A
> object.Error: Access Violation

----
import std.stdio;

struct Scoped(T) {
	void[__traits(classInstanceSize, T)] buf = void;
	
	this(Args...)(auto ref Args args) {
		this.buf = typeid(T).init[];
		(cast(T) this.buf.ptr).__ctor(args);
	}
	
	~this() {
		.destroy(this.get());
	}
	
		
	T get() {
		return cast(T) this.buf.ptr;
	}
	
	alias get this;
}

auto scoped(T, Args...)(auto ref Args args) {
	return Scoped!T(args);
}

class A
{
    string name;

    this(string name)
    {
       this.name = name;
       writeln("Creating A");
    }

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

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

void main() {
	auto a1 = scoped!A("Foo");
	a1.hello();
	
	auto a2 = scoped!A("Bar");
	a2.hello();
}
----

Application output:
Creating A
Hello, Foo
Creating A
Hello, Bar
Destroying A
Destroying A
October 05, 2015
On Monday, 5 October 2015 at 20:23:41 UTC, Namespace wrote:
> On Monday, 5 October 2015 at 19:07:20 UTC, Meta wrote:
>> [...]
>
> ----
> import std.stdio;
>
> [...]

I think you kinda missed the point. The second one was _supposed_ to be typed as Foo.

The point is that the compiler allows it. It's unsafe.

   Bit
October 05, 2015
On Monday, 5 October 2015 at 20:49:08 UTC, bitwise wrote:
[...]
>
> I think you kinda missed the point. The second one was _supposed_ to be typed as Foo.
>
> The point is that the compiler allows it. It's unsafe.
>
>    Bit

typo:

a2 was supposed to be typed as 'A'.

    Bit
October 05, 2015
On Monday 05 October 2015 21:07, Meta wrote:

> There's a critical flaw in `scoped`. Observe:
> 
> import std.stdio;
> import std.typecons;
> 
> class A
> {
> 	string name;
> 
> 	this(string name)
> 	{
> 		this.name = name;
> 		writeln("Creating A");
> 	}
> 
> 	~this()
> 	{
> 		writeln("Destroying A");
> 	}
> 
> 	void hello()
> 	{
> 		writeln("Hello, ", this.name);
> 	}
> }
> 
> void main()
> {
> 	auto a1 = scoped!A("Foo");
> 	a1.hello();
> 
> 	A a2 = scoped!A("Foo");
> 	a2.hello();
> }
> 
> 
> The output:
> 
> Creating A
> Hello, Foo
> Creating A
> Destroying A
> Destroying A
> object.Error: Access Violation

You're getting at the segfault, right? The example can then be much simpler:

----
import std.typecons;

class A
{
    void hello() {}
}

void main()
{
    A a2 = scoped!A();
    a2.hello(); /* segfault */
}
----