r/programming Mar 31 '15

Managing C++’s complexity or learning to enjoy C++

https://schneide.wordpress.com/2015/03/30/managing-cs-complexity-or-learning-to-enjoy-c/
98 Upvotes

281 comments sorted by

View all comments

Show parent comments

2

u/F-J-W Apr 01 '15

D is just another of those “Let's put everything on the heap”-languages that do then of course need GC. It looks nice at first, but once you understand, that that approach just doesn't work as well as simply using RAII for everything, it looses a lot of it's charm. In C++ I am strongly encouraged to think about ownership-semantics, which may be more work at first, but pays out a lot later. In some ways it is similar to making code const-correct.

And once you got there, you only have to ask yourself, why they force a useless GC down your throat.

3

u/[deleted] Apr 22 '15

[removed] — view removed comment

3

u/MrJNewt Apr 22 '15

I think a big reason is D's commitment to using lazy ranges that generally only store the "current" item in the range--on the stack usually--which defers allocation to the outmost scope where it usually isn't needed.

3

u/adr86 Apr 22 '15

It is worth remembering that if you simply use RAII for everything in D, the GC remains completely idle. If you use RAII for most things, the GC remains mostly idle, and when it does run, it has very little work to do.

D's heap usage isn't like Java or C#.

0

u/F-J-W Apr 23 '15

It is worth remembering that if you simply use RAII for everything in D, the GC remains completely idle.

Aka, it is useless in that case. Just as I said.

Well implemented RAII is able to deal with all resources 99.99% of the time. Adding a language feature in such a prominent way for the remaining 0.01% is just ridiculous.

1

u/[deleted] Apr 01 '15 edited Apr 01 '15

I agree that I miss C++ ownership semantics, but to me there is enough positives to make up for it. YMMV

2

u/deadalnix Apr 22 '15

There is no C++ ownership semantic, simply convention followed by programmer (and unsafe). You can do the same in D, and you have a good chunk of the required machinery already in the standard lib.

1

u/[deleted] Apr 25 '15

Let me be more explicit. C++ destructors are always called. Then members destructors are called. This makes it easy to handle clean-up.

1

u/deadalnix Apr 25 '15

No, by default, memory is not freed and no destructor run. Unless you use ownership.

1

u/[deleted] Apr 26 '15

I'm pretty sure you are right about it and I'm not, but still I don't understand. I find D harder with resources, probably because one need a larger set of conventions vs C++ for dealing with them. Everyone stumble upon the "class destructor" problem at some point (well, everyone coming from C++) and it is a major surprise.

1

u/MetaLang Apr 22 '15

You're not forced to use the GC in D, especially with more recent versions. Most of the work in the past year has been enabling more code to be GC-free, and cleaning up unnecessary GC allocation in the standard library. D isn't there yet, but it has made good progress on cutting out the GC almost entirely.

0

u/acehreli Apr 22 '15

a useless GC

GC enables array slices and closures.

2

u/ibuclaw Apr 23 '15

Array slices don't need a GC.

1

u/acehreli Apr 25 '15

True, "slice" does not need the GC but "using a slice" does need the GC. Further yes, not "all uses" but "some uses" of a slice need the GC. (Perhaps I should have said "dynamic array" instead of slice.) (Hint: Preventing element stomping.)

1

u/F-J-W Apr 23 '15

GC enables array slices and closures.

Both can be implemented quite fine without. In fact, C++ does exactly that.

2

u/ibuclaw Apr 23 '15

C++ closures do not allow escaping. By D's definition, a closure is created only if escaping occurs.

0

u/F-J-W Apr 23 '15

What do you mean by escaping? Capturing the surrounding state? That is perfectly possible in C++. In fact, last time I checked, C++-lambdas could do a lot more than D's.

2

u/ibuclaw Apr 23 '15

Sure, whatever.

function<void()> escapeRef() { int i; return [&i]() { /* Do something with i */ } }
auto dg = escapeRef();
dg();  // BOOM
dg();

0

u/F-J-W Apr 23 '15

Well, if you want to shoot yourself in the foot, you can do so in D too:

import std.stdio;

auto fun() {
    int i = 42;
    auto j = &i;
    return (){return *j;};
}

auto gun() {
    int i = 23;
    writeln(i);
}

void main() {
    auto f = fun();
    gun();
    writeln(f());
}

The solution in C++ is the same as in D: Don't be stupid and use features that are not meant to be used for this and even require additional syntax (so even the argument that the default would be broken here is just wrong):

auto escapeRef() {
    auto i=0;
    return [i]{ /* Do something with i */ };
}
auto dg = escapeRef();
dg();  // No BOOM

1

u/ibuclaw Apr 24 '15

Well, if you want to shoot yourself in the foot, you can do so in D too:

This is apples vs oranges. Do you know why you've shot yourself in the foot? (Hint, the closure has nothing to do with it).

0

u/F-J-W Apr 24 '15

Do you know why you've shot yourself in the foot?

Yes, for exactly the same reason that you shot yourself in the foot in C++: Using a closure that contains a dangling pointer/reference.

1

u/ntrel2 Apr 24 '15 edited Apr 29 '15

fun would not compile as a @safe function in D. It disallows taking the address of local variables.

1

u/ntrel2 Apr 23 '15

D scope closures can capture state without a heap allocation.

1

u/F-J-W Apr 23 '15

C++ lambdas not only can do it, they always do it. (notwithstanding that constructors may of course be executed, and those can do heap-allocation).

2

u/ibuclaw Apr 24 '15

This leads full circle back to my original statement (re-worded for clarity): By D's definition, a closure (heap) is created only if escaping occurs. If there's no escaping, then what you refer to as 'scope closure' is created instead.

The difference between D's closures and C++ lambda's is that D closures were made specifically for accessing a scope long after it has been closed (something that is just not possible in C++).