digitalmars.D - D 2015/2016 Vision?
- bitwise (21/21) Oct 04 2015 http://wiki.dlang.org/Vision/2015H1
- rsw0x (6/10) Oct 04 2015 It's just a symptom of D classes being so difficult to use
- bitwise (4/14) Oct 04 2015 Yeah, thats all good stuff, but for compile time. Also, it makes
- Namespace (45/55) Oct 04 2015 You can use classes without GC _and_ with deteministic lifetime:
- bitwise (7/61) Oct 04 2015 And if I want to nest a class in a struct? or pass a class
- Gary Willoughby (5/11) Oct 05 2015 I think you can get a lot of mileage from using the following:
- Gary Willoughby (21/66) Oct 05 2015 This can be shortened to:
- Meta (35/55) Oct 05 2015 There's a critical flaw in `scoped`. Observe:
- bitwise (14/75) Oct 05 2015 also:
- Meta (4/17) Oct 05 2015 This is more an issue with `alias this` than it is with `scoped`.
- Namespace (57/118) Oct 05 2015 ----
- bitwise (5/10) Oct 05 2015 I think you kinda missed the point. The second one was _supposed_
- bitwise (5/9) Oct 05 2015 typo:
- Namespace (2/14) Oct 05 2015 Ah, right. I've overseen it.
- Namespace (2/2) Oct 05 2015 But you can simply relinquish alias this and use opDispatch.
- bitwise (10/12) Oct 05 2015 I don't understand what you mean.
- Namespace (47/51) Oct 06 2015 ----
- Namespace (29/29) Oct 06 2015 It's a step simpler with the new inline feature (works sadly only
- Meta (4/6) Oct 05 2015 There is std.typecons.Proxy which may do the trick. It provides
- bitwise (5/11) Oct 05 2015 Well, again that has it's pros and cons. This is why I just want
- Jonathan M Davis (10/12) Oct 05 2015 They're not the same thing at all. scoped is supposed to put the
- Ola Fosheim =?UTF-8?B?R3LDuHN0YWQ=?= (8/19) Oct 06 2015 Why not leave stack allocation of objects to the compiler, like
- Jonathan M Davis (18/39) Oct 06 2015 scoped is not designed with the idea that it's memory safe.
- bitwise (13/26) Oct 06 2015 On Monday, 5 October 2015 at 18:18:15 UTC, bitwise wrote:
- Jonathan M Davis (22/41) Oct 06 2015 Well, they might seem the same when you only look at that part,
- bitwise (24/69) Oct 06 2015 I'm not sure what else I can say. The example I posted says it
- Namespace (2/2) Oct 06 2015 You don't need RC, just use Unique. In most cases you don't want
- Jonathan M Davis (33/47) Oct 06 2015 It's a side effect of having the lifetime of an object managed by
- Paulo Pinto (16/51) Oct 06 2015 As of Java 7
- rsw0x (2/6) Oct 06 2015 +1, tired of repeating this.
- jmh530 (4/11) Oct 06 2015 What improvements to structs do you think would help people
- Jonathan M Davis (21/32) Oct 06 2015 I don't think the problem is with structs. The problem is that
- Kagamin (6/13) Oct 08 2015 Hmm... If we must emulate reference semantics manually, it feels
- Jonathan M Davis (20/35) Oct 08 2015 Maybe, but having classes be value generally makes no sense,
- bitwise (8/16) Oct 08 2015 This is a huge generalization, and is incorrect. You can still
- Ola Fosheim =?UTF-8?B?R3LDuHN0YWQ=?= (5/7) Oct 08 2015 Inaccurate maybe, but something that has an identity is not a
- Meta (3/7) Oct 08 2015 Why, because you can take their address?
- Ola Fosheim =?UTF-8?B?R3LDuHN0YWQ=?= (5/6) Oct 08 2015 You or someone you call can take the address. If there is a way
- Kagamin (5/7) Oct 12 2015 If it doesn't make sense for reference types to be value types,
- Jonathan M Davis (24/31) Oct 12 2015 My point is that without a level of indirection, polymorphism
- Kagamin (8/13) Oct 12 2015 As soon as you choose to use value types you already get the
- deadalnix (4/11) Oct 12 2015 It does make sense. You have the value type that do the reference
- bitwise (33/66) Oct 06 2015 You are literally repeating what I just said in different words.
- ponce (4/8) Oct 06 2015 This problem comes up again and again, here is an ugly trick to
- bitwise (4/13) Oct 06 2015 Heh...This only adds insult to injury.... I would have thought
- ponce (7/12) Oct 06 2015 Yes. It seems everyone has this epiphany at one point in their D
- Jonathan M Davis (65/88) Oct 06 2015 The reality of the matter is that having the lifetime of an
- bitwise (37/59) Oct 06 2015 I'll give you that. The situation is slightly better than C#.
- Meta (10/13) Oct 06 2015 This isn't really a problem as it can be easily fixed. It's just
- Kagamin (3/7) Oct 08 2015 You manage resources with reference counting in C++? How it deals
- Ola Fosheim =?UTF-8?B?R3LDuHN0YWQ=?= (3/10) Oct 08 2015 You have to use weak pointers for back references.
- Kagamin (4/6) Oct 08 2015 I suspected as much and the next question is how is it better
- Ola Fosheim =?UTF-8?B?R3LDuHN0YWQ=?= (4/11) Oct 08 2015 I don't know. I don't like extensive reference counting and don't
- Kagamin (8/11) Oct 08 2015 The required design is that a resource handle must reside in a
- Ola Fosheim =?UTF-8?B?R3LDuHN0YWQ=?= (10/12) Oct 08 2015 It's a system programming language design... If you plan your
- Jonathan M Davis (20/28) Oct 08 2015 I've programmed extensively in C++ with smart pointers, and in my
- Ola Fosheim =?UTF-8?B?R3LDuHN0YWQ=?= (27/33) Oct 08 2015 Yes, in general ownership should not be circular at all. It
- Jonathan M Davis (16/20) Oct 08 2015 As far as I can tell, finalizers are for when you either really
- Jim Hewes (9/13) Oct 08 2015 +1. Reference counting has not been much of an issue for me since using
- Kagamin (5/16) Oct 12 2015 That's all understandable. What's not understandable is when one
- Jonathan M Davis (10/27) Oct 12 2015 If you don't want to care about ownership, then use a GC. The
- deadalnix (5/33) Oct 12 2015 Well technically, there is still ownership: the GC owns
- Ola Fosheim =?UTF-8?B?R3LDuHN0YWQ=?= (14/17) Oct 12 2015 Only if you have finalizers. Without finalization it is better to
- Ola Fosheim =?UTF-8?B?R3LDuHN0YWQ=?= (2/5) Oct 12 2015 You don't have to, you can use a region allocator.
- Kagamin (6/7) Oct 14 2015 I wrote one and use it in my code for everything. I guess the
- Ola Fosheim =?UTF-8?B?R3LDuHN0YWQ=?= (5/12) Oct 14 2015 Yeah, well, not even sure what C++ idioms are? The ones that a
- Kagamin (9/11) Oct 15 2015 Off the top of my head:
- bitwise (8/15) Oct 08 2015 Resources don't generally own each other, so this isn't really an
- Paulo Pinto (18/69) Oct 07 2015 That no, but this yes (at least in C#):
- ponce (6/23) Oct 07 2015 This is similar to Scoped!T in D. But this is not composable
- Paulo Pinto (30/60) Oct 07 2015 If you reduce everything to just using(), yes you are right.
- rumbu (19/49) Oct 07 2015 You must implement IDisposable.
- bitwise (24/111) Oct 07 2015 Still no ;)
- Paulo Pinto (17/67) Oct 07 2015 I guess you misunderstood the // Somewhere in the call stack
- Ola Fosheim =?UTF-8?B?R3LDuHN0YWQ=?= (5/10) Oct 07 2015 Are you thinking about more lintish tools that can give false
- Paulo Pinto (10/20) Oct 07 2015 All of the ones that explore this area. Rust, ATS, Idris, F*....
- Atila Neves (8/32) Oct 07 2015 The CppCon demos were impressive, but I'm dying to see how
- Paulo Pinto (13/51) Oct 07 2015 I would say the answer to that question was present in both
- Ola Fosheim =?UTF-8?B?R3LDuHN0YWQ=?= (20/31) Oct 08 2015 Oh, yeah, sure. I wondered more if you were looking to adopt a
- Paulo Pinto (29/63) Oct 08 2015 Not a chance.
- Ola Fosheim =?UTF-8?B?R3LDuHN0YWQ=?= (6/11) Oct 08 2015 I've found getting a good understanding of C++11/14 to be a
- Jim Hewes (10/16) Oct 08 2015 I've recently become curious about the actor model and would like to
- Ola Fosheim =?UTF-8?B?R3LDuHN0YWQ=?= (20/28) Oct 08 2015 Yes, there are libraries, but for it to be pleasant I think
- Jim Hewes (13/25) Oct 09 2015 Yeah, I watched that after I saw your other post with the link. Thanks.
- Ola Fosheim =?UTF-8?B?R3LDuHN0YWQ=?= (28/41) Oct 09 2015 Well, I think Hewitt wants actors to be a foundational concept,
- bitwise (24/91) Oct 07 2015 I still don't think your example exists in real world
- Adrian Matoga (10/21) Oct 14 2015 It does, entirely. I usually translate it to D this way:
- ponce (6/17) Oct 06 2015 Unfortunately, it is quite common to need both virtual functions
- Jonathan M Davis (23/41) Oct 06 2015 If you need both polymorphism and determinstic destruction, then
- rsw0x (4/11) Oct 06 2015 +1 to this and pretty much everything else you've said in this
- Dmitry Olshansky (4/8) Oct 06 2015 +111
- Marc =?UTF-8?B?U2Now7x0eg==?= (27/55) Oct 07 2015 import std.stdio;
- Namespace (3/65) Oct 07 2015 Because there is no guarantee that others, who use your code, get
- Jonathan M Davis (21/27) Oct 07 2015 Language-supported ref-counting wouldn't fix that. As long as
- Namespace (4/26) Oct 07 2015 Sure, there are, of course, still ways to avoid the guarantees.
- Jonathan M Davis (10/39) Oct 07 2015 Well, except that then it's less obvious that an object is
- Namespace (25/34) Oct 07 2015 Well then, there is another solution: enable inheritance for
- Jonathan M Davis (44/58) Oct 07 2015 How does that solve anything? The problem is that some types need
- Ola Fosheim =?UTF-8?B?R3LDuHN0YWQ=?= (15/23) Oct 07 2015 This is a costly (in terms of collection) and unreliable approach
- Adam D. Ruppe (19/25) Oct 07 2015 There's a lot of hassles with a wrapper, the biggest blocker
- Marc =?UTF-8?B?U2Now7x0eg==?= (12/19) Oct 07 2015 I doubt that it can gain much performance in contrast to a
- Marc =?UTF-8?B?U2Now7x0eg==?= (7/9) Oct 07 2015 The obvious way would be to make the constructor private and only
- Namespace (46/55) Oct 07 2015 Something like this?
- bitwise (5/10) Oct 07 2015 If you make a class that owns a resources that needs
- Jonathan M Davis (43/55) Oct 07 2015 Except it doesn't even matter if they always wrap it properly -
- bitwise (18/46) Oct 08 2015 With DIP74, the ref counting is hard coded into the type itself.
- Jonathan M Davis (9/11) Oct 08 2015 That may be, but my point was that it doesn't actually guarantee
- bitwise (8/20) Oct 08 2015 True. I agree with you on this.
- Andrei Alexandrescu (2/3) Oct 08 2015 I think that should be the case. -- Andrei
- bitwise (4/8) Oct 08 2015 Can you comment on the status of the next vision document?
- Andrei Alexandrescu (4/10) Oct 08 2015 The H1 vision document has had some effectiveness albeit limited. I'll
- bitwise (4/17) Oct 08 2015 Awesome, thanks!
- rsw0x (4/17) Oct 08 2015 The vision document mentioned finalizing shared so that it's
- bitwise (4/17) Oct 17 2015 Any news on this?
- Andrei Alexandrescu (2/17) Oct 17 2015 Still on the road! Thanks for the prod. -- Andrei
- bitwise (8/32) Oct 18 2015 Oh, sorry!
- anonymous (14/58) Oct 05 2015 You're getting at the segfault, right? The example can then be much simp...
- bitwise (2/7) Oct 05 2015 bump
- Jonathan M Davis (31/33) Oct 05 2015 It is not the goal to eliminate GC usage entirely. It's not like
- Brian Rogoff (3/5) Oct 05 2015 Downward only closures should not always require GC. Pascal had
- bitwise (23/35) Oct 05 2015 The deterministic destruction is actually what I'm after.
- Walter Bright (2/3) Oct 06 2015 No streams. InputRanges.
- bitwise (5/8) Oct 06 2015 This is too vague to really respond to. It does serve as an
- Walter Bright (2/10) Oct 06 2015 What can a stream do that a range cannot?
- bitwise (17/31) Oct 06 2015 I was trying to make my case for polymorphism, so I haven't
- Walter Bright (3/13) Oct 06 2015 Yes. We need to do that.
- bitwise (4/12) Oct 07 2015 I think that every nanometer this one gets moved up the todo list
- Sebastiaan Koppe (7/21) Oct 07 2015 I wouldn't know about streams vs ranges, but I have worked with
- Marco Leise (58/62) Oct 08 2015 ... what bitwise said ...
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
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
On Sunday, 4 October 2015 at 20:18:25 UTC, rsw0x wrote:On Sunday, 4 October 2015 at 18:02:21 UTC, bitwise wrote:Yeah, thats all good stuff, but for compile time. Also, it makes things that should be trivial to design 3x harder to code. BitCurrently, 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
On Sunday, 4 October 2015 at 20:18:25 UTC, rsw0x wrote:On Sunday, 4 October 2015 at 18:02:21 UTC, bitwise 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(); } ----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
On Sunday, 4 October 2015 at 22:10:18 UTC, Namespace wrote:On Sunday, 4 October 2015 at 20:18:25 UTC, rsw0x 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. BitOn Sunday, 4 October 2015 at 18:02:21 UTC, bitwise 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(); } ----[...]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
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. BitI think you can get a lot of mileage from using the following:
Oct 05 2015
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
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
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: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); } BitThis 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
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); } BitThis 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
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:---- 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 AThis 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
On Monday, 5 October 2015 at 20:23:41 UTC, Namespace wrote:On Monday, 5 October 2015 at 19:07:20 UTC, Meta 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[...]---- import std.stdio; [...]
Oct 05 2015
On Monday, 5 October 2015 at 20:49:08 UTC, bitwise wrote: [...]I think you kinda missed the point. The second one was _supposed_ to be typed as Foo. The point is that the compiler allows it. It's unsafe. Bittypo: a2 was supposed to be typed as 'A'. Bit
Oct 05 2015
On Monday, 5 October 2015 at 20:49:08 UTC, bitwise wrote:On Monday, 5 October 2015 at 20:23:41 UTC, Namespace wrote:Ah, right. I've overseen it.On Monday, 5 October 2015 at 19:07:20 UTC, Meta 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[...]---- import std.stdio; [...]
Oct 05 2015
But you can simply relinquish alias this and use opDispatch. Problem solved.
Oct 05 2015
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
On Monday, 5 October 2015 at 22:15:59 UTC, bitwise wrote:On Monday, 5 October 2015 at 21:29:20 UTC, Namespace wrote:---- 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(); } ----But you can simply relinquish alias this and use opDispatch. Problem solved.I don't understand what you mean.
Oct 06 2015
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
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
On Monday, 5 October 2015 at 22:48:37 UTC, Meta wrote:On Monday, 5 October 2015 at 21:29:20 UTC, Namespace wrote: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 BitBut 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
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
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: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.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.
Oct 06 2015
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: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 DavisOn Monday, 5 October 2015 at 23:08:37 UTC, bitwise wrote: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.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.
Oct 06 2015
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:On Monday, 5 October 2015 at 18:18:15 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 DavisThe 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
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
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: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.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 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 DavisCorrect, 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
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
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
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: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[...]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
Oct 06 2015
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
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
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: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 DavisIn 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
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
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: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 DavisI 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
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
On Thursday, 8 October 2015 at 16:17:49 UTC, bitwise 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.Classes are inherently reference types given their semantics.Incorrect.
Oct 08 2015
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
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
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
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: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 DavisMaybe, 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
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
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: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.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
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:You are literally repeating what I just said in different words.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.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 scopeFor 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
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
On Tuesday, 6 October 2015 at 20:46:00 UTC, ponce wrote:On Tuesday, 6 October 2015 at 20:43:42 UTC, bitwise wrote:Heh...This only adds insult to injury.... I would have thought the GC was smart enough not to have to do this. BitI 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
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
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.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.In most cases though, just don't use classes. In most cases, inheritance is a horrible way to write programs anyway,Opinion.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 DavisI 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.
Oct 06 2015
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..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._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.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
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.orgThis 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
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
On Thursday, 8 October 2015 at 10:05:53 UTC, Kagamin wrote:On Wednesday, 7 October 2015 at 00:17:37 UTC, bitwise wrote:You have to use weak pointers for back references. http://en.cppreference.com/w/cpp/memory/weak_ptrIf 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
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_ptrI suspected as much and the next question is how is it better
Oct 08 2015
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: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.You have to use weak pointers for back references. http://en.cppreference.com/w/cpp/memory/weak_ptrI suspected as much and the next question is how is it better
Oct 08 2015
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
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
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: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 Daviscyclic 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.
Oct 08 2015
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
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
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
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
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: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 DavisI'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
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:Well technically, there is still ownership: the GC owns everything. But yeah, you don't need to care about for this very reason.On Thursday, 8 October 2015 at 14:13:30 UTC, Jonathan M Davis wrote: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 DavisI'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
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
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
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
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: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...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
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
On Thursday, 8 October 2015 at 10:05:53 UTC, Kagamin wrote:On Wednesday, 7 October 2015 at 00:17:37 UTC, bitwise wrote: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. BitIf 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
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: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. -- PauloOn Tuesday, 6 October 2015 at 18:10:42 UTC, bitwise wrote:You are literally repeating what I just said in different words.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.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 scopeFor 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) { // ... }
Oct 07 2015
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. -- PauloThis 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
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: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. -- Paulousing (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. -- PauloThis 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
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: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().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. -- PauloThis 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
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: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. BitOn Tuesday, 6 October 2015 at 18:43:42 UTC, Jonathan M Davis 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. -- PauloOn Tuesday, 6 October 2015 at 18:10:42 UTC, bitwise wrote:You are literally repeating what I just said in different words.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.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 scopeFor 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) { // ... }
Oct 07 2015
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: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. -- PauloOn Tuesday, 6 October 2015 at 20:43:42 UTC, bitwise wrote: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[...]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
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
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:All of the ones that explore this area. Rust, ATS, Idris, F*....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?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.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
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: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? AtilaOn Wednesday, 7 October 2015 at 13:15:11 UTC, Paulo Pinto wrote:All of the ones that explore this area. Rust, ATS, Idris, F*....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?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.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
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: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.On Wednesday, 7 October 2015 at 15:42:57 UTC, Ola Fosheim Grøstad wrote: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? AtilaOn Wednesday, 7 October 2015 at 13:15:11 UTC, Paulo Pinto wrote:All of the ones that explore this area. Rust, ATS, Idris, F*....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?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.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
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: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 Rust, or some other language?All of the ones that explore this area. Rust, ATS, Idris, F*....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/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.I think this is needed, but adoption probably won't happen without IDE benefits.
Oct 08 2015
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: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.On Wednesday, 7 October 2015 at 15:42:57 UTC, Ola Fosheim Grøstad wrote: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.Are you thinking about Rust, or some other language?All of the ones that explore this area. Rust, ATS, Idris, F*....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.pdfBut 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.They use it on their driver validation tools.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/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++.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. -- PauloI 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
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
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
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_modelI 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
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-askYeah, 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
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=WGXPFPKQC2oThanks! 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
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: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.On Wednesday, 7 October 2015 at 07:24:03 UTC, Paulo Pinto wrote: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.On Tuesday, 6 October 2015 at 20:43:42 UTC, bitwise wrote: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[...]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. -- PauloAnd 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
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
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
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: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 DavisBut 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
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:+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.[...]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
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
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: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.On Monday, 5 October 2015 at 23:08:37 UTC, bitwise wrote:On Monday, 5 October 2015 at 18:18:15 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 DavisThe 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 07 2015
On Wednesday, 7 October 2015 at 09:49:27 UTC, Marc Schütz wrote:On Tuesday, 6 October 2015 at 17:03:07 UTC, bitwise wrote:Because there is no guarantee that others, who use your code, get it right and use those constructs.On Tuesday, 6 October 2015 at 06:45:47 UTC, Jonathan M Davis wrote: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.On Monday, 5 October 2015 at 23:08:37 UTC, bitwise wrote:On Monday, 5 October 2015 at 18:18:15 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 DavisThe 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 07 2015
On Wednesday, 7 October 2015 at 10:03:44 UTC, Namespace wrote:On Wednesday, 7 October 2015 at 09:49:27 UTC, Marc Schütz wrote: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 DavisReally, 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
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 DavisSure, 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
On Wednesday, 7 October 2015 at 10:50:35 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 DavisLanguage-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 DavisSure, 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
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 DavisWell 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
On Wednesday, 7 October 2015 at 11:21:04 UTC, Namespace wrote: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 DavisWell, 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 DavisWell 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:
Oct 07 2015
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
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
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
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
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: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.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
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
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: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 DavisRefCounted 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
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: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.On Wednesday, 7 October 2015 at 09:49:27 UTC, Marc Schütz wrote: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.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.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
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
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: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. BitAgain, 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
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
On Thursday, 8 October 2015 at 17:21:52 UTC, Andrei Alexandrescu wrote:On 10/8/15 5:59 PM, bitwise wrote:Can you comment on the status of the next vision document? Thanks =)With DIP74, the ref counting is hard coded into the type itself.I think that should be the case. -- Andrei
Oct 08 2015
On 10/9/15 4:47 AM, bitwise wrote:On Thursday, 8 October 2015 at 17:21:52 UTC, Andrei Alexandrescu wrote: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. -- AndreiOn 10/8/15 5:59 PM, bitwise wrote:Can you comment on the status of the next vision document?With DIP74, the ref counting is hard coded into the type itself.I think that should be the case. -- Andrei
Oct 08 2015
On Friday, 9 October 2015 at 05:21:13 UTC, Andrei Alexandrescu wrote:On 10/9/15 4:47 AM, bitwise wrote:Awesome, thanks! BitOn Thursday, 8 October 2015 at 17:21:52 UTC, Andrei Alexandrescu wrote: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. -- AndreiOn 10/8/15 5:59 PM, bitwise wrote:Can you comment on the status of the next vision document?With DIP74, the ref counting is hard coded into the type itself.I think that should be the case. -- Andrei
Oct 08 2015
On Friday, 9 October 2015 at 05:21:13 UTC, Andrei Alexandrescu wrote:On 10/9/15 4:47 AM, bitwise wrote:The vision document mentioned finalizing shared so that it's "done" IIRC. Is there anything still being done about this?On Thursday, 8 October 2015 at 17:21:52 UTC, Andrei Alexandrescu wrote: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. -- AndreiOn 10/8/15 5:59 PM, bitwise wrote:Can you comment on the status of the next vision document?With DIP74, the ref counting is hard coded into the type itself.I think that should be the case. -- Andrei
Oct 08 2015
On Friday, 9 October 2015 at 05:21:13 UTC, Andrei Alexandrescu wrote:On 10/9/15 4:47 AM, bitwise wrote:Any news on this? BitOn Thursday, 8 October 2015 at 17:21:52 UTC, Andrei Alexandrescu wrote: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. -- AndreiOn 10/8/15 5:59 PM, bitwise wrote:Can you comment on the status of the next vision document?With DIP74, the ref counting is hard coded into the type itself.I think that should be the case. -- Andrei
Oct 17 2015
On 10/18/15 1:57 AM, bitwise wrote:On Friday, 9 October 2015 at 05:21:13 UTC, Andrei Alexandrescu wrote:Still on the road! Thanks for the prod. -- AndreiOn 10/9/15 4:47 AM, bitwise wrote:Any news on this? BitOn Thursday, 8 October 2015 at 17:21:52 UTC, Andrei Alexandrescu wrote: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. -- AndreiOn 10/8/15 5:59 PM, bitwise wrote:Can you comment on the status of the next vision document?With DIP74, the ref counting is hard coded into the type itself.I think that should be the case. -- Andrei
Oct 17 2015
On Sunday, 18 October 2015 at 04:03:49 UTC, Andrei Alexandrescu wrote:On 10/18/15 1:57 AM, bitwise wrote:Oh, sorry! For some reason I thought that this was the only thing on the itinerary: http://curiousminds.ro/ Thanks BitOn Friday, 9 October 2015 at 05:21:13 UTC, Andrei Alexandrescu wrote:Still on the road! Thanks for the prod. -- AndreiOn 10/9/15 4:47 AM, bitwise wrote:Any news on this? BitOn Thursday, 8 October 2015 at 17:21:52 UTC, Andrei Alexandrescu wrote: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. -- AndreiOn 10/8/15 5:59 PM, bitwise wrote:Can you comment on the status of the next vision document?With DIP74, the ref counting is hard coded into the type itself.I think that should be the case. -- Andrei
Oct 18 2015
On Monday 05 October 2015 21:07, Meta wrote:There's a critical flaw in `scoped`. Observe: import std.stdio; import std.typecons; class A { string name; this(string name) { this.name = name; writeln("Creating A"); } ~this() { writeln("Destroying A"); } void hello() { writeln("Hello, ", this.name); } } void main() { auto a1 = scoped!A("Foo"); a1.hello(); A a2 = scoped!A("Foo"); a2.hello(); } The output: Creating A Hello, Foo Creating A Destroying A Destroying A object.Error: Access ViolationYou'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
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
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
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
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 DavisThe 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
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(){}". BitUser-defined `new` is already in the language, but it's deprecated.
Oct 05 2015
On Monday, 5 October 2015 at 19:02:59 UTC, Meta wrote:On Monday, 5 October 2015 at 18:18:15 UTC, bitwise wrote:I 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()" BitI think the solution to the above question is as simple as allowing RC classes to have something like "static T opNew(){}". BitUser-defined `new` is already in the language, but it's deprecated.
Oct 05 2015
On 10/4/2015 11:02 AM, bitwise wrote:For example, streams.No streams. InputRanges.
Oct 06 2015
On Wednesday, 7 October 2015 at 01:27:27 UTC, Walter Bright wrote:On 10/4/2015 11:02 AM, bitwise wrote: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. BitFor example, streams.No streams. InputRanges.
Oct 06 2015
On 10/6/2015 7:04 PM, bitwise wrote:On Wednesday, 7 October 2015 at 01:27:27 UTC, Walter Bright wrote:What can a stream do that a range cannot?On 10/4/2015 11:02 AM, bitwise wrote: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.For example, streams.No streams. InputRanges.
Oct 06 2015
On Wednesday, 7 October 2015 at 02:41:12 UTC, Walter Bright wrote:On 10/6/2015 7:04 PM, bitwise wrote: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. BitOn Wednesday, 7 October 2015 at 01:27:27 UTC, Walter Bright wrote:What can a stream do that a range cannot?On 10/4/2015 11:02 AM, bitwise wrote: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.For example, streams.No streams. InputRanges.
Oct 06 2015
On 10/6/2015 7:57 PM, bitwise wrote:It's what an OutputRange does.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.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
On Wednesday, 7 October 2015 at 05:13:06 UTC, Walter Bright wrote:On 10/6/2015 7:57 PM, bitwise wrote:I think that every nanometer this one gets moved up the todo list will be a big win. BitAgain 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 07 2015
On Wednesday, 7 October 2015 at 02:41:12 UTC, Walter Bright wrote:On 10/6/2015 7:04 PM, bitwise wrote: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?On Wednesday, 7 October 2015 at 01:27:27 UTC, Walter Bright wrote:What can a stream do that a range cannot?On 10/4/2015 11:02 AM, bitwise wrote: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.For example, streams.No streams. InputRanges.
Oct 07 2015
Am Tue, 6 Oct 2015 18:27:28 -0700 schrieb Walter Bright <newshound2 digitalmars.com>:On 10/4/2015 11:02 AM, bitwise wrote:... 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. -- MarcoFor example, streams.No streams. InputRanges.
Oct 08 2015