D - more or less power than C++?
Kevin Bealer
Kevin_member at pathlink.com
Fri Mar 3 20:34:12 PST 2006
First, let me say, that on balance, D is more powerful than C++ in many ways,
and moreso when implicit template etc gets here. But there are things C++ can
do that D can't. C++ templates and macros can do lots of things. I agree with
not putting C style macros in D; but to be fair, its still powerful, if ugly and
unsymmetric.
That said, I wanted to make a few points about Andrew's item #1...
In article <duaqm9$25at$1 at digitaldaemon.com>, Walter Bright says...
>
>"Andrew Fedoniouk" <news at terrainformatica.com> wrote in message
>news:duajvt$1re3$1 at digitaldaemon.com...
>>> What does D have to do to have more power than C++?
>> 1) C++ has ctors/dtors for stack allocated objects. This problem
>> is known in D as luck of struct ctor/dtor.
>
>The following:
> auto Foo f = new Foo();
>has ctor/dtor semantics. It isn't allocated on the stack at the moment, but
>there is no semantic reason why it couldn't be. Allocating it on the heap is
>an artifact of dmd, and not of the language.
Constructors and destructors in C++ have become something much larger than
memory cleanup. They provide a syntactic tool that plays almost the same role
as (and complements well with) assertions and contract programming.
For instance, in C++ I can write a mutex class like this (I'll use D notation):
class Mutex {
, void Lock();
, void Unlock();
, bool is_Locked();
};
,class MutexHolder {
, this(Mutex x);
,
, void Lock() { assert(! am_locked); x.Lock(); am_locked = true; }
, void Unlock() { assert(am_locked); x.Unlock(); am_locked = false; }
,
, ~this()
, {
, if (am_locked) {
, assert(my_x.isLocked());
, my_x.Unlock();
, }
, }
,}
If you insert enough assert() tests, you can virtually guarantee that you either
have perfect locking, or die (or hang) at the point of the first usage error.
But the ability to check for correct state, or unlock things, in the destructor
is CRUCIAL to this kind of testing. I worked for a few years in C language file
system code that did not have these guarantees. We were constantly hunting down
mysterious hangs. Doing locking via object lifetime is an excellent way to
enforce a kind of whole-system behavior -- that is what's powerful.
But D can't do this kind of thing, AFAIK. The RIIA works for a class, but I
can't nest classes this way. I can't tell class Foo that it owns Bar in "auto"
mode. I suspect that this would interact badly with "dup" if it was
implemented. I'm not sure what to do about that, unless there is an "opDup()"
method (I like this idea, but its another been-discussed idea.).
I think "auto" RIIA is a good start but to get the kind of RIIA that C++
designers want, you would need a minimum of these things too:
1. classes that can own others with an "auto" reference.
2. constructors / destructors for struct.
The second one is not really a requirement, but it almost is, because C++
designers use "lightweight" classes like nuts, particularly for this kind of
validity checking. In C++ adding a class, and using it, costs almost nothing,
and in some cases, nothing at all. D classes are just too heavy for this tiny
stuff, and D structs don't have quite enough features.
C++ programmers hate to have to buy the big package - the "Java" like class with
all the fixins. If you write a class in C++ to wrap an integer for some reason,
its only 4 bytes.
You can replace a set of integers (this is usually done if you have at least a
pair of ints) with "managed" ints or int-pairs, that guarantee some property.
If the guarantee is done via debug-mode-only testing, the class really does turn
into primitive types in release mode.
Unfortunately, if I have a container of D structs, getting one out and calling a
method, copies the value out - I can't set fields via "container[i].x = 5;" the
last time I checked.
The frustrating thing (for me) is that D "struct" seems like it can almost do
all this now. Add the constructor / destructor and you have 95% of the "light
struct" coverage.
I know this is not a new idea, so I apologize if I'm just repeating complaints.
If these destructors are too hard to do - that's okay. But I've never been able
to figure out where the difficulty is, except that "dup / auto" interaction
thing. I would think its just a matter of patching up the existing destructors
for classes containing MutexHolder and Mutex to call delete foo, and adding
similar code to function blocks.
I'm also thinking there is one restriction that may make this easier - if the
auto class could only be owned by a function scope (as currently) OR another
auto class - this would fix the unknown-delete-order issue for auto classes,
because it would mean that every auto class knew it was not garbage (and
inductively, its children arent garbage) at destruct time.
Lastly - it doesn't have to call "delete" or be a real destructor! Just some
way for the compiler to say "I think this is going out of scope now". If I had
something like auto that called a "cleanUp" method on all sub objects (but
especially structs, since other classes may have been collected first) when a
scope ends or a class is collected, that would be 99% of the cases.
Kevin
More information about the Digitalmars-d
mailing list