www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - D 2015/2016 Vision?

reply bitwise <bitwise.pvt gmail.com> writes:
http://wiki.dlang.org/Vision/2015H1

Looking at this, It's obvious that some of it has spilled over, 
but it would be nice to have a fresh document detailing the plan 
moving forward.

I'm hoping to see something about D's memory model. In 
particular, an update on DIP74.

There are several things in phobos that are classes. This goes 
against the nogc goal, so what's the plan?

Currently, it seems like someone will eventually take all these 
classes/hierarchies and flatten them into some struct/template 
approach. I am not looking forward to this at all. I like 
polymorphism when it's appropriate.

For example, streams. I like to be able to do things like this:

Stream s1 = new MemoryStream();
Stream s2 = new File("..");
BinaryReader br;
br = new BinaryReader(s1);
br = new BinaryReader(s2);

But, given D's current nogc goal, it seems they must all be 
eliminated...unless DIP74 swoops in to save the day.

     Bit
Oct 04 2015
next sibling parent reply rsw0x <anonymous anonymous.com> writes:
On Sunday, 4 October 2015 at 18:02:21 UTC, bitwise wrote:
 Currently, it seems like someone will eventually take all these 
 classes/hierarchies and flatten them into some struct/template 
 approach. I am not looking forward to this at all. I like 
 polymorphism when it's appropriate.
It's just a symptom of D classes being so difficult to use without the GC, compared to when I first picked up D to now I find myself barely *ever* using classes and just C-style(er, D-style?) polymorphism with structs, mixins, and alias this. Bye.
Oct 04 2015
next sibling parent bitwise <bitwise.pvt gmail.com> writes:
On Sunday, 4 October 2015 at 20:18:25 UTC, rsw0x wrote:
 On Sunday, 4 October 2015 at 18:02:21 UTC, bitwise wrote:
 Currently, it seems like someone will eventually take all 
 these classes/hierarchies and flatten them into some 
 struct/template approach. I am not looking forward to this at 
 all. I like polymorphism when it's appropriate.
It's just a symptom of D classes being so difficult to use without the GC, compared to when I first picked up D to now I find myself barely *ever* using classes and just C-style(er, D-style?) polymorphism with structs, mixins, and alias this. Bye.
Yeah, thats all good stuff, but for compile time. Also, it makes things that should be trivial to design 3x harder to code. Bit
Oct 04 2015
prev sibling parent reply Namespace <rswhite4 gmail.com> writes:
On Sunday, 4 October 2015 at 20:18:25 UTC, rsw0x wrote:
 On Sunday, 4 October 2015 at 18:02:21 UTC, bitwise wrote:
 Currently, it seems like someone will eventually take all 
 these classes/hierarchies and flatten them into some 
 struct/template approach. I am not looking forward to this at 
 all. I like polymorphism when it's appropriate.
It's just a symptom of D classes being so difficult to use without the GC, compared to when I first picked up D to now I find myself barely *ever* using classes and just C-style(er, D-style?) polymorphism with structs, mixins, and alias this. Bye.
You can use classes without GC _and_ with deteministic lifetime: ---- import std.stdio; class A { string name; this(string name) { this.name = name; } void hello() { writeln("Hallo, ", this.name); } } struct Scoped(T) if (is(T == class)) { import core.stdc.stdlib : malloc, free; enum SIZE = __traits(classInstanceSize, T); T obj; this(Args...)(auto ref Args args) { void[] buffer = malloc(SIZE)[0 .. SIZE]; buffer[] = typeid(T).init[]; this.obj = cast(T) buffer.ptr; this.obj.__ctor(args); } ~this() { destroy(this.obj); free(cast(void*) this.obj); } alias obj this; } auto scoped(T, Args...)(auto ref Args args) if (is(T == class)) { return Scoped!T(args); } void main() { auto a = scoped!A("Foo"); a.hello(); } ----
Oct 04 2015
next sibling parent reply bitwise <bitwise.pvt gmail.com> writes:
On Sunday, 4 October 2015 at 22:10:18 UTC, Namespace wrote:
 On Sunday, 4 October 2015 at 20:18:25 UTC, rsw0x wrote:
 On Sunday, 4 October 2015 at 18:02:21 UTC, bitwise wrote:
 [...]
It's just a symptom of D classes being so difficult to use without the GC, compared to when I first picked up D to now I find myself barely *ever* using classes and just C-style(er, D-style?) polymorphism with structs, mixins, and alias this. Bye.
You can use classes without GC _and_ with deteministic lifetime: ---- import std.stdio; class A { string name; this(string name) { this.name = name; } void hello() { writeln("Hallo, ", this.name); } } struct Scoped(T) if (is(T == class)) { import core.stdc.stdlib : malloc, free; enum SIZE = __traits(classInstanceSize, T); T obj; this(Args...)(auto ref Args args) { void[] buffer = malloc(SIZE)[0 .. SIZE]; buffer[] = typeid(T).init[]; this.obj = cast(T) buffer.ptr; this.obj.__ctor(args); } ~this() { destroy(this.obj); free(cast(void*) this.obj); } alias obj this; } auto scoped(T, Args...)(auto ref Args args) if (is(T == class)) { return Scoped!T(args); } void main() { auto a = scoped!A("Foo"); a.hello(); } ----
And if I want to nest a class in a struct? or pass a class around? and still have deterministic destruction? You can't nest a Scoped in a struct, and RefCounted doesn't work on a class. It just shouldn't be this hard do such trivial things. Bit
Oct 04 2015
parent Gary Willoughby <dev nomad.so> writes:
On Sunday, 4 October 2015 at 22:49:59 UTC, bitwise wrote:
 And if I want to nest a class in a struct? or pass a class 
 around? and still have deterministic destruction?

 You can't nest a Scoped in a struct, and RefCounted doesn't 
 work on a class.

 It just shouldn't be this hard do such trivial things.

    Bit
I think you can get a lot of mileage from using the following:
Oct 05 2015
prev sibling parent reply Gary Willoughby <dev nomad.so> writes:
On Sunday, 4 October 2015 at 22:10:18 UTC, Namespace wrote:
 You can use classes without GC _and_ with deteministic lifetime:

 ----
 import std.stdio;

 class A {
 	string name;
 	
 	this(string name) {
 		this.name = name;
 	}
 	
 	void hello() {
 		writeln("Hallo, ", this.name);
 	}
 }

 struct Scoped(T) if (is(T == class)) {
 	import core.stdc.stdlib : malloc, free;
 	
 	enum SIZE = __traits(classInstanceSize, T);
 	
 	T obj;
 	
 	this(Args...)(auto ref Args args) {
 		void[] buffer = malloc(SIZE)[0 .. SIZE];
 		buffer[] = typeid(T).init[];
 		
 		this.obj = cast(T) buffer.ptr;
 		this.obj.__ctor(args);
 	}
 	
 	~this() {
 		destroy(this.obj);
 		free(cast(void*) this.obj);
 	}
 	
 	alias obj this;
 }

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

 void main() {
 	auto a = scoped!A("Foo");
 	a.hello();
 }
 ----
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(); }
Oct 05 2015
parent reply Meta <jared771 gmail.com> writes:
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
Oct 05 2015
next sibling parent reply bitwise <bitwise.pvt gmail.com> writes:
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
Oct 05 2015
parent Meta <jared771 gmail.com> writes:
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.
Oct 05 2015
prev sibling next sibling parent reply Namespace <rswhite4 gmail.com> writes:
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
Oct 05 2015
parent reply bitwise <bitwise.pvt gmail.com> writes:
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
Oct 05 2015
next sibling parent bitwise <bitwise.pvt gmail.com> writes:
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
Oct 05 2015
prev sibling parent reply Namespace <rswhite4 gmail.com> writes:
On Monday, 5 October 2015 at 20:49:08 UTC, bitwise wrote:
 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
Ah, right. I've overseen it.
Oct 05 2015
parent reply Namespace <rswhite4 gmail.com> writes:
But you can simply relinquish alias this and use opDispatch. 
Problem solved.
Oct 05 2015
next sibling parent reply bitwise <bitwise.pvt gmail.com> writes:
On Monday, 5 October 2015 at 21:29:20 UTC, Namespace wrote:
 But you can simply relinquish alias this and use opDispatch. 
 Problem solved.
I don't understand what you mean. Thinking about this now though, shouldn't this be a bug? I think there should be a rule for this one: class A{} struct B { A a; alias a this; } A a = B(); // error Error: assignment from alias this not allowed for class or interface alias. Bit
Oct 05 2015
parent reply Namespace <rswhite4 gmail.com> writes:
On Monday, 5 October 2015 at 22:15:59 UTC, bitwise wrote:
 On Monday, 5 October 2015 at 21:29:20 UTC, Namespace wrote:
 But you can simply relinquish alias this and use opDispatch. 
 Problem solved.
I don't understand what you mean.
---- import std.stdio; struct Scoped(T) { private void[__traits(classInstanceSize, T)] buf = void; this(Args...)(auto ref Args args) { this.buf = typeid(T).init[]; this.get().__ctor(args); } ~this() { .destroy(this.get()); } private T get() { return cast(T) this.buf.ptr; } auto opDispatch(string method, Args...)(auto ref Args args) { return mixin("this.get()." ~ method ~ "(args)"); } } 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() { //A a0 = scoped!A("Test"); <-- fails auto a1 = scoped!A("Foo"); a1.hello(); auto a2 = scoped!A("Bar"); a2.hello(); } ----
Oct 06 2015
parent Namespace <rswhite4 gmail.com> writes:
It's a step simpler with the new inline feature (works sadly only 
with the -inline flag):

----
pragma(inline, true)
auto scoped(T, Args...)(auto ref Args args) if (is(T == class)) {
     void[__traits(classInstanceSize, T)] buf = void;
     buf[] = typeid(T).init[];

     T obj = cast(T) buf.ptr;
     obj.__ctor(args);

     return obj;
}

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() {
     A a1 = scoped!A("Foo");
     a1.hello();
}
----
Oct 06 2015
prev sibling parent reply Meta <jared771 gmail.com> writes:
On Monday, 5 October 2015 at 21:29:20 UTC, Namespace wrote:
 But you can simply relinquish alias this and use opDispatch. 
 Problem solved.
There is std.typecons.Proxy which may do the trick. It provides forwarding while disallowing implicit conversion. It might make sense to reimplement `scoped` in terms of Proxy, I don't know.
Oct 05 2015
parent reply bitwise <bitwise.pvt gmail.com> writes:
On Monday, 5 October 2015 at 22:48:37 UTC, Meta wrote:
 On Monday, 5 October 2015 at 21:29:20 UTC, Namespace wrote:
 But you can simply relinquish alias this and use opDispatch. 
 Problem solved.
There is std.typecons.Proxy which may do the trick. It provides forwarding while disallowing implicit conversion. It might make sense to reimplement `scoped` in terms of Proxy, I don't know.
Well, again that has it's pros and cons. This is why I just want a normal language solution like DIP74. http://imgur.com/v6CIWln Bit
Oct 05 2015
parent reply Jonathan M Davis <jmdavisProg gmx.com> writes:
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
Oct 05 2015
next sibling parent reply Ola Fosheim =?UTF-8?B?R3LDuHN0YWQ=?= writes:
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.
Why not leave stack allocation of objects to the compiler, like inlining? Then add a " stack" constraint that will make the compilation fail if the compiler is unable to put it on the stack? You need the compiler to prove that that the life time of the object is shorter than the stack frame in order to have memory safety anyway.
Oct 06 2015
parent Jonathan M Davis <jmdavisProg gmx.com> writes:
On Tuesday, 6 October 2015 at 08:27:02 UTC, Ola Fosheim Grøstad 
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.
Why not leave stack allocation of objects to the compiler, like inlining? Then add a " stack" constraint that will make the compilation fail if the compiler is unable to put it on the stack? You need the compiler to prove that that the life time of the object is shorter than the stack frame in order to have memory safety anyway.
scoped is not designed with the idea that it's memory safe. scoped is very much an system operation. And scoped is intended to replace scope classes in the language, so I don't think that any language support is going to be added for this. It's something that someone who knows what they're doing and needs that extra bit of efficiency can do, not something that's really intended to be in your average D program. In most cases, anything that's supposed to live on the stack should have been a struct anyway. It's just an issue when you want to use something on the stack in a particular case whereas it normally would be on the heap. And given D's lack of flow analysis and Walter's insistence on not adding it, I rather doubt that he's going to be big on the idea of having the compiler decide that it's safe to allocate a class object on the stack rather than the heap. But I don't remember him ever saying anything on that topic specifically. - Jonathan M Davis
Oct 06 2015
prev sibling parent reply bitwise <bitwise.pvt gmail.com> writes:
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
Oct 06 2015
next sibling parent reply Jonathan M Davis <jmdavisProg gmx.com> writes:
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:
 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.
 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.
Well, they might seem the same when you only look at that part, but they won't both solve your problem, depending on what you're trying to do. But in general, at this point, with D, if you want deterministic destruction, then you use structs. Classes are not the appropriate place for it. If they were ref-counted, then they could be, but as long as they're not, then classes are not the place to have stuff that cares about deterministic destruction. And if you're stuck with stuff in classes that do care about deterministic destruction, then you have to use the sort of destructor/finalizer to clean anything up except for the cases where you screw up and forget to manually call the function that does the cleanup. I expect that we'll get ref-counting for classes at some point, since while ref-counting with structs works, as I understand it, it does have some holes that make it less than ideal (but not necessarily unusable). And for some reason, IIRC, RefCounted doesn't work with classes, so you'd be forced to write your own ref-counted wrapper. It can certainly be done though. - Jonathan M Davis
Oct 06 2015
next sibling parent reply bitwise <bitwise.pvt gmail.com> writes:
On Tuesday, 6 October 2015 at 17:20:39 UTC, Jonathan M Davis 
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:
 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.
 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.
Well, they might seem the same when you only look at that part, but they won't both solve your problem, depending on what you're trying to do. But in general, at this point, with D, if you want deterministic destruction, then you use structs. Classes are not the appropriate place for it. If they were ref-counted, then they could be, but as long as they're not, then classes are not the place to have stuff that cares about deterministic destruction. And if you're stuck with stuff in classes that do care about deterministic destruction, then you have to use the the destructor/finalizer to clean anything up except for the cases where you screw up and forget to manually call the function that does the cleanup.
I'm not sure what else I can say. The example I posted says it bar because of their mistakes? ;) non-memory resources, but I'm guessing it's something like calling GC.collect() manually every couple of seconds. If the textures aren't released in the destructor, I don't really see any other way to tell when they're referenced or not. Of course though, mobile devices are the new PC, and battery life is very much a concern, so this is a total waste...especially if I'm doing very little GC allocation anyways. Also, of course, there are the performance issues.
 I expect that we'll get ref-counting for classes at some point, 
 since while ref-counting with structs works, as I understand 
 it, it does have some holes that make it less than ideal (but 
 not necessarily unusable). And for some reason, IIRC, 
 RefCounted doesn't work with classes, so you'd be forced to 
 write your own ref-counted wrapper. It can certainly be done 
 though.

 - Jonathan M Davis
Correct, RefCounted doesn't work with classes. Not sure why, but I wrote my own, and trivial unittests pass: http://dpaste.dzfl.pl/997615d2d188 But again, as I've already mentioned, it hides the type, has annoying syntax, and most importantly, is error prone. I can't really write a class thats meant to be used with RC(T) and know that no one will ever try to use it on it's own, GC allocated. D needs a real solution here. http://imgur.com/v6CIWln Bit
Oct 06 2015
next sibling parent Namespace <rswhite4 gmail.com> writes:
You don't need RC, just use Unique. In most cases you don't want 
to use RC, because you never have control over the ownership.
Oct 06 2015
prev sibling parent reply Jonathan M Davis <jmdavisProg gmx.com> writes:
On Tuesday, 6 October 2015 at 18:10:42 UTC, bitwise wrote:
 On Tuesday, 6 October 2015 at 17:20:39 UTC, Jonathan M Davis 
 wrote:
 I'm not sure what else I can say. The example I posted says it 

 the bar because of their mistakes? ;)
It's a side effect of having the lifetime of an object managed by the GC. There's no way around that except to use something else like manual memory management or reference counting. In D, it's a good reason to use structs to manage resources like that, and since most objects really have no need of inheritance and have no business being classes, it's usually fine. But in the cases where you do have to use a class, it can get annoying.

 non-memory resources, but I'm guessing it's something like 
 calling GC.collect() manually every couple of seconds. If the 
 textures aren't released in the destructor, I don't really see 
 any other way to tell when they're referenced or not.

 Of course though, mobile devices are the new PC, and battery 
 life is very much a concern, so this is a total 
 waste...especially if I'm doing very little GC allocation 
 anyways. Also, of course, there are the performance issues.
You simply do not rely on the GC or the destruction of the object to free system resources. You manually call a function on the object to free those resources when you're done with it. In the something like using(myObj) { } // myObj.dispose() is called when exiting this scope In Java, you'd have no choice but to call dispose manually. And yes, that sucks, but it's life with a GC-managed object. The GC has a number of benefits to it, but it does not come without its costs. Having the option to have properly ref-counted classes in addition to classes managed by the GC would definitely be an improvement, and I expect that we'll get there at some point, but there _are_ ways to deal with the problem in the interim, even if it's not ideal. In most cases though, just don't use classes. In most cases, inheritance is a horrible way to write programs anyway, because it's _horrible_ for code reuse. It definitely has its uses, but I've found that I rarely need classes in D. I suspect that far too many folks new to D end up using classes instead of structs just because they're used to using classes in C++ or Java or whatever. - Jonathan M Davis
Oct 06 2015
next sibling parent reply Paulo Pinto <pjmlp progtools.org> writes:
On Tuesday, 6 October 2015 at 18:43:42 UTC, Jonathan M Davis 
wrote:
 On Tuesday, 6 October 2015 at 18:10:42 UTC, bitwise wrote:
 [...]
It's a side effect of having the lifetime of an object managed by the GC. There's no way around that except to use something else like manual memory management or reference counting. In D, it's a good reason to use structs to manage resources like that, and since most objects really have no need of inheritance and have no business being classes, it's usually fine. But in the cases where you do have to use a class, it can get annoying.
 [...]
You simply do not rely on the GC or the destruction of the object to free system resources. You manually call a function on the object to free those resources when you're done with it. (IIRC) is something like using(myObj) { } // myObj.dispose() is called when exiting this scope In Java, you'd have no choice but to call dispose manually. And yes, that sucks, but it's life with a GC-managed object. The GC has a number of benefits to it, but it does not come without its costs. Having the option to have properly ref-counted classes in addition to classes managed by the GC would definitely be an improvement, and I expect that we'll get there at some point, but there _are_ ways to deal with the problem in the interim, even if it's not ideal. In most cases though, just don't use classes. In most cases, inheritance is a horrible way to write programs anyway, because it's _horrible_ for code reuse. It definitely has its uses, but I've found that I rarely need classes in D. I suspect that far too many folks new to D end up using classes instead of structs just because they're used to using classes in C++ or Java or whatever. - Jonathan M Davis
As of Java 7 try (BufferedReader br = new BufferedReader(new FileReader(path))) { return br.readLine(); } Or just use a functional programming pattern for resource management: withFile (something, { fd -> work with file }) Better languages allow to write that like withFile (something) { fd -> work with file } GC is not an hindrance when the languages are built to properly work with it. -- Paulo
Oct 06 2015
parent rsw0x <anonymous anonymous.com> writes:
On Tuesday, 6 October 2015 at 19:15:09 UTC, Paulo Pinto wrote:
 GC is not an hindrance when the languages are built to properly 
 work with it.

 --
 Paulo
+1, tired of repeating this.
Oct 06 2015
prev sibling next sibling parent reply jmh530 <john.michael.hall gmail.com> writes:
On Tuesday, 6 October 2015 at 18:43:42 UTC, Jonathan M Davis 
wrote:
 In most cases though, just don't use classes. In most cases, 
 inheritance is a horrible way to write programs anyway, because 
 it's _horrible_ for code reuse. It definitely has its uses, but 
 I've found that I rarely need classes in D. I suspect that far 
 too many folks new to D end up using classes instead of structs 
 just because they're used to using classes in C++ or Java or 
 whatever.
What improvements to structs do you think would help people coming from C++/Java most?
Oct 06 2015
parent reply Jonathan M Davis <jmdavisProg gmx.com> writes:
On Tuesday, 6 October 2015 at 20:04:06 UTC, jmh530 wrote:
 On Tuesday, 6 October 2015 at 18:43:42 UTC, Jonathan M Davis 
 wrote:
 In most cases though, just don't use classes. In most cases, 
 inheritance is a horrible way to write programs anyway, 
 because it's _horrible_ for code reuse. It definitely has its 
 uses, but I've found that I rarely need classes in D. I 
 suspect that far too many folks new to D end up using classes 
 instead of structs just because they're used to using classes 
 in C++ or Java or whatever.
What improvements to structs do you think would help people coming from C++/Java most?
I don't think the problem is with structs. The problem is that programmers coming from other languages default to using classes. The default in D should always be a struct. You use a class because you actually need inheritance or because you want to ensure that a type is always a reference type and don't want to go to the trouble of writing a struct that way (and even then, you should probably just write the struct that way). You shouldn't use a class as your default for user-defined types in D. But because other languages don't have structs quite like D does, and you use classes in those other languages, that's what most everyone wants to use - at least initially. It would not surprise me if there are some compiler bugs with regards to structs that result in some loopholes that shouldn't be there (e.g. with disable-ing stuff), but on the whole, I think that D structs are very well designed as they are. The only real issue IMHO is having an init value vs having a default constructor, and that's a tradeoff with pros and cons on both sides. It does sometimes seem painful to folks coming from C++, but on the whole, I think that we're better off for it. - Jonathan M Davis
Oct 06 2015
parent reply Kagamin <spam here.lot> writes:
On Tuesday, 6 October 2015 at 20:31:58 UTC, Jonathan M Davis 
wrote:
 I don't think the problem is with structs. The problem is that 
 programmers coming from other languages default to using 
 classes. The default in D should always be a struct. You use a 
 class because you actually need inheritance or because you want 
 to ensure that a type is always a reference type and don't want 
 to go to the trouble of writing a struct that way (and even 
 then, you should probably just write the struct that way).
Hmm... If we must emulate reference semantics manually, it feels like C++ with explicit references, pointers and all sorts of smart pointers, and obviates need for classes being reference types: just emulate reference semantics as we must do it anyway.
Oct 08 2015
parent reply Jonathan M Davis <jmdavisProg gmx.com> writes:
On Thursday, 8 October 2015 at 08:21:09 UTC, Kagamin wrote:
 On Tuesday, 6 October 2015 at 20:31:58 UTC, Jonathan M Davis 
 wrote:
 I don't think the problem is with structs. The problem is that 
 programmers coming from other languages default to using 
 classes. The default in D should always be a struct. You use a 
 class because you actually need inheritance or because you 
 want to ensure that a type is always a reference type and 
 don't want to go to the trouble of writing a struct that way 
 (and even then, you should probably just write the struct that 
 way).
Hmm... If we must emulate reference semantics manually, it feels like C++ with explicit references, pointers and all sorts of smart pointers, and obviates need for classes being reference types: just emulate reference semantics as we must do it anyway.
Maybe, but having classes be value generally makes no sense, because you can't use polymorphism with value types. Classes are inherently reference types given their semantics. Even in C++ that's the case. It's just that they don't separate out classes and structs the way we do. But classes that use inheritance have to be used as reference types or all you're doing is sharing implementation (which can be done easily enough without inheritance). You're not doing anything with polymorphism without references. So, the separation that D has makes a lot of sense. It's just that in some cases - namely where determinstic destruction is required - having them be managed by the GC doesn't work, and we need a solution for that. Most classes work just fine with a garbage collector though. And if we have classes that are inherently ref-counted or which are sitting inside of smart-pointers, then they're still reference types like they should be. They just have their lifetime managed in a more deterministic manner for those cases where that's appropriate. - Jonathan M Davis
Oct 08 2015
next sibling parent reply bitwise <bitwise.pvt gmail.com> writes:
On Thursday, 8 October 2015 at 14:05:07 UTC, Jonathan M Davis 
wrote:
 Maybe, but having classes be value generally makes no sense, 
 because you can't use polymorphism with value types.
This is a huge generalization, and is incorrect. You can still use inheritance.
 Classes are inherently reference types given their semantics.
Incorrect.
 Even in C++ that's the case. It's just that they don't separate 
 out classes and structs the way we do. But classes that use 
 inheritance have to be used as reference types or all you're 
 doing is sharing implementation (which can be done easily 
 enough without inheritance).
It _can_ be done, but that doesn't mean that it's _always_ the best, or even preferred approach. Bit
Oct 08 2015
parent reply Ola Fosheim =?UTF-8?B?R3LDuHN0YWQ=?= writes:
On Thursday, 8 October 2015 at 16:17:49 UTC, bitwise wrote:
 Classes are inherently reference types given their semantics.
Incorrect.
Inaccurate maybe, but something that has an identity is not a value type. Value types are by definition identity-less. The type system of C/C++/D doesn't really provide guarantees needed to have proper value types.
Oct 08 2015
parent reply Meta <jared771 gmail.com> writes:
On Thursday, 8 October 2015 at 16:29:58 UTC, Ola Fosheim Grøstad 
wrote:
 Inaccurate maybe, but something that has an identity is not a 
 value type. Value types are by definition identity-less. The 
 type system of C/C++/D doesn't really provide guarantees needed 
 to have proper value types.
Why, because you can take their address?
Oct 08 2015
parent Ola Fosheim =?UTF-8?B?R3LDuHN0YWQ=?= writes:
On Thursday, 8 October 2015 at 16:38:31 UTC, Meta wrote:
 Why, because you can take their address?
You or someone you call can take the address. If there is a way to distinguish instances one way or another then it isn't a value, it is an object. Doesn't mean your code is taking the address.
Oct 08 2015
prev sibling parent reply Kagamin <spam here.lot> writes:
On Thursday, 8 October 2015 at 14:05:07 UTC, Jonathan M Davis 
wrote:
 Maybe, but having classes be value generally makes no sense, 
 because you can't use polymorphism with value types.
If it doesn't make sense for reference types to be value types, then your suggestion to use structs (value types) to implement reference types doesn't make sense either.
Oct 12 2015
next sibling parent reply Jonathan M Davis <jmdavisProg gmx.com> writes:
On Monday, 12 October 2015 at 07:39:30 UTC, Kagamin wrote:
 On Thursday, 8 October 2015 at 14:05:07 UTC, Jonathan M Davis 
 wrote:
 Maybe, but having classes be value generally makes no sense, 
 because you can't use polymorphism with value types.
If it doesn't make sense for reference types to be value types, then your suggestion to use structs (value types) to implement reference types doesn't make sense either.
My point is that without a level of indirection, polymorphism isn't involved. For polymorphism, you have a pointer or a reference of a base type, and it points/refers to an object of a derived type which then implements some of the functionality differently from the base. If you have a value type, there is no polymorphism, because there is no indirection. You're dealing with the exact type that the variable is. With a value type, all inheritance does is provide a way to reuse code (and not even a very good way to reuse code, since you can only derive from one type at a time). Inheritance's primary benefit is with polymorphism. Given that, it makes a lot of sense to use inheritance with objects on the heap and a lot less sense to use it for objects on the stack. You can stick value types on the heap and pass around pointers or references to them, but then you're not dealing with polymorphism. You're just providing a way to have multiple pieces of code point operate on the same object, whereas passing it around on the stack means copying it. In some cases, ref would do the job just as well. But I wasn't really trying to say anything about whether value types made sense on the heap or anything like that. My point was entirely about polymorphism not applying to anything without using a pointer or reference. - Jonathan M Davis
Oct 12 2015
parent Kagamin <spam here.lot> writes:
On Monday, 12 October 2015 at 08:00:59 UTC, Jonathan M Davis 
wrote:
 You can stick value types on the heap and pass around pointers 
 or references to them, but then you're not dealing with 
 polymorphism.
As soon as you choose to use value types you already get the first problem: pointers or references. Neither of these two work and there are two more options here, I've gone with the fourth.
 My point was entirely about polymorphism not applying to 
 anything without using a pointer or reference.
My point was about structs not applying to anything that needs reference semantics (in current state of D). Your suggestion to do otherwise is a kludge and doesn't work well.
Oct 12 2015
prev sibling parent deadalnix <deadalnix gmail.com> writes:
On Monday, 12 October 2015 at 07:39:30 UTC, Kagamin wrote:
 On Thursday, 8 October 2015 at 14:05:07 UTC, Jonathan M Davis 
 wrote:
 Maybe, but having classes be value generally makes no sense, 
 because you can't use polymorphism with value types.
If it doesn't make sense for reference types to be value types, then your suggestion to use structs (value types) to implement reference types doesn't make sense either.
It does make sense. You have the value type that do the reference counting, and a reference type that is reference counted. That is good separation of concerns.
Oct 12 2015
prev sibling parent reply bitwise <bitwise.pvt gmail.com> writes:
On Tuesday, 6 October 2015 at 18:43:42 UTC, Jonathan M Davis 
wrote:
 On Tuesday, 6 October 2015 at 18:10:42 UTC, bitwise wrote:
 On Tuesday, 6 October 2015 at 17:20:39 UTC, Jonathan M Davis 
 wrote:
 I'm not sure what else I can say. The example I posted says it 

 the bar because of their mistakes? ;)
It's a side effect of having the lifetime of an object managed by the GC. There's no way around that except to use something else like manual memory management or reference counting.
You are literally repeating what I just said in different words.
 in D, it's a good reason to use structs to manage resources 
 like that, and since most objects really have no need of 
 inheritance and have no business being classes, it's usually 
 fine.
This is an opinion. I want polymorphism AND deterministic destruction, and the least you could do is just admit that it's a downside to D not having it, instead of trying to tell me that everything I know is wrong..
 But in the cases where you do have to use a class, it can get 
 annoying.
YES, its does, and it's not just an odd case here and there..
 You simply do not rely on the GC or the destruction of the 
 object to free system resources. You manually call a function 
 on the object to free those resources when you're done with it.
I'm sorry, but I almost can't believe you're saying this. So, you're saying you want me to just revert back to manual resource management and accept that huge resources like textures and such may just leak if someone doesn't use them right? or throws an exception? in a language like D that is supposed to be safe?

 (IIRC) is something like

 using(myObj)
 {
 } // myObj.dispose() is called when exiting this scope
For the THIRD time, I'll post my example: class Texture { } class Texture2D : Texture { this() { /* load texture... */ } ~this { /* free texture */ } // OOPS, when, if ever, will this be called? } Now, does this really seem like a realistic use case to you? using(Texture tex = new Texture2D) { // ... }
 Having the option to have properly ref-counted classes in 
 addition to classes managed by the GC would definitely be an 
 improvement, and I expect that we'll get there at some point, 
 but there _are_ ways to deal with the problem in the interim, 
 even if it's not ideal.
This brings me right back to where I started this, which was asking about this situation. _Is_ it just the interim? Will DIP74 actually ever be implemented? if so, when?
 In most cases though, just don't use classes. In most cases, 
 inheritance is a horrible way to write programs anyway,
Opinion.
 I suspect that far too many folks new to D end up using classes 
 instead of structs just because they're used to
 using classes in C++ or Java or whatever.
I use classes for polymorphism, and although I can't speak for everyone else, I doubt I'm the only one. Bit
Oct 06 2015
next sibling parent reply ponce <contact gam3sfrommars.fr> writes:
On Tuesday, 6 October 2015 at 20:43:42 UTC, bitwise wrote:
 I want polymorphism AND deterministic destruction, and the 
 least you could do is just admit that it's a downside to D not 
 having it, instead of trying to tell me that everything I know 
 is wrong..
This problem comes up again and again, here is an ugly trick to ease the pain: http://p0nce.github.io/d-idioms/#GC-proof-resource-class
Oct 06 2015
parent bitwise <bitwise.pvt gmail.com> writes:
On Tuesday, 6 October 2015 at 20:46:00 UTC, ponce wrote:
 On Tuesday, 6 October 2015 at 20:43:42 UTC, bitwise wrote:
 I want polymorphism AND deterministic destruction, and the 
 least you could do is just admit that it's a downside to D not 
 having it, instead of trying to tell me that everything I know 
 is wrong..
This problem comes up again and again, here is an ugly trick to ease the pain: http://p0nce.github.io/d-idioms/#GC-proof-resource-class
Heh...This only adds insult to injury.... I would have thought the GC was smart enough not to have to do this. Bit
Oct 06 2015
prev sibling next sibling parent ponce <contact gam3sfrommars.fr> writes:
On Tuesday, 6 October 2015 at 20:43:42 UTC, bitwise wrote:
 So, you're saying you want me to just revert back to manual 
 resource management and accept that huge resources like 
 textures and such may just leak if someone doesn't use them 
 right? or throws an exception? in a language like D that is 
 supposed to be safe?
Yes. It seems everyone has this epiphany at one point in their D adventures. C++. On the plus side, you get freedom from thinking about ownership for the 30% or so of resources who only owns memory.
Oct 06 2015
prev sibling next sibling parent reply Jonathan M Davis <jmdavisProg gmx.com> writes:
On Tuesday, 6 October 2015 at 20:43:42 UTC, bitwise wrote:
 On Tuesday, 6 October 2015 at 18:43:42 UTC, Jonathan M Davis 
 wrote:
 For the THIRD time, I'll post my example:

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

 Now, does this really seem like a realistic use case to you?

 using(Texture tex = new Texture2D) {
     // ...
 }
The reality of the matter is that having the lifetime of an object managed by a GC inherently does not work with deterministic destruction. Garbage collectors simply do not work that way. It's a cost to using them. Code like this is what you have to do when you have a garbage collected language without the are stuck with that. And it definitely sucks, but it can work. In D, if you're using only classes, you're still stuck with that. However, if you use structs to wrap classes and do reference counting, then it becomes possible to have deterministic destruction for classes, because the struct is doing the deterministic part, and the lifetime of the class object is no longer managed by the GC (assuming that you don't end up with something like the last of the structs with references to that class object inside of classes which aren't ref-counted, in which case, you lost the deterministic destruction and the GC manages but unfortunately, there are just enough issues with ref-counting structs that to get it fully right, we do need ref-counting in the language (unfortunately, I don't remember all of the corner cases that make that the case). So, ref-counting with structs _mostly_ works, and it is a solution, but it's not quite where we want it to be, which is Walter and Andrei have been talking about adding ref-counting support to the language and DIP 74 was proposed.
 _Is_ it just the interim? Will DIP74 actually ever be 
 implemented? if so, when?
We don't know. It hasn't been officially accepted yet. Walter and Andrei could change their minds and decide that it's not necessary to add ref-counting support to the language. It could be that DIP 74 or something like it ends up being accepted and implemented in a year or three. Or it could be accepted as-is tomorrow. Until Walter and Andrei make a decision on it, we don't know. Given the issues involved, I expect that some form of DIP 74 will be accepted at some point in the future, but they're not going to do that until they're reasonably sure that we've got it right, and that sort of thing is always slow. So, we may very well end up with ref-counting in the language sometime next year, but I'd be shocked if it were this year, and depending on what Walter and Andrei are focusing on, it could be longer than that.
 In most cases though, just don't use classes. In most cases, 
 inheritance is a horrible way to write programs anyway,
Opinion.
It's well known that it's generally better to use composition than inheritance. Inheritance is useful when you need to have runtime polymorphism - where multiple types need to be used in exactly the same code as if they were the same type with the code using them not caring what they really are. Beyond that, it just starts causing problems - especially with regards to reusibility. As soon as code is in a member function, the only way it can be reused is by deriving from the class that it's in, which is incredibly limiting. Free functions (especially templated free functions) cream member functions for flexibility and reusibility, because they aren't restricted to a single type and its descendants. This is especially true when the language does not support multiple inheritance. Inheritance makes sense when it's needed, but when you use it, you're losing flexibility and harming code reusibility, meaning that if it's not actually needed, it's just costing you. And I don't see how anyone can really argue otherwise. Where a lot of that gets debatable is when deciding whether a particular problem actually needs a solution that uses polymorphism, but it's quite clear that using inheritance limits code reusibility.
 I suspect that far too many folks new to D end up using 
 classes instead of structs just because they're used to
 using classes in C++ or Java or whatever.
I use classes for polymorphism, and although I can't speak for everyone else, I doubt I'm the only one.
And that's what D classes should be used for. But based on questions on SO and stuff in D.Learn and other places online, it's pretty clear that at minimum, many folks that are new to D use classes even when they don't need polymorphism. - Jonathan M Davis
Oct 06 2015
parent reply bitwise <bitwise.pvt gmail.com> writes:
On Tuesday, 6 October 2015 at 22:21:41 UTC, Jonathan M Davis 
wrote:

 there are just enough issues with ref-counting structs that to
 get it fully right, we do need ref-counting in the language 
 (unfortunately, I don't remember all of the corner cases that 
 make that the case). So, ref-counting with structs _mostly_ 
 works, and it is a solution, but it's not quite where we want 
 it to be, which is Walter and Andrei have been talking about 
 adding ref-counting support to the language and DIP 74 was 
 proposed.
But these problems still exists: -structs can be nested in classes, and inherit the non-deterministic lifetime of classes. The designer of a library has no control over this. -classes that are meant to be ref counted(meaning that they depend on their destructor being called in a timely fashion) are not guaranteed to be wrapped in a RefCounted or similar RAII object. Again, the designer of a library has no control over this. -again, alias this allows class references to escape their RAII containers and can cause access violations as show here: http://forum.dlang.org/post/zfggjsjmfttbcekqwgjd forum.dlang.org -The syntax is annoying(this is a lot more important that people in this forum usually want to believe) -wrapper structs hide the real type of the object from templates, typeof(), etc.. -etc..
 _Is_ it just the interim? Will DIP74 actually ever be 
 implemented? if so, when?
We don't know. It hasn't been officially accepted yet. Walter and Andrei could change their minds and decide that it's not necessary to add ref-counting support to the language. It could be that DIP 74 or something like it ends up being accepted and implemented in a year or three.
If it takes long enough that C++ has reflection, modules, ranges, stackless coroutines, concepts, etc, then I gotta be honest, I'm gonna start worrying about investing too much time in D.
 I'd be shocked if it were this year, and depending on what
 Walter and Andrei are focusing on, it could be longer than that.
I hope you're wrong. It's one thing to move slow, but looking at DIP74, it doesn't seem to be moving at all(last updated 2015-03-04)
 But based on questions on SO and stuff in D.Learn and other
 places online, it's pretty clear that at minimum, many folks 
 that are
 new to D use classes even when they don't need polymorphism.
Yes, that's why they're asking question in D.Learn and SO... :) There seems to be a general trend in software these days where developers are getting lazy, and saying "But no one is asking for it" and using that to justify leaving features out. This approach doesn't take into account people that just _expect_ certain features to be present because they're so obvious. If you went to a Pizza Hut and they gave you a pizza with no cheese on it "because no one asks for it", would you actually say anything, or just dismiss them offhand and go somewhere else? There is something to be said for tradition. Not everything has to be a democracy.. Bit
Oct 06 2015
next sibling parent Meta <jared771 gmail.com> writes:
On Wednesday, 7 October 2015 at 00:17:37 UTC, bitwise wrote:
 -again, alias this allows class references to escape their RAII 
 containers and can cause access violations as show here:
 http://forum.dlang.org/post/zfggjsjmfttbcekqwgjd forum.dlang.org
This isn't really a problem as it can be easily fixed. It's just that the original writer of Scoped made the mistake of allowing implicit conversion of a Scoped!C to a C, which when you think about it doesn't make any sense and is actually quite dangerous (as my post that you cited shows). All that has to be done to fix that is to disallow implicit conversion to the wrapped type while retaining the interface, which we can do today with a myriad of different methods (mixins, opDispatch, std.typecons.Proxy, etc.). It's just that nobody has done it.
Oct 06 2015
prev sibling parent reply Kagamin <spam here.lot> writes:
On Wednesday, 7 October 2015 at 00:17:37 UTC, bitwise wrote:
 If it takes long enough that C++ has reflection, modules, 
 ranges, stackless coroutines, concepts, etc, then I gotta be 
 honest, I'm gonna start worrying about investing too much time 
 in D.
You manage resources with reference counting in C++? How it deals with circular references?
Oct 08 2015
next sibling parent reply Ola Fosheim =?UTF-8?B?R3LDuHN0YWQ=?= writes:
On Thursday, 8 October 2015 at 10:05:53 UTC, Kagamin wrote:
 On Wednesday, 7 October 2015 at 00:17:37 UTC, bitwise wrote:
 If it takes long enough that C++ has reflection, modules, 
 ranges, stackless coroutines, concepts, etc, then I gotta be 
 honest, I'm gonna start worrying about investing too much time 
 in D.
You manage resources with reference counting in C++? How it deals with circular references?
You have to use weak pointers for back references. http://en.cppreference.com/w/cpp/memory/weak_ptr
Oct 08 2015
parent reply Kagamin <spam here.lot> writes:
On Thursday, 8 October 2015 at 10:15:12 UTC, Ola Fosheim Grøstad 
wrote:
 You have to use weak pointers for back references.

 http://en.cppreference.com/w/cpp/memory/weak_ptr
I suspected as much and the next question is how is it better
Oct 08 2015
parent reply Ola Fosheim =?UTF-8?B?R3LDuHN0YWQ=?= writes:
On Thursday, 8 October 2015 at 10:26:22 UTC, Kagamin wrote:
 On Thursday, 8 October 2015 at 10:15:12 UTC, Ola Fosheim 
 Grøstad wrote:
 You have to use weak pointers for back references.

 http://en.cppreference.com/w/cpp/memory/weak_ptr
I suspected as much and the next question is how is it better
I don't know. I don't like extensive reference counting and don't use weak_ptr. One can usually avoid cycles for resources by design.
Oct 08 2015
parent reply Kagamin <spam here.lot> writes:
On Thursday, 8 October 2015 at 10:34:44 UTC, Ola Fosheim Grøstad 
wrote:
 I don't know. I don't like extensive reference counting and 
 don't use weak_ptr. One can usually avoid cycles for resources 
 by design.
The required design is that a resource handle must reside in a non-resource object, that object must be reference counted too to ensure correctness of resource management, and you can't guarantee reliably that those non-resource objects don't form a cyclic graph. If you must manually verify the graph and put weak references appropriately - what kind of design in that?
Oct 08 2015
parent reply Ola Fosheim =?UTF-8?B?R3LDuHN0YWQ=?= writes:
On Thursday, 8 October 2015 at 11:31:49 UTC, Kagamin wrote:
 cyclic graph. If you must manually verify the graph and put 
 weak references appropriately - what kind of design in that?
It's a system programming language design... If you plan your model before coding it is rather easy to detect cycles in the model. Make the primary data structure a directed acyclic graph, then add back pointers as weak_ptr for secondary relations. I believe you will find the same issues in Objective-C and Swift. Other options: - use regional allocation (free all resources at once) - use a local scanner (trace live resources locally to a data structure, then free the ones that are not referenced).
Oct 08 2015
parent reply Jonathan M Davis <jmdavisProg gmx.com> writes:
On Thursday, 8 October 2015 at 12:10:24 UTC, Ola Fosheim Grøstad 
wrote:
 On Thursday, 8 October 2015 at 11:31:49 UTC, Kagamin wrote:
 cyclic graph. If you must manually verify the graph and put 
 weak references appropriately - what kind of design in that?
It's a system programming language design... If you plan your model before coding it is rather easy to detect cycles in the model. Make the primary data structure a directed acyclic graph, then add back pointers as weak_ptr for secondary relations.
I've programmed extensively in C++ with smart pointers, and in my experience, circular references are rarely a problem. There are some cases where it's obvious that you have one (e.g. where one object owns another and they need to talk to each other), in which case you either use a normal pointer or a weak reference, depending on which makes more sense. And in the cases that you don't catch, you find them in testing, figure out what should be a weak reference to get rid of the circular dependency, you fix it, and you move on. It really isn't a big deal in general, though I suppose that there could be certain ways of designing programs where it would be more problematic. One advantage of using smart pointers with a GC is that the GC can then clean up circular references, and you don't necessarily even need weak pointers (though there are bound to be cases where they'd still be desirable). But the GC isn't required to solve the problem. It just makes it so that if you do end up with a circular reference problem, it'll fix itself. - Jonathan M Davis
Oct 08 2015
next sibling parent reply Ola Fosheim =?UTF-8?B?R3LDuHN0YWQ=?= writes:
On Thursday, 8 October 2015 at 14:13:30 UTC, Jonathan M Davis 
wrote:
 One advantage of using smart pointers with a GC is that the GC 
 can then clean up circular references, and you don't 
 necessarily even need weak pointers (though there are bound to 
 be cases where they'd still be desirable). But the GC isn't 
 required to solve the problem. It just makes it so that if you 
 do end up with a circular reference problem, it'll fix itself.
Yes, in general ownership should not be circular at all. It should be a DAG growing from the current actor/process/stack in an unbroken chain of ownership-references. This should not be conflated with references on the GC heap. In a pure object oriented GC world objects are created and never cease to exist, but when they are no longer reachable the computer can recycle the technical address (but not the conceptual identity). Conceptually automatic garbage collection (like mark/sweep) can be viewed as a memory optimization for our non-ideal computers that would not be needed on a turing machine where you have an endless amount of memory. So I don't think there are _good_ reasons to call finalizers/destructors on the GC heap, it's a sign of a bad model where the responsibilities are unclear. GC objects should only "own" memory on the GC heap. Example: An initial GC file object should just represent the resource identity (URI/filepath), and the open/close actions should be handled by ownership tracking references rooted in the actors/processes that actually are responsible for accessing it. I think the viewpoint that ownership is rooted in actors and not in objects is a better and more performant view than the "finalizer" view. However if the system allows actors to become unreachable the runtime might need a collection cycle for the actors themselves...
Oct 08 2015
next sibling parent Jonathan M Davis <jmdavisProg gmx.com> writes:
On Thursday, 8 October 2015 at 15:51:44 UTC, Ola Fosheim Grøstad 
wrote:
 So I don't think there are _good_ reasons to call 
 finalizers/destructors on the GC heap, it's a sign of a bad 
 model where the responsibilities are unclear. GC objects should 
 only "own" memory on the GC heap.
As far as I can tell, finalizers are for when you either really don't care when a non-GC resource gets cleaned up (which is probably a bad design in any program that isn't fairly small) or for cleaning up after you screw-up and you forget to call the appropriate function on the object to tell it to free those resources (be it call dispose or something else). In general, finalizers are a sign of a problem. If all you have is the GC heap, or if you need to be able to put an object that needs to clean-up a non-GC resource inside of an object on the GC heap, then you're kind of stuck, in which case, you do manual resource cleanup and have a finalizer as backup, but it's definitely a bandaid rather than an ideal solution. The potential need for finalizers is definitely a con to dealing with a GC. - Jonathan M Davis
Oct 08 2015
prev sibling parent Jim Hewes <jimhewes gmail.com> writes:
On 10/8/2015 8:51 AM, Ola Fosheim Grøstad wrote:
 On Thursday, 8 October 2015 at 14:13:30 UTC, Jonathan M Davis wrote:
 Yes, in general ownership should not be circular at all. It should be a
 DAG growing from the current actor/process/stack in an unbroken chain of
 ownership-references.
+1. Reference counting has not been much of an issue for me since using smart pointers because I rarely if ever need std::shared_ptr. It's about ownership and std::unique_ptr works fine for that. (std::shared_ptr gets overused and shouldn't be a substitute for a GC). Parents can own a resource with std::unique_ptr and distribute access to it using native pointers. You just need to be aware of lifetimes. I guess people might consider that having to worry about lifetime management is a disadvantage but I have no problem with it.
Oct 08 2015
prev sibling parent reply Kagamin <spam here.lot> writes:
On Thursday, 8 October 2015 at 14:13:30 UTC, Jonathan M Davis 
wrote:
 I've programmed extensively in C++ with smart pointers, and in 
 my experience, circular references are rarely a problem. There 
 are some cases where it's obvious that you have one (e.g. where 
 one object owns another and they need to talk to each other), 
 in which case you either use a normal pointer or a weak 
 reference, depending on which makes more sense. And in the 
 cases that you don't catch, you find them in testing, figure 
 out what should be a weak reference to get rid of the circular 
 dependency, you fix it, and you move on. It really isn't a big 
 deal in general, though I suppose that there could be certain 
 ways of designing programs where it would be more problematic.
That's all understandable. What's not understandable is when one insists that a necessity to figure out ownership for every non-resource object in C++ is superior to D.
Oct 12 2015
next sibling parent reply Jonathan M Davis <jmdavisProg gmx.com> writes:
On Monday, 12 October 2015 at 08:21:24 UTC, Kagamin wrote:
 On Thursday, 8 October 2015 at 14:13:30 UTC, Jonathan M Davis 
 wrote:
 I've programmed extensively in C++ with smart pointers, and in 
 my experience, circular references are rarely a problem. There 
 are some cases where it's obvious that you have one (e.g. 
 where one object owns another and they need to talk to each 
 other), in which case you either use a normal pointer or a 
 weak reference, depending on which makes more sense. And in 
 the cases that you don't catch, you find them in testing, 
 figure out what should be a weak reference to get rid of the 
 circular dependency, you fix it, and you move on. It really 
 isn't a big deal in general, though I suppose that there could 
 be certain ways of designing programs where it would be more 
 problematic.
That's all understandable. What's not understandable is when one insists that a necessity to figure out ownership for every non-resource object in C++ is superior to D.
If you don't want to care about ownership, then use a GC. The only other memory management model that I can think of where you don't have to care about ownership is when everything is a value type on the stack, so there's nothing to own. There are pros and cons to using a GC, and there are pros and cons to use reference counting everything on the heap. I don't think that either is objectively superior. It all depends on what you're trying to do and what your requirements are. - Jonathan M Davis
Oct 12 2015
parent reply deadalnix <deadalnix gmail.com> writes:
On Monday, 12 October 2015 at 08:49:24 UTC, Jonathan M Davis 
wrote:
 On Monday, 12 October 2015 at 08:21:24 UTC, Kagamin wrote:
 On Thursday, 8 October 2015 at 14:13:30 UTC, Jonathan M Davis 
 wrote:
 I've programmed extensively in C++ with smart pointers, and 
 in my experience, circular references are rarely a problem. 
 There are some cases where it's obvious that you have one 
 (e.g. where one object owns another and they need to talk to 
 each other), in which case you either use a normal pointer or 
 a weak reference, depending on which makes more sense. And in 
 the cases that you don't catch, you find them in testing, 
 figure out what should be a weak reference to get rid of the 
 circular dependency, you fix it, and you move on. It really 
 isn't a big deal in general, though I suppose that there 
 could be certain ways of designing programs where it would be 
 more problematic.
That's all understandable. What's not understandable is when one insists that a necessity to figure out ownership for every non-resource object in C++ is superior to D.
If you don't want to care about ownership, then use a GC. The only other memory management model that I can think of where you don't have to care about ownership is when everything is a value type on the stack, so there's nothing to own. There are pros and cons to using a GC, and there are pros and cons to use reference counting everything on the heap. I don't think that either is objectively superior. It all depends on what you're trying to do and what your requirements are. - Jonathan M Davis
Well technically, there is still ownership: the GC owns everything. But yeah, you don't need to care about for this very reason.
Oct 12 2015
parent Ola Fosheim =?UTF-8?B?R3LDuHN0YWQ=?= writes:
On Monday, 12 October 2015 at 08:59:55 UTC, deadalnix wrote:
 Well technically, there is still ownership: the GC owns 
 everything. But yeah, you don't need to care about for this 
 very reason.
Only if you have finalizers. Without finalization it is better to think of the GC as a memory optimization that is transparent to the semantics of the program. It basically recycles identities (addresses) allowing us to "pretend" that they are unique by checking all relevant used identities (pointers). In some distributed contexts recycling identities is not worth the trouble... so you just use a big space (e.g. 64 bits)... But in most settings you get practically the same result with a region allocator at the entry point of an event loop as with a GC. It uses more memory than explicit management, but so does GC. The GC is usually more robust, but a GC can tank too if we forget to null out pointers (by running out of memory...). Tradeoffs across the board.
Oct 12 2015
prev sibling parent reply Ola Fosheim =?UTF-8?B?R3LDuHN0YWQ=?= writes:
On Monday, 12 October 2015 at 08:21:24 UTC, Kagamin wrote:
 That's all understandable. What's not understandable is when 
 one insists that a necessity to figure out ownership for every 
 non-resource object in C++ is superior to D.
You don't have to, you can use a region allocator.
Oct 12 2015
parent reply Kagamin <spam here.lot> writes:
On Monday, 12 October 2015 at 09:01:54 UTC, Ola Fosheim Grøstad 
wrote:
 You don't have to, you can use a region allocator.
I wrote one and use it in my code for everything. I guess the conclusion is that C++ programmers used to C++ way of resource management and don't want to change their habits. But if D is going to support all C++ idioms... hmm...
Oct 14 2015
parent reply Ola Fosheim =?UTF-8?B?R3LDuHN0YWQ=?= writes:
On Wednesday, 14 October 2015 at 16:12:59 UTC, Kagamin wrote:
 On Monday, 12 October 2015 at 09:01:54 UTC, Ola Fosheim Grøstad 
 wrote:
 You don't have to, you can use a region allocator.
I wrote one and use it in my code for everything. I guess the conclusion is that C++ programmers used to C++ way of resource management and don't want to change their habits. But if D is going to support all C++ idioms... hmm...
Yeah, well, not even sure what C++ idioms are? The ones that a sold on slides at CppCon, or the ones we find at Github? :-) The tricky bit is that D going to support interfacing with C++, that kind of locks D to C++'s memory model...
Oct 14 2015
parent Kagamin <spam here.lot> writes:
On Wednesday, 14 October 2015 at 16:24:25 UTC, Ola Fosheim 
Grøstad wrote:
 Yeah, well, not even sure what C++ idioms are? The ones that a 
 sold on slides at CppCon, or the ones we find at Github? :-)
Off the top of my head: 1) polymorphic value types 2) references in fields 3) value type strings and stl containers 4) multiple inheritance BTW how one does polymorphism and introspection with shared_ptr in C++?
Oct 15 2015
prev sibling parent bitwise <bitwise.pvt gmail.com> writes:
On Thursday, 8 October 2015 at 10:05:53 UTC, Kagamin wrote:
 On Wednesday, 7 October 2015 at 00:17:37 UTC, bitwise wrote:
 If it takes long enough that C++ has reflection, modules, 
 ranges, stackless coroutines, concepts, etc, then I gotta be 
 honest, I'm gonna start worrying about investing too much time 
 in D.
You manage resources with reference counting in C++? How it deals with circular references?
Resources don't generally own each other, so this isn't really an issue. I had code which would request a resources from a shared store, and then the shared store would keep a weak-pointer to the resource and give out shared pointers to whoever asked. When no one was using the resource anymore, it would be freed automatically. Bit
Oct 08 2015
prev sibling next sibling parent reply Paulo Pinto <pjmlp progtools.org> writes:
On Tuesday, 6 October 2015 at 20:43:42 UTC, bitwise wrote:
 On Tuesday, 6 October 2015 at 18:43:42 UTC, Jonathan M Davis 
 wrote:
 On Tuesday, 6 October 2015 at 18:10:42 UTC, bitwise wrote:
 On Tuesday, 6 October 2015 at 17:20:39 UTC, Jonathan M Davis 
 wrote:
 I'm not sure what else I can say. The example I posted says 

 lower the bar because of their mistakes? ;)
It's a side effect of having the lifetime of an object managed by the GC. There's no way around that except to use something else like manual memory management or reference counting.
You are literally repeating what I just said in different words.
 in D, it's a good reason to use structs to manage resources 
 like that, and since most objects really have no need of 
 inheritance and have no business being classes, it's usually 
 fine.
This is an opinion. I want polymorphism AND deterministic destruction, and the least you could do is just admit that it's a downside to D not having it, instead of trying to tell me that everything I know is wrong..
 But in the cases where you do have to use a class, it can get 
 annoying.
YES, its does, and it's not just an odd case here and there..
 You simply do not rely on the GC or the destruction of the 
 object to free system resources. You manually call a function 
 on the object to free those resources when you're done with it.
I'm sorry, but I almost can't believe you're saying this. So, you're saying you want me to just revert back to manual resource management and accept that huge resources like textures and such may just leak if someone doesn't use them right? or throws an exception? in a language like D that is supposed to be safe?

 (IIRC) is something like

 using(myObj)
 {
 } // myObj.dispose() is called when exiting this scope
For the THIRD time, I'll post my example: class Texture { } class Texture2D : Texture { this() { /* load texture... */ } ~this { /* free texture */ } // OOPS, when, if ever, will this be called? } Now, does this really seem like a realistic use case to you? using(Texture tex = new Texture2D) { // ... }
using (LevelManager mgr = new LevelManager()) { //.... // Somewhere in the call stack Texture text = mgr.getTexture(); } --> All level resources gone that require manual management gone --> Ask the GC to collect the remaining memory right now If not level wide, than maybe scene/section wide. However I do get that not all architectures are amendable to be re-written in a GC friendly way. But the approach is similar to RAII in C++, reduce new to minimum and allocate via factory functions that work together with handle manager classes. -- Paulo
Oct 07 2015
next sibling parent reply ponce <contact gam3sfrommars.fr> writes:
On Wednesday, 7 October 2015 at 07:24:03 UTC, Paulo Pinto wrote:


 using (LevelManager mgr = new LevelManager())
 {
      //....
      // Somewhere in the call stack
      Texture text = mgr.getTexture();
 }
 --> All level resources gone that require manual management gone
 --> Ask the GC to collect the remaining memory right now

 If not level wide, than maybe scene/section wide.

 However I do get that not all architectures are amendable to be 
 re-written in a GC friendly way.

 But the approach is similar to RAII in C++, reduce new to 
 minimum and allocate via factory functions that work together 
 with handle manager classes.

 --
 Paulo
This is similar to Scoped!T in D. But this is not composable either. You cannot have a "using()" field in a class object, much like implement IDispose interface AFAIK.
Oct 07 2015
next sibling parent Paulo Pinto <pjmlp progtools.org> writes:
On Wednesday, 7 October 2015 at 07:35:05 UTC, ponce wrote:
 On Wednesday, 7 October 2015 at 07:24:03 UTC, Paulo Pinto wrote:


 using (LevelManager mgr = new LevelManager())
 {
      //....
      // Somewhere in the call stack
      Texture text = mgr.getTexture();
 }
 --> All level resources gone that require manual management 
 gone
 --> Ask the GC to collect the remaining memory right now

 If not level wide, than maybe scene/section wide.

 However I do get that not all architectures are amendable to 
 be re-written in a GC friendly way.

 But the approach is similar to RAII in C++, reduce new to 
 minimum and allocate via factory functions that work together 
 with handle manager classes.

 --
 Paulo
This is similar to Scoped!T in D. But this is not composable either. You cannot have a "using()" field in a class object, much like implement IDispose interface AFAIK.
If you reduce everything to just using(), yes you are right. However, with a bit of functional programming flavor you don't really need to implement IDispose. Just have a wrapper function own the resource. withLevelManager (mgr => { //.. Texture text = mgr.getTexture(); }); And when one is able to use languages that offer syntax sugar for closures as last parameter, it can be improved to withLevelManager { //.. Texture text = it.getTexture(); }; No need to implement any interface, just like a RAII handler implementation in C++. Of course, this assumes all resources that were allocated via the level manager are going to die after the scope ends. If any reference to any of them is kept somewhere else, then something bad will happen when it gets eventually used again. Unless I am missing something, at least in the GC languages I am used to, there isn't a problem with the member fields as long all resources that require deterministic release follow a similar pattern. Like with the _ptr<>() classes in C++, new should only exist in the deepest layers for such classes. I guess a problem with D is the bugs that interactions between classes and structs still have. -- Paulo
Oct 07 2015
prev sibling parent rumbu <rumbu rumbu.ro> writes:
On Wednesday, 7 October 2015 at 07:35:05 UTC, ponce wrote:
 On Wednesday, 7 October 2015 at 07:24:03 UTC, Paulo Pinto wrote:


 using (LevelManager mgr = new LevelManager())
 {
      //....
      // Somewhere in the call stack
      Texture text = mgr.getTexture();
 }
 --> All level resources gone that require manual management 
 gone
 --> Ask the GC to collect the remaining memory right now

 If not level wide, than maybe scene/section wide.

 However I do get that not all architectures are amendable to 
 be re-written in a GC friendly way.

 But the approach is similar to RAII in C++, reduce new to 
 minimum and allocate via factory functions that work together 
 with handle manager classes.

 --
 Paulo
This is similar to Scoped!T in D. But this is not composable either. You cannot have a "using()" field in a class object, much like implement IDispose interface AFAIK.
You must implement IDisposable. using(obj = new Obj()) { obj.DoSomething(); } obj = new Obj(); try { obj.DoSomething(); } finally { if (obj != null) (IDisposable)obj.Dispose(); } This can be easily translated to D using a delegate instead of the using block, if someone is keen to use this pattern. More than that, due to TLS, there is no need to worry about thread safety in this case. Resources are discarded in Dispose(), the class destructor is just calling Dispose().
Oct 07 2015
prev sibling parent reply bitwise <bitwise.pvt gmail.com> writes:
On Wednesday, 7 October 2015 at 07:24:03 UTC, Paulo Pinto wrote:
 On Tuesday, 6 October 2015 at 20:43:42 UTC, bitwise wrote:
 On Tuesday, 6 October 2015 at 18:43:42 UTC, Jonathan M Davis 
 wrote:
 On Tuesday, 6 October 2015 at 18:10:42 UTC, bitwise wrote:
 On Tuesday, 6 October 2015 at 17:20:39 UTC, Jonathan M Davis 
 wrote:
 I'm not sure what else I can say. The example I posted says 

 lower the bar because of their mistakes? ;)
It's a side effect of having the lifetime of an object managed by the GC. There's no way around that except to use something else like manual memory management or reference counting.
You are literally repeating what I just said in different words.
 in D, it's a good reason to use structs to manage resources 
 like that, and since most objects really have no need of 
 inheritance and have no business being classes, it's usually 
 fine.
This is an opinion. I want polymorphism AND deterministic destruction, and the least you could do is just admit that it's a downside to D not having it, instead of trying to tell me that everything I know is wrong..
 But in the cases where you do have to use a class, it can get 
 annoying.
YES, its does, and it's not just an odd case here and there..
 You simply do not rely on the GC or the destruction of the 
 object to free system resources. You manually call a function 
 on the object to free those resources when you're done with 
 it.
I'm sorry, but I almost can't believe you're saying this. So, you're saying you want me to just revert back to manual resource management and accept that huge resources like textures and such may just leak if someone doesn't use them right? or throws an exception? in a language like D that is supposed to be safe?

 (IIRC) is something like

 using(myObj)
 {
 } // myObj.dispose() is called when exiting this scope
For the THIRD time, I'll post my example: class Texture { } class Texture2D : Texture { this() { /* load texture... */ } ~this { /* free texture */ } // OOPS, when, if ever, will this be called? } Now, does this really seem like a realistic use case to you? using(Texture tex = new Texture2D) { // ... }
using (LevelManager mgr = new LevelManager()) { //.... // Somewhere in the call stack Texture text = mgr.getTexture(); } --> All level resources gone that require manual management gone --> Ask the GC to collect the remaining memory right now If not level wide, than maybe scene/section wide. However I do get that not all architectures are amendable to be re-written in a GC friendly way. But the approach is similar to RAII in C++, reduce new to minimum and allocate via factory functions that work together with handle manager classes. -- Paulo
Still no ;) It's a Texture. It's meant to be seen on the screen for a while, not destroyed in the same scope which it was created. In games though, we have a scene graph. When things happen, we often chip off a large part of it while the game is running, discard it, and load something new. We need to know that what we just discarded has been destroyed completely before we start loading new stuff when we're heavily constrained by memory. And even in cases where we aren't that constrained by memory, we need to know things have been destroyed, period, for non-memory resources. Also, when using graphics APIs like OpenGL, we need control over which thread an object is destroyed in, because you can't access OpenGL resources from just any thread. Now, you could set up some complicated queue where you send textures and so on to(latently) be destroyed, but this is just complicated. Picture a Hello OpenGL app in D and the hoops some noob would have to jump through. It's bad news. Also, I should add, that a better example of the Texture thing would be a regular Texture and a RenderTexture. You can only draw to the RenderTexture, but you should be able to apply both to a primitive for drawing. You need polymorphism for this. A struct will not do. Bit
Oct 07 2015
parent reply Paulo Pinto <pjmlp progtools.org> writes:
On Wednesday, 7 October 2015 at 12:56:32 UTC, bitwise wrote:
 On Wednesday, 7 October 2015 at 07:24:03 UTC, Paulo Pinto wrote:
 On Tuesday, 6 October 2015 at 20:43:42 UTC, bitwise wrote:
[...]
using (LevelManager mgr = new LevelManager()) { //.... // Somewhere in the call stack Texture text = mgr.getTexture(); } --> All level resources gone that require manual management gone --> Ask the GC to collect the remaining memory right now If not level wide, than maybe scene/section wide. However I do get that not all architectures are amendable to be re-written in a GC friendly way. But the approach is similar to RAII in C++, reduce new to minimum and allocate via factory functions that work together with handle manager classes. -- Paulo
Still no ;) It's a Texture. It's meant to be seen on the screen for a while, not destroyed in the same scope which it was created. In games though, we have a scene graph. When things happen, we often chip off a large part of it while the game is running, discard it, and load something new. We need to know that what we just discarded has been destroyed completely before we start loading new stuff when we're heavily constrained by memory. And even in cases where we aren't that constrained by memory, we need to know things have been destroyed, period, for non-memory resources. Also, when using graphics APIs like OpenGL, we need control over which thread an object is destroyed in, because you can't access OpenGL resources from just any thread. Now, you could set up some complicated queue where you send textures and so on to(latently) be destroyed, but this is just complicated. Picture a Hello OpenGL app in D and the hoops some noob would have to jump through. It's bad news. Also, I should add, that a better example of the Texture thing would be a regular Texture and a RenderTexture. You can only draw to the RenderTexture, but you should be able to apply both to a primitive for drawing. You need polymorphism for this. A struct will not do. Bit
I guess you misunderstood the // Somewhere in the call stack It is meant as the logical region where that scene graph block you refer to is valid. As for OpenGL being complex, fear not, Vulkan, Metal and DX 12 are here to help (ca 600 LOC for a triangle). :) And both Java and .NET do offer support such type of queues as well. Anyway I was just explaining what is possible when one embraces the tools GC languages offer. In general, I advocate any form of automatic memory/resource management. With substructural type systems now being my favorite, but they still have an uphill battle for adoption. Also as a note, Microsoft will be discussing their proposed C++ solution with the Rust team. -- Paulo
Oct 07 2015
next sibling parent reply Ola Fosheim =?UTF-8?B?R3LDuHN0YWQ=?= writes:
On Wednesday, 7 October 2015 at 13:15:11 UTC, Paulo Pinto wrote:
 In general, I advocate any form of automatic memory/resource 
 management. With substructural type systems now being my 
 favorite, but they still have an uphill battle for adoption.
Are you thinking about Rust, or some other language?
 Also as a note, Microsoft will be discussing their proposed C++ 
 solution with the Rust team.
Are you thinking about more lintish tools that can give false positives, or something with guarantees that can be a language feature?
Oct 07 2015
parent reply Paulo Pinto <pjmlp progtools.org> writes:
On Wednesday, 7 October 2015 at 15:42:57 UTC, Ola Fosheim Grøstad 
wrote:
 On Wednesday, 7 October 2015 at 13:15:11 UTC, Paulo Pinto wrote:
 In general, I advocate any form of automatic memory/resource 
 management. With substructural type systems now being my 
 favorite, but they still have an uphill battle for adoption.
Are you thinking about Rust, or some other language?
All of the ones that explore this area. Rust, ATS, Idris, F*....
 Also as a note, Microsoft will be discussing their proposed 
 C++ solution with the Rust team.
Are you thinking about more lintish tools that can give false positives, or something with guarantees that can be a language feature?
What Herb Sutter demoed at CppCon as compiler validation to CoreC++. I can imagine that depending on how well the community takes those guidelines, they might become part of C++20. On the other hand, on Herb's talk around 1% of the audience acknowledged the use of static analysers. Pretty much in sync what I see in enterprise developers.
Oct 07 2015
next sibling parent reply Atila Neves <atila.neves gmail.com> writes:
On Wednesday, 7 October 2015 at 17:02:51 UTC, Paulo Pinto wrote:
 On Wednesday, 7 October 2015 at 15:42:57 UTC, Ola Fosheim 
 Grøstad wrote:
 On Wednesday, 7 October 2015 at 13:15:11 UTC, Paulo Pinto 
 wrote:
 In general, I advocate any form of automatic memory/resource 
 management. With substructural type systems now being my 
 favorite, but they still have an uphill battle for adoption.
Are you thinking about Rust, or some other language?
All of the ones that explore this area. Rust, ATS, Idris, F*....
 Also as a note, Microsoft will be discussing their proposed 
 C++ solution with the Rust team.
Are you thinking about more lintish tools that can give false positives, or something with guarantees that can be a language feature?
What Herb Sutter demoed at CppCon as compiler validation to CoreC++. I can imagine that depending on how well the community takes those guidelines, they might become part of C++20. On the other hand, on Herb's talk around 1% of the audience acknowledged the use of static analysers. Pretty much in sync what I see in enterprise developers.
The CppCon demos were impressive, but I'm dying to see how Microsoft's analyser works out in real life. I've seen too many tools with too many false positives to be useful, and I'm sceptical that a library solution is all it takes to make C++ safe. As I asked Bjarne after his keynote, if it were that easy, why does Rust exist? Atila
Oct 07 2015
parent Paulo Pinto <pjmlp progtools.org> writes:
On Wednesday, 7 October 2015 at 17:22:49 UTC, Atila Neves wrote:
 On Wednesday, 7 October 2015 at 17:02:51 UTC, Paulo Pinto wrote:
 On Wednesday, 7 October 2015 at 15:42:57 UTC, Ola Fosheim 
 Grøstad wrote:
 On Wednesday, 7 October 2015 at 13:15:11 UTC, Paulo Pinto 
 wrote:
 In general, I advocate any form of automatic memory/resource 
 management. With substructural type systems now being my 
 favorite, but they still have an uphill battle for adoption.
Are you thinking about Rust, or some other language?
All of the ones that explore this area. Rust, ATS, Idris, F*....
 Also as a note, Microsoft will be discussing their proposed 
 C++ solution with the Rust team.
Are you thinking about more lintish tools that can give false positives, or something with guarantees that can be a language feature?
What Herb Sutter demoed at CppCon as compiler validation to CoreC++. I can imagine that depending on how well the community takes those guidelines, they might become part of C++20. On the other hand, on Herb's talk around 1% of the audience acknowledged the use of static analysers. Pretty much in sync what I see in enterprise developers.
The CppCon demos were impressive, but I'm dying to see how Microsoft's analyser works out in real life. I've seen too many tools with too many false positives to be useful, and I'm sceptical that a library solution is all it takes to make C++ safe. As I asked Bjarne after his keynote, if it were that easy, why does Rust exist? Atila
I would say the answer to that question was present in both Bjarne and Herb's talks. Most developers don't care, specially in the enterprise space, they will keep on using what they learned until something new forces them to change their habits. Hence why almost no one answered that they were using static analysers on Herb's talk and Bjarne had that slide about C++11 and C++14 being ignored. I always see C with a C++ compiler or C with Classes idoms. Hence why I happily live in Java/.NET land with the occasional trip to the C++ cousin. Still, as a language geek, I find their work quite interesting.
Oct 07 2015
prev sibling parent reply Ola Fosheim =?UTF-8?B?R3LDuHN0YWQ=?= writes:
On Wednesday, 7 October 2015 at 17:02:51 UTC, Paulo Pinto wrote:
 On Wednesday, 7 October 2015 at 15:42:57 UTC, Ola Fosheim 
 Grøstad wrote:
 Are you thinking about Rust, or some other language?
All of the ones that explore this area. Rust, ATS, Idris, F*....
Oh, yeah, sure. I wondered more if you were looking to adopt a language with substructural typing (beyond library types like unique_ptr) for production. I personally think that they future is with actor-based programming in combination with substructural/behavioural typing since it lends itself to distributed computing, multi core etc. The challenge is making a good language for it that is sufficiently performant and still allows breaking out actors to other computational units (computers/CPUs). But yeah, I think there is a paradigm shift coming in ~10-15 years maybe?
 Are you thinking about more lintish tools that can give false 
 positives, or something with guarantees that can be a language 
 feature?
What Herb Sutter demoed at CppCon as compiler validation to CoreC++.
I've only seen the talks on youtube. I was under the impression that Microsoft had accurate and inaccurate analysers, but that the accurate ones were too slow on current C++ code bases. With more annotations to guide the analyser... yes, maybe. I assume Microsoft use analysers based on Boogie: http://research.microsoft.com/en-us/projects/boogie/
 I can imagine that depending on how well the community takes 
 those guidelines, they might become part of C++20.
I think this is needed, but adoption probably won't happen without IDE benefits.
Oct 08 2015
next sibling parent reply Paulo Pinto <pjmlp progtools.org> writes:
On Thursday, 8 October 2015 at 11:15:35 UTC, Ola Fosheim Grøstad 
wrote:
 On Wednesday, 7 October 2015 at 17:02:51 UTC, Paulo Pinto wrote:
 On Wednesday, 7 October 2015 at 15:42:57 UTC, Ola Fosheim 
 Grøstad wrote:
 Are you thinking about Rust, or some other language?
All of the ones that explore this area. Rust, ATS, Idris, F*....
Oh, yeah, sure. I wondered more if you were looking to adopt a language with substructural typing (beyond library types like unique_ptr) for production.
Not a chance. In my little universe it is all about JVM and .NET languages, JavaScript for the browser and C++ for when there isn't any other way. Those languages is where I am having fun now, besides mobile coding. But it is just dabbling and reading papers about them, nothing serious. It is really hard to keep up with JVM, .NET and occasional look into C++. The languages are easy when compared to the whole ecosystem, hence why I went silent. Just decided to comment, as an explanation of what resource management is possible in modern versions of Java/.NET.
 I personally think that they future is with actor-based 
 programming in combination with substructural/behavioural 
 typing since it lends itself to distributed computing, multi 
 core etc. The challenge is making a good language for it that 
 is sufficiently performant and still allows breaking out actors 
 to other computational units (computers/CPUs).
Microsoft Research had an actor-based language for awhile, Axum. Many of the ideas are now in .NET. http://blogs.msdn.com/b/maestroteam/archive/2011/02/28/the-state-of-axum.aspx https://en.wikipedia.org/wiki/Axum_%28programming_language%29 http://download.microsoft.com/download/B/D/5/BD51FFB2-C777-43B0-AC24-BDE3C88E231F/Axum%20Programmers%20Guide.pdf
 But yeah, I think there is a paradigm shift coming in ~10-15 
 years maybe?
Substructural typing for sure as it is coming slowly mainstream now. Several years ago it was just Agda, now there are several projects, including companies like Microsoft looking at it.
 Are you thinking about more lintish tools that can give false 
 positives, or something with guarantees that can be a 
 language feature?
What Herb Sutter demoed at CppCon as compiler validation to CoreC++.
I've only seen the talks on youtube. I was under the impression that Microsoft had accurate and inaccurate analysers, but that the accurate ones were too slow on current C++ code bases. With more annotations to guide the analyser... yes, maybe. I assume Microsoft use analysers based on Boogie: http://research.microsoft.com/en-us/projects/boogie/
They use it on their driver validation tools.
 I can imagine that depending on how well the community takes 
 those guidelines, they might become part of C++20.
I think this is needed, but adoption probably won't happen without IDE benefits.
Yep, but for all of us that aren't shaving off ms or squeezing one more byte into the cache line, it doesn't matter much. -- Paulo
Oct 08 2015
parent Ola Fosheim =?UTF-8?B?R3LDuHN0YWQ=?= writes:
On Thursday, 8 October 2015 at 13:20:51 UTC, Paulo Pinto wrote:
 It is really hard to keep up with JVM, .NET and occasional look 
 into C++. The languages are easy when compared to the whole 
 ecosystem, hence why I went silent.
I've found getting a good understanding of C++11/14 to be a serious time sink, and that's just the language and standard library, which is pretty small. So I can understand those who just stick to a conservative C++ subset.
 Just decided to comment, as an explanation of what resource 
 management is possible in modern versions of Java/.NET.
Thanks :-)
Oct 08 2015
prev sibling parent reply Jim Hewes <jimhewes gmail.com> writes:
On 10/8/2015 4:15 AM, Ola Fosheim Grøstad wrote:
 I personally think that they future is with actor-based programming in
 combination with substructural/behavioural typing since it lends itself
 to distributed computing, multi core etc.
I've recently become curious about the actor model and would like to learn more about it and maybe play around with it a bit. The C++ Actor Framework looks good, but unfortunately it doesn't yet work with MSVC so I'm waiting for that.
 The challenge is making a good
 language for it that is sufficiently performant and still allows
 breaking out actors to other computational units (computers/CPUs).
I read a comment* on stack overflow that mentions that the important component is actually environment/platform such as OTP is for Erlang. I'm not sure how this affects the C++ actor implementations. *The last answer at: http://stackoverflow.com/questions/8107612/the-actor-model-why-is-erlang-special-or-why-do-you-need-another-language-for
Oct 08 2015
parent reply Ola Fosheim =?UTF-8?B?R3LDuHN0YWQ=?= writes:
On Thursday, 8 October 2015 at 18:19:51 UTC, Jim Hewes wrote:
 I've recently become curious about the actor model and would 
 like to learn more about it and maybe play around with it a 
 bit. The C++ Actor Framework looks good, but unfortunately it 
 doesn't yet work with MSVC so I'm waiting for that.
Yes, there are libraries, but for it to be pleasant I think language support is needed. I've linked to this video before, but it is quite entertaining if you haven't seen it yet: https://channel9.msdn.com/Shows/Going+Deep/Hewitt-Meijer-and-Szyperski-The-Actor-Model-everything-you-wanted-to-know-but-were-afraid-to-ask Carl Hewitt stresses the difference from other concurrency models that use non-determinism by pointing out that a key quality with actor based systems is indeterminism. (i.e. that things may simply evaporate without notice). Wikipedia has a rather lengthy page on Actors: https://en.wikipedia.org/wiki/Actor_model
 I read a comment* on stack overflow that mentions that the 
 important component is actually environment/platform such as 
 OTP is for Erlang.
 I'm not sure how this affects the C++ actor implementations.
I don't know much about C++ actor frame works, but if you only want to play with actors then you could give pony-lang.org a spin. Not sure if they really focus on indeterminism, though. But an interesting property of using actors is that you (in theory) could scale up by migrating actors to other compute nodes. Or run programs on many core CPUs and just keep going even if one core crashes. If indeterminism is assumed then you have to design for unstable communication between actors with more robustness as an outcome (hopefully).
Oct 08 2015
parent reply Jim Hewes <jimhewes gmail.com> writes:
On 10/8/2015 11:56 AM, Ola Fosheim Grøstad wrote:
 On Thursday, 8 October 2015 at 18:19:51 UTC, Jim Hewes wrote:

 Yes, there are libraries, but for it to be pleasant I think language
 support is needed. I've linked to this video before, but it is quite
 entertaining if you haven't seen it yet:

 https://channel9.msdn.com/Shows/Going+Deep/Hewitt-Meijer-and-Szyperski-The-Actor-Model-everything-you-wanted-to-know-but-were-afraid-to-ask
Yeah, I watched that after I saw your other post with the link. Thanks. One early question that I have (that someone else also asked in a comment below the video) is about design and the granularity of actors. What sorts of things do you define as an actor? How big does an entity need to be before you should make it an actor?
 Carl Hewitt stresses the difference from other concurrency models that
 use non-determinism by pointing out that a key quality with actor based
 systems is indeterminism. (i.e. that things may simply evaporate without
 notice).
Another thing I noted was when he said once you get to 1000 cores the programmer knows nothing about the environment. So rather than figure out new ways to manage these things like locks and threads, it makes sense to get to a more abstract level where you don't even deal with it at all anymore.
 I don't know much about C++ actor frame works, but if you only want to
 play with actors then you could give pony-lang.org a spin. Not sure if
 they really focus on indeterminism, though.
Thanks! I'll give it a look. It's probably good for playing around with to get a feel for it.
Oct 09 2015
parent Ola Fosheim =?UTF-8?B?R3LDuHN0YWQ=?= writes:
On Friday, 9 October 2015 at 20:16:42 UTC, Jim Hewes wrote:
 Yeah, I watched that after I saw your other post with the link. 
 Thanks. One early question that I have (that someone else also 
 asked in a comment below the video) is about design and the 
 granularity of actors. What sorts of things do you define as an 
 actor? How big does an entity need to be before you should make 
 it an actor?
Well, I think Hewitt wants actors to be a foundational concept, but then again this is his baby so he might take it all the way, just to explore the territory? I've browsed his paper on ActorScript, which unfortunately doesn't look like a very user-friendly language IMO, but he apparently thinks that an Actor could cover just a single expression if you want to. I also think he is playing with a different type of logic that covers uncertainty. https://hal.archives-ouvertes.fr/hal-01147821/document But... maybe it is more reasonable to use larger units that represent something in the system we try to model? (Connection, Account, and so on?)
 Another thing I noted was when he said once you get to 1000 
 cores the programmer knows nothing about the environment. So 
 rather than figure out new ways to manage these things like 
 locks and threads, it makes sense to get to a more abstract 
 level where you don't even deal with it at all anymore.
Yes, just have actors and very efficient message passing and remove all notions of cores and threads. I think this is where Pony is heading too. There was a kind of interesting presentation by Andreas Olofsson from Adapteva on an Erlang conference I saw on youtube where he talks a bit about a 1000 core CPU he is designing (manufacturing is probably a different matter). Each core has 64KiB of local memory, that's what is called a "scratchpad" which replace traditional caching so you can get fast memory closer to the core. https://www.youtube.com/watch?v=WGXPFPKQC2o
 Thanks! I'll give it a look. It's probably good for playing 
 around with to get a feel for it.
I think the Pony feature set is kind of interesting, they are trying to be innovative. Pony is probably not usable for anything serious at this stage, but they appear to have proved the type system to be sound formally so it is interesting to look at, I think. What they try to do makes sense.
Oct 09 2015
prev sibling parent bitwise <bitwise.pvt gmail.com> writes:
On Wednesday, 7 October 2015 at 13:15:11 UTC, Paulo Pinto wrote:
 On Wednesday, 7 October 2015 at 12:56:32 UTC, bitwise wrote:
 On Wednesday, 7 October 2015 at 07:24:03 UTC, Paulo Pinto 
 wrote:
 On Tuesday, 6 October 2015 at 20:43:42 UTC, bitwise wrote:
[...]
using (LevelManager mgr = new LevelManager()) { //.... // Somewhere in the call stack Texture text = mgr.getTexture(); } --> All level resources gone that require manual management gone --> Ask the GC to collect the remaining memory right now If not level wide, than maybe scene/section wide. However I do get that not all architectures are amendable to be re-written in a GC friendly way. But the approach is similar to RAII in C++, reduce new to minimum and allocate via factory functions that work together with handle manager classes. -- Paulo
Still no ;) It's a Texture. It's meant to be seen on the screen for a while, not destroyed in the same scope which it was created. In games though, we have a scene graph. When things happen, we often chip off a large part of it while the game is running, discard it, and load something new. We need to know that what we just discarded has been destroyed completely before we start loading new stuff when we're heavily constrained by memory. And even in cases where we aren't that constrained by memory, we need to know things have been destroyed, period, for non-memory resources. Also, when using graphics APIs like OpenGL, we need control over which thread an object is destroyed in, because you can't access OpenGL resources from just any thread. Now, you could set up some complicated queue where you send textures and so on to(latently) be destroyed, but this is just complicated. Picture a Hello OpenGL app in D and the hoops some noob would have to jump through. It's bad news. Also, I should add, that a better example of the Texture thing would be a regular Texture and a RenderTexture. You can only draw to the RenderTexture, but you should be able to apply both to a primitive for drawing. You need polymorphism for this. A struct will not do. Bit
I guess you misunderstood the // Somewhere in the call stack It is meant as the logical region where that scene graph block you refer to is valid. Anyway I was just explaining what is possible when one embraces the tools GC languages offer.
I still don't think your example exists in real world applications. Typically, you don't have that kind of control over the application's control-flow. You don't really have the option of unwinding the stack when you want to clean up. Most applications these days are event-based. When things are loaded or unloaded, it's usually as a result of some event-callback originating from either an input event, or a display link callback. To clarify, on iOS, you don't have a game loop. You can register a display-link or timer which will call your 'draw' or 'update' function at a fixed interval. On top of this, you just can't rely on a strict hierarchical ownership of resources like this. large bundles of resources may be loaded/unloaded in any order, at any time.
 And both Java and .NET do offer support such type of queues as 
 well.
I was actually thinking about this. If D had a standard runloop of some sort(like NSRunLoop/performSelectorOnThread: for iOS/OSX) then it would make queueing things to other threads a little easier. I suppose D's receive() API could be used to make something a little more specialized. But although this would allow classes to delegate the destruction of resources to the correct thread, it wouldn't resolve the problem that those destruction commands will still only be delegated if/when a classes destructor is actually called.
 In general, I advocate any form of automatic memory/resource 
 management.
+1 :)
Oct 07 2015
prev sibling parent Adrian Matoga <epi atari8.info> writes:
On Tuesday, 6 October 2015 at 20:43:42 UTC, bitwise wrote:
 For the THIRD time, I'll post my example:

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

 Now, does this really seem like a realistic use case to you?

 using(Texture tex = new Texture2D) {
     // ...
 }
It does, entirely. I usually translate it to D this way: { Texture tex = new Texture2D; scope(exit) tex.destroy; // ... } Of course, nothing protects you from passing the reference outside the scope and having a dangling ref after the scope ends, so it's by no means safe, but the destruction is deterministic.
Oct 14 2015
prev sibling parent reply ponce <contact gam3sfrommars.fr> writes:
On Tuesday, 6 October 2015 at 17:20:39 UTC, Jonathan M Davis 
wrote:
 But in general, at this point, with D, if you want 
 deterministic destruction, then you use structs. Classes are 
 not the appropriate place for it. If they were ref-counted, 
 then they could be, but as long as they're not, then classes 
 are not the place to have stuff that cares about deterministic 
 destruction. And if you're stuck with stuff in classes that do 
 care about deterministic destruction, then you have to use the 

 the destructor/finalizer to clean anything up except for the 
 cases where you screw up and forget to manually call the 
 function that does the cleanup.
Unfortunately, it is quite common to need both virtual functions and deterministic destruction. It isn't helpful to disregard the problem by saying "you should have used a struct", in many cases it's not any easier.
Oct 06 2015
next sibling parent Jonathan M Davis <jmdavisProg gmx.com> writes:
On Tuesday, 6 October 2015 at 20:44:22 UTC, ponce wrote:
 On Tuesday, 6 October 2015 at 17:20:39 UTC, Jonathan M Davis 
 wrote:
 But in general, at this point, with D, if you want 
 deterministic destruction, then you use structs. Classes are 
 not the appropriate place for it. If they were ref-counted, 
 then they could be, but as long as they're not, then classes 
 are not the place to have stuff that cares about deterministic 
 destruction. And if you're stuck with stuff in classes that do 
 care about deterministic destruction, then you have to use the 

 the destructor/finalizer to clean anything up except for the 
 cases where you screw up and forget to manually call the 
 function that does the cleanup.
Unfortunately, it is quite common to need both virtual functions and deterministic destruction. It isn't helpful to disregard the problem by saying "you should have used a struct", in many cases it's not any easier.
If you need both polymorphism and determinstic destruction, then you're just plain screwed when a GC is managing the lifetime of an object, because garbage collectors simply don't work that way. And anyone using class objects which are managed by a GC needs to understand that. To get the determinism, you need a way to control the lifetime of an object other than the GC (whether the memory for it lives on the GC heap or not). So, at this point, structs _have_ to get involved to get deterministic destruction unless you're going to do it all manually. And that means that it's better to avoid classes if you don't actually need them, because then you can actually have deterministic destruction (on top of being able to avoid heap allocation if it's not necessary for any member variables). It also means that programs should generally avoid needing to combine polymorphism and deterministic destruction. If you need it, you need it, and you have to either get structs involved as wrappers or manually manage the lifetime of the class objects (or at least manually call a function to tell it to release whatever resources need to be released, even if the object itself isn't actually freed), but if it can reasonably be avoided, it should be avoided. - Jonathan M Davis
Oct 06 2015
prev sibling next sibling parent rsw0x <anonymous anonymous.com> writes:
On Tuesday, 6 October 2015 at 20:44:22 UTC, ponce wrote:
 On Tuesday, 6 October 2015 at 17:20:39 UTC, Jonathan M Davis 
 wrote:
 [...]
Unfortunately, it is quite common to need both virtual functions and deterministic destruction. It isn't helpful to disregard the problem by saying "you should have used a struct", in many cases it's not any easier.
+1 to this and pretty much everything else you've said in this thread. Tired of people just shrugging it off when it's a serious issue.
Oct 06 2015
prev sibling parent Dmitry Olshansky <dmitry.olsh gmail.com> writes:
On 06-Oct-2015 23:44, ponce wrote:
 On Tuesday, 6 October 2015 at 17:20:39 UTC, Jonathan M Davis wrote:

 Unfortunately, it is quite common to need both virtual functions and
 deterministic destruction. It isn't helpful to disregard the problem by
 saying "you should have used a struct", in many cases it's not any easier.
+111 -- Dmitry Olshansky
Oct 06 2015
prev sibling parent reply Marc =?UTF-8?B?U2Now7x0eg==?= <schuetzm gmx.net> writes:
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.
Oct 07 2015
next sibling parent reply Namespace <rswhite4 gmail.com> writes:
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.
Oct 07 2015
next sibling parent reply Jonathan M Davis <jmdavisProg gmx.com> writes:
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
Oct 07 2015
next sibling parent reply Namespace <rswhite4 gmail.com> writes:
 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.
Oct 07 2015
parent reply Jonathan M Davis <jmdavisProg gmx.com> writes:
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
Oct 07 2015
parent reply Namespace <rswhite4 gmail.com> writes:
 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
Oct 07 2015
parent reply Jonathan M Davis <jmdavisProg gmx.com> writes:
On Wednesday, 7 October 2015 at 11:21:04 UTC, Namespace wrote:
 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:
How does that solve anything? The problem is that some types need a deterministic lifetime, and no object whose lifetime is managed by the GC gets a deterministic lifetime. So, even if an object supports deterministic destruction, that stops working as soon as its put inside of something whose lifetime is managed by the GC. Whether the object with deterministic destruction has inheritance or not doesn't really matter, and whether it naturally has deterministic destruction or whether it has it because it's being used in smart pointer doesn't really matter. It's the fact that it was put in an object whose lifetime is managed by the GC that screws with things. Even if D classes were identical to C++ classes, and we had no structs, the fact that we have a GC managing the lifetime of anything causes problems with any type that needs deterministic destruction. What's needed is to have a smart pointer of some kind to manage the lifetime of objects on the heap that need deterministic destruction and then to have the programmer make sure that they do put any of such objects inside of an object whose lifetime is managed by the GC if they want the deterministic destruction to work. You can't get away from requiring the programmer to be smart about things here unless you simply have no GC (which then requires them to smart about other things), or any type with deterministic destruction simply isn't allowed to be on the GC heap, which is unnecessarily limiting, particularly since in many cases, it's perfectly okay to let objects that might normally be destroy deterministically to be destroyed at the GC's leisure. std.typecons.RefCounted aside, it's quite possible as-is to implement smart pointers in D with structs, thus providing deterministic destruction for reference types. I don't know why RefCounted wasn't implemented to work with classes, and I don't know why Walter and Andrei think that something like DIP 74 is necessary to support ref-counting. I might have heard, but if so, I forgot. It probably comes down to a loophole somewhere in how structs are put together that makes it throw off the ref-count (maybe something to do with init values), but that's speculation on my part. Regardless, if we have a proper ref-counting mechanism for classes (built-in or not doesn't necessarily matter - it just needs to work), then the folks that need deterministic destruction with polymorphism get it. But they're always going to have be careful about having such objects on the GC heap due to the nature of garbage collection. - Jonathan M Davis
Oct 07 2015
next sibling parent Ola Fosheim =?UTF-8?B?R3LDuHN0YWQ=?= writes:
On Wednesday, 7 October 2015 at 15:13:40 UTC, Jonathan M Davis 
wrote:
 the GC heap, which is unnecessarily limiting, particularly 
 since in many cases, it's perfectly okay to let objects that 
 might normally be destroy deterministically to be destroyed at 
 the GC's leisure.
This is a costly (in terms of collection) and unreliable approach to resource managment, so it ought to be removed and replaced with something more robust with less overhead.
 speculation on my part. Regardless, if we have a proper 
 ref-counting mechanism for classes (built-in or not doesn't 
 necessarily matter - it just needs to work), then the folks 
 that need deterministic destruction with polymorphism get it.
Proper reference counting generally requires a lot of programmer attention if you want to get what C++ offers. 1. There is a need to add support for weak pointers so you can avoid circular references, this leads to double indirection. 2. There is a need to add support aliasing pointers (pointers to members) that increase the ownership refcount to prevent premature destruction if you retain a pointer to a member. 3. For multi threading you need to have atomics both on the counter and on the pointer (coming in C++17). This is also needed for cache-objects IMO.
Oct 07 2015
prev sibling parent Adam D. Ruppe <destructionator gmail.com> writes:
On Wednesday, 7 October 2015 at 15:13:40 UTC, Jonathan M Davis 
wrote:
 std.typecons.RefCounted aside, it's quite possible as-is to 
 implement smart pointers in D with structs, thus providing 
 deterministic destruction for reference types. I don't know why 
 RefCounted wasn't implemented to work with classes, and I don't 
 know why Walter and Andrei think that something like DIP 74 is 
 necessary to support ref-counting.
There's a lot of hassles with a wrapper, the biggest blocker being how it interacts with inheritance. interface Base {} class Foo : Base {} void test(RefCounted!Base b) {} RefCounted!Foo foo = initialized; test(foo); What happens? Well, you could alias this to Base... but what about to Object? We'd need multiple alias this to get it all working right, and then it'd still be kinda a pain. Also, what about covariant method overriding? Base class returns RefCounted!this and child class does too... will it be accepted? Library refcounting is cool and great for a great many things, but it doesn't play well with classes at all, inheritance just makes it too complicated. The magic of dip74 is that most the language remains the same so those cases are already covered.
Oct 07 2015
prev sibling parent Marc =?UTF-8?B?U2Now7x0eg==?= <schuetzm gmx.net> writes:
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.
Oct 07 2015
prev sibling parent reply Marc =?UTF-8?B?U2Now7x0eg==?= <schuetzm gmx.net> writes:
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...
Oct 07 2015
parent Namespace <rswhite4 gmail.com> writes:
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.
Oct 07 2015
prev sibling parent reply bitwise <bitwise.pvt gmail.com> writes:
On Wednesday, 7 October 2015 at 09:49:27 UTC, Marc Schütz wrote:
 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.
If you make a class that owns a resources that needs deterministic destruction, there is no guarantee that everyone will wrap that class in a RefCounted properly. Also, I've already stated many more problems with struct wrappers.
Oct 07 2015
parent reply Jonathan M Davis <jmdavisProg gmx.com> writes:
On Wednesday, 7 October 2015 at 12:59:05 UTC, bitwise wrote:
 On Wednesday, 7 October 2015 at 09:49:27 UTC, Marc Schütz wrote:
 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.
If you make a class that owns a resources that needs deterministic destruction, there is no guarantee that everyone will wrap that class in a RefCounted properly. Also, I've already stated many more problems with struct wrappers.
Except it doesn't even matter if they always wrap it properly - or even if something like DIP 74 is implemented. The core problem is that no object (be it struct or class) which needs deterministic destruction can have its lifetime managed by the GC - be it be simply being directly allocated by the GC or by sitting inside of an object that was put on the GC heap without any kind of ref-counting. The programmer is always going to have to be careful about where they put an object that needs deterministic destruction. They either have to keep it out of objects whose lifetimes are managed by the GC or be okay with them not actually being destroyed deterministically in that particular case. The method of ref-counting doesn't change that. That's simply a restriction of dealing with the GC. The only way around that would be to make it illegal to put any kind of object with a destructor (rather than a finalizer) on the GC heap, which would be unnecessarily restrictive, particularly since there are plenty of cases where a programmer isn't going to care that an object that is normally destroyed deterministically ends up being destroyed at the GC's leisure instead. Rather, if we don't want solve these problems, we need a way to provide deterministic destruction for the programmer to use when they want to. Structs get that natively. Classes don't. So, for classes to be able to work with deterministic destruction, we either need to have RefCounted (or something similar) work properly with classes, or we need something like DIP 74 which builds it into the language. It really doesn't matter which it is so long as it works. My guess is that RefCounted doesn't work with classes because of the fact that it uses alias this, and alias this off the table if you're trying to make it so that the wrapped object can't escape - though with that in mind, I question that RefCounted to should have alias this even it doesn't involve classes. opDispatch would be our only hope at that point, and I don't know if that would fully work or not (which may be why Walter and Andrei now are looking at putting ref-counting into the language rather than getting it to work with smart pointers like they were arguing for before). Regardless of the ref-counting mechanism though, you can't guarantee that it's going to be used correctly. The best that we can do is guarantee that if it is used correctly, then the object will be destroyed deterministically. - Jonathan M Davis
Oct 07 2015
parent reply bitwise <bitwise.pvt gmail.com> writes:
On Wednesday, 7 October 2015 at 15:30:17 UTC, Jonathan M Davis 
wrote:
 On Wednesday, 7 October 2015 at 12:59:05 UTC, bitwise wrote:
 On Wednesday, 7 October 2015 at 09:49:27 UTC, Marc Schütz 
 wrote:
 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.
If you make a class that owns a resources that needs deterministic destruction, there is no guarantee that everyone will wrap that class in a RefCounted properly. Also, I've already stated many more problems with struct wrappers.
Except it doesn't even matter if they always wrap it properly - or even if something like DIP 74 is implemented. The core problem is that no object (be it struct or class) which needs deterministic destruction can have its lifetime managed by the GC - be it be simply being directly allocated by the GC or by sitting inside of an object that was put on the GC heap without any kind of ref-counting.
With DIP74, the ref counting is hard coded into the type itself. The intention of the original author of the class is inseparable from the code. The same is not true for a class that is simply "meant" to be used in a context where it has deterministic destruction. So if I was a graphics programmer, and awake of this issue(deterministic destruction), I could open up a class and just look for opRelease() rather than having to try and reason about it's usage.
 The programmer is always going to have to be careful about 
 where they put an object that needs deterministic destruction.
Again, it's much easier to be careful about this when the author's intent is baked into the class.
 Regardless of the ref-counting mechanism though, you can't 
 guarantee that it's going to be used correctly. The best that 
 we can do is guarantee that if it is used correctly, then the 
 object will be destroyed deterministically.
Since you're probably going to make me repeat myself anyways, I might as well get it out of the way ;) Again, it's much easier to be careful about this when the author's intent is baked into the class. Bit
Oct 08 2015
next sibling parent reply Jonathan M Davis <jmdavisProg gmx.com> writes:
On Thursday, 8 October 2015 at 15:59:23 UTC, bitwise wrote:
 Again, it's much easier to be careful about this when the 
 author's intent is baked into the class.
That may be, but my point was that it doesn't actually guarantee that the object is going to be destroyed determinstically. That's going to require that the programmer using the object know that it's designed to be destroyed deterministically and program accordingly. Having it be easier for the programmer to figure out whether an object was designed that way is definitely a plus, but it doesn't make it so that they don't have to worry about it. - Jonathan M Davis
Oct 08 2015
parent bitwise <bitwise.pvt gmail.com> writes:
On Thursday, 8 October 2015 at 16:14:05 UTC, Jonathan M Davis 
wrote:
 On Thursday, 8 October 2015 at 15:59:23 UTC, bitwise wrote:
 Again, it's much easier to be careful about this when the 
 author's intent is baked into the class.
That may be, but my point was that it doesn't actually guarantee that the object is going to be destroyed determinstically. That's going to require that the programmer using the object know that it's designed to be destroyed deterministically and program accordingly. Having it be easier for the programmer to figure out whether an object was designed that way is definitely a plus, but it doesn't make it so that they don't have to worry about it. - Jonathan M Davis
True. I agree with you on this. All the time, this idea comes to mind when I see people arguing back and forth, thinking that they will eventually converge on some perfectly ideal solution which obviates the need for any real effort. Bit
Oct 08 2015
prev sibling parent reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
On 10/8/15 5:59 PM, bitwise wrote:
 With DIP74, the ref counting is hard coded into the type itself.
I think that should be the case. -- Andrei
Oct 08 2015
parent reply bitwise <bitwise.pvt gmail.com> writes:
On Thursday, 8 October 2015 at 17:21:52 UTC, Andrei Alexandrescu 
wrote:
 On 10/8/15 5:59 PM, bitwise wrote:
 With DIP74, the ref counting is hard coded into the type 
 itself.
I think that should be the case. -- Andrei
Can you comment on the status of the next vision document? Thanks =)
Oct 08 2015
parent reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
On 10/9/15 4:47 AM, bitwise wrote:
 On Thursday, 8 October 2015 at 17:21:52 UTC, Andrei Alexandrescu wrote:
 On 10/8/15 5:59 PM, bitwise wrote:
 With DIP74, the ref counting is hard coded into the type itself.
I think that should be the case. -- Andrei
Can you comment on the status of the next vision document?
The H1 vision document has had some effectiveness albeit limited. I'll discuss outlook with Walter during our road trip, and plan to update it when we get back. -- Andrei
Oct 08 2015
next sibling parent bitwise <bitwise.pvt gmail.com> writes:
On Friday, 9 October 2015 at 05:21:13 UTC, Andrei Alexandrescu 
wrote:
 On 10/9/15 4:47 AM, bitwise wrote:
 On Thursday, 8 October 2015 at 17:21:52 UTC, Andrei 
 Alexandrescu wrote:
 On 10/8/15 5:59 PM, bitwise wrote:
 With DIP74, the ref counting is hard coded into the type 
 itself.
I think that should be the case. -- Andrei
Can you comment on the status of the next vision document?
The H1 vision document has had some effectiveness albeit limited. I'll discuss outlook with Walter during our road trip, and plan to update it when we get back. -- Andrei
Awesome, thanks! Bit
Oct 08 2015
prev sibling next sibling parent rsw0x <anonymous anonymous.com> writes:
On Friday, 9 October 2015 at 05:21:13 UTC, Andrei Alexandrescu 
wrote:
 On 10/9/15 4:47 AM, bitwise wrote:
 On Thursday, 8 October 2015 at 17:21:52 UTC, Andrei 
 Alexandrescu wrote:
 On 10/8/15 5:59 PM, bitwise wrote:
 With DIP74, the ref counting is hard coded into the type 
 itself.
I think that should be the case. -- Andrei
Can you comment on the status of the next vision document?
The H1 vision document has had some effectiveness albeit limited. I'll discuss outlook with Walter during our road trip, and plan to update it when we get back. -- Andrei
The vision document mentioned finalizing shared so that it's "done" IIRC. Is there anything still being done about this?
Oct 08 2015
prev sibling parent reply bitwise <bitwise.pvt gmail.com> writes:
On Friday, 9 October 2015 at 05:21:13 UTC, Andrei Alexandrescu 
wrote:
 On 10/9/15 4:47 AM, bitwise wrote:
 On Thursday, 8 October 2015 at 17:21:52 UTC, Andrei 
 Alexandrescu wrote:
 On 10/8/15 5:59 PM, bitwise wrote:
 With DIP74, the ref counting is hard coded into the type 
 itself.
I think that should be the case. -- Andrei
Can you comment on the status of the next vision document?
The H1 vision document has had some effectiveness albeit limited. I'll discuss outlook with Walter during our road trip, and plan to update it when we get back. -- Andrei
Any news on this? Bit
Oct 17 2015
parent reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
On 10/18/15 1:57 AM, bitwise wrote:
 On Friday, 9 October 2015 at 05:21:13 UTC, Andrei Alexandrescu wrote:
 On 10/9/15 4:47 AM, bitwise wrote:
 On Thursday, 8 October 2015 at 17:21:52 UTC, Andrei Alexandrescu wrote:
 On 10/8/15 5:59 PM, bitwise wrote:
 With DIP74, the ref counting is hard coded into the type itself.
I think that should be the case. -- Andrei
Can you comment on the status of the next vision document?
The H1 vision document has had some effectiveness albeit limited. I'll discuss outlook with Walter during our road trip, and plan to update it when we get back. -- Andrei
Any news on this? Bit
Still on the road! Thanks for the prod. -- Andrei
Oct 17 2015
parent bitwise <bitwise.pvt gmail.com> writes:
On Sunday, 18 October 2015 at 04:03:49 UTC, Andrei Alexandrescu 
wrote:
 On 10/18/15 1:57 AM, bitwise wrote:
 On Friday, 9 October 2015 at 05:21:13 UTC, Andrei Alexandrescu 
 wrote:
 On 10/9/15 4:47 AM, bitwise wrote:
 On Thursday, 8 October 2015 at 17:21:52 UTC, Andrei 
 Alexandrescu wrote:
 On 10/8/15 5:59 PM, bitwise wrote:
 With DIP74, the ref counting is hard coded into the type 
 itself.
I think that should be the case. -- Andrei
Can you comment on the status of the next vision document?
The H1 vision document has had some effectiveness albeit limited. I'll discuss outlook with Walter during our road trip, and plan to update it when we get back. -- Andrei
Any news on this? Bit
Still on the road! Thanks for the prod. -- Andrei
Oh, sorry! For some reason I thought that this was the only thing on the itinerary: http://curiousminds.ro/ Thanks Bit
Oct 18 2015
prev sibling parent anonymous <anonymous example.com> writes:
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 */ } ----
Oct 05 2015
prev sibling next sibling parent bitwise <bitwise.pvt gmail.com> writes:
On Sunday, 4 October 2015 at 18:02:21 UTC, bitwise wrote:
 http://wiki.dlang.org/Vision/2015H1

 Looking at this, It's obvious that some of it has spilled over, 
 but it would be nice to have a fresh document detailing the 
 plan moving forward.

 [...]
bump
Oct 05 2015
prev sibling next sibling parent reply Jonathan M Davis <jmdavisProg gmx.com> writes:
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?
It is not the goal to eliminate GC usage entirely. It's not like we're getting rid of the GC. Rather, it's the goal to make it so that the GC is not used when it's not necessary and to minimize how much it's necessary. Some things will always require polymorphism and classes, and in at least some of those cases, that means using the GC. And features like appending to arrays or allocating closures will always require the GC. We just need to make sure that those features aren't used when they're not actually needed. 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. And not much in Phobos uses classes anyway. There are a few cases where it does and probably shouldn't (e.g. std.mmfile probably shouldn't be using class, since no polymorphism is required), but most of Phobos has used structs for ages, and a few places actually need classes to provide the functionality that it provides. The biggest GC problem in Phobos is probably how often you end up with closures being allocated for algorithms, particularly since it's invisible unless you use nogc. And that definitely needs to be resolved. The second is probably how many functions return strings instead of lazy ranges, and work has been done on that, though there's still more to do. But classes are a very small part of Phobos. - Jonathan M Davis
Oct 05 2015
next sibling parent Brian Rogoff <brogoff gmail.com> writes:
On Monday, 5 October 2015 at 17:27:30 UTC, Jonathan M Davis wrote:
 And features like appending to arrays or allocating closures 
 will always require the GC.
Downward only closures should not always require GC. Pascal had these.
Oct 05 2015
prev sibling parent reply bitwise <bitwise.pvt gmail.com> writes:
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
Oct 05 2015
parent reply Meta <jared771 gmail.com> writes:
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.
Oct 05 2015
parent bitwise <bitwise.pvt gmail.com> writes:
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
Oct 05 2015
prev sibling parent reply Walter Bright <newshound2 digitalmars.com> writes:
On 10/4/2015 11:02 AM, bitwise wrote:
 For example, streams.
No streams. InputRanges.
Oct 06 2015
next sibling parent reply bitwise <bitwise.pvt gmail.com> writes:
On Wednesday, 7 October 2015 at 01:27:27 UTC, Walter Bright wrote:
 On 10/4/2015 11:02 AM, bitwise wrote:
 For example, streams.
No streams. InputRanges.
This is too vague to really respond to. It does serve as an example of the over-emphasis on ranges in the D community. Ranges are great, but not for everything. Bit
Oct 06 2015
parent reply Walter Bright <newshound2 digitalmars.com> writes:
On 10/6/2015 7:04 PM, bitwise wrote:
 On Wednesday, 7 October 2015 at 01:27:27 UTC, Walter Bright wrote:
 On 10/4/2015 11:02 AM, bitwise wrote:
 For example, streams.
No streams. InputRanges.
This is too vague to really respond to. It does serve as an example of the over-emphasis on ranges in the D community. Ranges are great, but not for everything.
What can a stream do that a range cannot?
Oct 06 2015
next sibling parent reply bitwise <bitwise.pvt gmail.com> writes:
On Wednesday, 7 October 2015 at 02:41:12 UTC, Walter Bright wrote:
 On 10/6/2015 7:04 PM, bitwise wrote:
 On Wednesday, 7 October 2015 at 01:27:27 UTC, Walter Bright 
 wrote:
 On 10/4/2015 11:02 AM, bitwise wrote:
 For example, streams.
No streams. InputRanges.
This is too vague to really respond to. It does serve as an example of the over-emphasis on ranges in the D community. Ranges are great, but not for everything.
What can a stream do that a range cannot?
I was trying to make my case for polymorphism, so I haven't thought much about streams specifically, but one obvious thing that stands out is growing on demand. Stream s = new Stream(4); s.write(1); s.write(2); // underlaying buffer grows automatically I don't see how you would do something like this with a range. When it comes to an InputRange, I suppose you're right. A BinaryReader could be generalized to read any range. 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. I've detailed my reasoning in this thread already, but structs alone, and structs wrapping classes are not goods solutions. Bit
Oct 06 2015
parent reply Walter Bright <newshound2 digitalmars.com> writes:
On 10/6/2015 7:57 PM, bitwise wrote:
 What can a stream do that a range cannot?
I was trying to make my case for polymorphism, so I haven't thought much about streams specifically, but one obvious thing that stands out is growing on demand. Stream s = new Stream(4); s.write(1); s.write(2); // underlaying buffer grows automatically I don't see how you would do something like this with a range.
It's what an OutputRange does.
 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.
Oct 06 2015
parent bitwise <bitwise.pvt gmail.com> writes:
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
Oct 07 2015
prev sibling parent Sebastiaan Koppe <mail skoppe.eu> writes:
On Wednesday, 7 October 2015 at 02:41:12 UTC, Walter Bright wrote:
 On 10/6/2015 7:04 PM, bitwise wrote:
 On Wednesday, 7 October 2015 at 01:27:27 UTC, Walter Bright 
 wrote:
 On 10/4/2015 11:02 AM, bitwise wrote:
 For example, streams.
No streams. InputRanges.
This is too vague to really respond to. It does serve as an example of the over-emphasis on ranges in the D community. Ranges are great, but not for everything.
What can a stream do that a range cannot?
I wouldn't know about streams vs ranges, but I have worked with Eric Meijer's RX and observables, and it has helped a lot with asynchronous/evented code. Mostly because of the FP thrown in. In a way it is very similar to ranges and std.algorithm. A range, however, is inherently synchronous and blocking. If streams could be asynchronous, wouldn't that qualify?
Oct 07 2015
prev sibling parent Marco Leise <Marco.Leise gmx.de> writes:
Am Tue, 6 Oct 2015 18:27:28 -0700
schrieb Walter Bright <newshound2 digitalmars.com>:

 On 10/4/2015 11:02 AM, bitwise wrote:
 For example, streams.
No streams. InputRanges.
... what bitwise said ... We had this discussion at least once and it did not change my mind back then: Ranges and streams have overlapping use cases, but they are not interchangeable. What they have in common is that they are both lazy and process data from start to end. === Composability === Streams work on a series of bits coming from the sender. Class polymorphism allows them to be wrapped into each other as needed at any point during the transmission. This includes applying or reversing compression, encryption and endianness changes. New filter streams can be loaded through DLLs and extend the hierarchy. void main() { InputStream is; // Simple stream is = new FileStream("./test.txt"); // File from the web, unzipped with a dynamically loaded // filter plugin is = new HttpStream("http://a.xy/test.gz"); auto fs = cast(FilterStream) Object.factory("UnGzipStream"); fs.wrapped = is; is = cast(InputStream) fs; // One common interface works with all combinations. No need // to know the stream types at compile time, no // combinatorial template explosion processTestFile(is); } void processTestFile(InputStream is); === Primitives === While ranges assume a fixed structure over all elements, streamed data embeds information about upcoming data types and sizes. To handle these, instead of getting the next "item" with .front, streams provide a different set of primitives: bulk reads, wrappers for basic data types (this is where endianness filters apply) and often bit-wise reads to handle compressed data. === Buffering === Since input ranges/streams can not generally be wound back, one natural component of streams is a buffer, similar to what .byLine does for text, but more flexible. A circular buffer that can grow is a good candidate. Like .byLine it offers slicing, or generally referencing its contents at the current read position without copying. Several primitives are provided to ask for a number of bytes to be buffered or dropped. This does not only obviate the need to check for "end of file" or "end of buffer" everywhere but also enables code reuse in cases like reading a BigInt off a stream, which takes a char[] as ctor argument. With a buffered stream you increase the look-ahead until the entire number string is buffered and can be passed to BigInt() by reference. BigInt could be changed to be constructible from a range, but the raw look-ahead enables use of SIMD to scan for end-of-line or end-of-number that traditional input range primitives don't. -- Marco
Oct 08 2015