www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - Newbie: copy, assignment of class instances

reply "Larry Luther" <larry.luther dolby.com> writes:
	charset="iso-8859-1"
Content-Transfer-Encoding: quoted-printable

Given:

  class A {
    int x, y;
  }

  class B : A {
    int z;
  }

  B
    foo =3D new B,
    bar =3D new B;

  scope B
    alpha =3D new B;

Q1:  How do I copy the member variables contributed by base class A
     from "foo" to "bar"?
     In C++:  (A &) bar =3D foo;

Q2:  How do I do a deepcopy of foo to bar?
     In C++:  bar =3D foo;

Q3:  Is the object "alpha" on the stack (per documentation)?

Q4:  What happens when I do "alpha =3D foo;"?
     "printf( "%p\n", alpha);" indicates that the address of "alpha" has =
changed.

Thanks, Larry
May 20 2010
next sibling parent reply "Simen kjaeraas" <simen.kjaras gmail.com> writes:
Larry Luther <larry.luther dolby.com> wrote:

 Given:

   class A {
     int x, y;
   }

   class B : A {
     int z;
   }

   B
     foo = new B,
     bar = new B;

   scope B
     alpha = new B;

 Q1:  How do I copy the member variables contributed by base class A
      from "foo" to "bar"?
      In C++:  (A &) bar = foo;
You can't. If you need this functionality, use a specialized function.
 Q2:  How do I do a deepcopy of foo to bar?
      In C++:  bar = foo;
You don't. If you need this functionality, use a specialized function. This should, to keep symmetry with other D features, probably be called dup. Possibly deepdup, as the other dups don't do deep copy. It is also worth noting that 'bar = foo;' does not perform deep copying in C++, it only copies the immediate members, whereas deep copy would follow pointers and deep copy those, too. btw, if what you're referring to is not the deep copy explained above, but merely a shallow copy (of the instance, not the reference), the function should be called dup.
 Q3:  Is the object "alpha" on the stack (per documentation)?
Yes. Proof: void main( ) { A a = new A( ); scope A b = new A( ); writefln( "%p, %p, %p", cast(void*)a, cast(void*)b, cast(void*)&a ); }
 Q4:  What happens when I do "alpha = foo;"?
      "printf( "%p\n", alpha);" indicates that the address of "alpha" has  
 changed.
'scope B alpha;' allocates stack space for a reference to a B, and space for an instance of B. If the pointer changes, it is simply no longer referencing the instance on the stack. -- Simen
May 20 2010
parent "Larry Luther" <larry.luther dolby.com> writes:
Thank you, I had no idea that scope was doing this.
I thought that when the docs said that it was being allocated on the stack
that I was getting "struct" like behavior.

"Simen kjaeraas" <simen.kjaras gmail.com> wrote in message 
news:op.vc0qlpjovxi10f biotronic-pc.home...
| Larry Luther <larry.luther dolby.com> wrote:
|
| >
| >   scope B
| >     alpha = new B;
| >
|
| 'scope B alpha;' allocates stack space for a reference to a B, and
| space for an instance of B. If the pointer changes, it is simply no longer
| referencing the instance on the stack.
|
| -- 
| Simen 
May 27 2010
prev sibling next sibling parent reply bearophile <bearophileHUGS lycos.com> writes:
Larry Luther:

   class A {
     int x, y;
   }
 
   class B : A {
     int z;
   }
 
   B
     foo = new B,
     bar = new B;
 
   scope B
     alpha = new B;
Don't write code like this, it's not easy to write:
   B
     foo = new B,
     bar = new B;
Write it this way: B foo = new B, B bar = new B; Or this way: auto foo = new B, auto bar = new B;
 Q1:  How do I copy the member variables contributed by base class A
      from "foo" to "bar"?
      In C++:  (A &) bar = foo;
There is no standard way to do this. In general you don't need to copy classes or their contents, because they are on the heap and managed by the GC. If you want to copy their contents you have to add methods written by you that perform the copy you need.
 Q2:  How do I do a deepcopy of foo to bar?
      In C++:  bar = foo;
In general you don't do it. There is no standard way to perform a deep copy. You can write your code to do it, but it's not easy to write. So far I have never had to perform a deep copy on D classes. For structs take a look at postblit too: http://www.digitalmars.com/d/2.0/struct.html#StructPostblit
 Q3:  Is the object "alpha" on the stack (per documentation)?
In this case with ldc and dmd alpha is on the stack, you can see also reading the produced asm. On the docs you can read the rules that must be satisfied for scoped classes to be really allocated on the stack.
 Q4:  What happens when I do "alpha = foo;"?
      "printf( "%p\n", alpha);" indicates that the address of "alpha" has
changed.
alpha is on the stack, but beside alpha on the stack there is also the reference to alpha. So such reference gets rewritten. Generally in D it's not a good idea to reassign the reference to a scoped class (some months ago I have even suggested to turn this operation into a syntax error) because when the scope ends, the class referenced by the reference to alpha gets collected, so if you change such reference to something else, it is such something else that gets collected. I am not sure what happens here, but this can be a source of bad troubles. This is probably why Walter was thinking of removing the scope attribute for classes. Bye, bearophile
May 20 2010
next sibling parent reply bearophile <bearophileHUGS lycos.com> writes:
 Generally in D it's not a good idea to reassign the reference to a scoped
class 
You can see it with this little D2 program: import std.stdio: printf; class Foo { int x; this(int xx) { this.x = xx; } ~this() { printf("Foo(%d) destructor\n", this.x); } } void main() { scope Foo f1 = new Foo(1); Foo f2 = new Foo(2); f1 = f2; } It prints just: Foo(2) destructor In general this is not good. Bye, bearophile
May 20 2010
next sibling parent bearophile <bearophileHUGS lycos.com> writes:
http://d.puremagic.com/issues/show_bug.cgi?id=4214
May 20 2010
prev sibling parent "Simen kjaeraas" <simen.kjaras gmail.com> writes:
bearophile <bearophileHUGS lycos.com> wrote:

 Generally in D it's not a good idea to reassign the reference to a  
 scoped class
You can see it with this little D2 program: import std.stdio: printf; class Foo { int x; this(int xx) { this.x = xx; } ~this() { printf("Foo(%d) destructor\n", this.x); } } void main() { scope Foo f1 = new Foo(1); Foo f2 = new Foo(2); f1 = f2; } It prints just: Foo(2) destructor In general this is not good. Bye, bearophile
It is indeed not. However, there are two bugs here - first, the wrong instance's destructor is called. Second, f1's destructor is never called. Surely this has roots in the same code, but they're two separate issues. Basically, with the current system, it must not be the reference's task to destroy an instance at end of scope, but the instance itself. The simplest way to fix this is, as you say, to disallow reassignment of a scope class reference. -- Simen
May 20 2010
prev sibling parent reply "Larry Luther" <larry.luther dolby.com> writes:
"bearophile" <bearophileHUGS lycos.com> wrote in message 
news:ht42el$6c9$1 digitalmars.com...
  ...
| There is no standard way to do this. In general you don't need to copy 
classes or their contents, because they are on the heap and managed by the 
GC.

I'm nonplussed.

Could you expand on why D class instances don't need to copy their contents 
and
instances of D structs do?  While migrating C++ code to D I've had to 
convert
"struct"s to "class"es because of the need for inheritance.  Why would the 
need
to copy an instance's contents to another instance disappear?

Larry 
May 27 2010
parent bearophile <bearophileHUGS lycos.com> writes:
Larry Luther:

I'm nonplussed. Could you expand on why D class instances don't need to copy
their contents and instances of D structs do?  While migrating C++ code to D
I've had to convert "struct"s to "class"es because of the need for inheritance.
 Why would the need to copy an instance's contents to another instance
disappear?<

You are an experienced programmer, so if you think well about this topic you
can probably understand the situation and the purposes behind D design as well
or better than me. You probably know already what I can tell you about this
topic.

D is different from C++, it's GC-based, and this changes many things and the
way they have to be designed and used. D is designed to be a little "simpler"
than C++, this means in some cases it prefers to do something in a safer way,
instead in the most efficient way. D doesn't follow the zero overhead policy of

shown it's often a waste of programmers time with no real performance gain.

In D structs don't support inheritance because they are designed to avoid the
slicing bug, to be simpler, to be Plain Old Data. So D is designed to have
different classes and structs because their usage style and purposes are
different. When more complexity is needed, classes are there to be used. Even
if currently DMD is not very good at optimizing away the class-derived
overhead, better future D compilers can solve this. For some situations there's
even the "scope" (that Walter is not so sure to keep, while other people like
me have suggested the opposite, that is to extend its purpose to allocate an
object inside the memory of another object) that can help performance. In Java
the HotSpot is able to perform Escape Analysis on objects to avoid many heap
allocations, and LDC is able to detect some simple cases and do the same.

By design D has no standard way to copy classes. I have not written very large
object oriented D programs, but from what I have seen, I generally don't need
to copy objects. I have often the need to move them around, put them in a
collection, pull them out and put them in another collection, or create them in
single instance, and so on. But I don't remember the last time I've had to copy
a class instance in D. I just copy and add and remove class references. When I
want I can keep many references to the same object and so on. In C++ programs
this has to be done with care, because there is no GC, or you need some kind of
smart pointer, while in D (in theory) you can relax and just let the GC do its
thing.

My experience tells me that when you try to translate a program written in
language X to language Y you always find some impedance (unless X and Y are
almost the same language). But most times in language Y there are ways to solve
the problem in a different way. You, as programmer, have to learn the way Y
programs are usually written, its idioms and to avoid to force X idioms in Y
and then complain that X idioms are not well represented in Y.

D design is far from perfect, and in future some things will probably need to
be fixed (I have written enough text to fill two books about D features that
can enjoy some change), but behind its design there is also lot of thought. So,
you can show us some D code where you think there is a need to copy objects. If
such uncommon need arises you can usually write your own copy functions.

Bye,
bearophile
May 27 2010
prev sibling parent reply "Larry Luther" <larry.luther dolby.com> writes:
	charset="iso-8859-1"
Content-Transfer-Encoding: quoted-printable

Thank you for the clarifications.
I'm trying to do a shallow copy.

After reading Simen & bearophile, I would add a "copy" member function:

  class A {
    int x, y;

    void copy (in A a) {
      x =3D a.x;
      y =3D a.y;
    }
  }

  class B : A {
    int z;

    void copy (in B b) {
      super.copy( b);
      z =3D b.z;
    }
  }

  B foo =3D new B,
  B bar =3D new B;

Q:  Do I copy the member variables contributed by class A
    from "foo" to "bar", this way:  "(cast(A) bar).copy( foo);"?
    Or maybe "bar.A.copy( foo);"?

Larry
May 20 2010
next sibling parent reply bearophile <bearophileHUGS lycos.com> writes:
Larry Luther:

  B foo = new B,
  B bar = new B;
This is not valid D code, you have to use something like: B foo = new B; B bar = new B; I suggest you to actually try to compile and run your little test programs. If you compile this: class A { int x, y; void copy(A a) { x = a.x; y = a.y; } } class B : A { // line 10 int z; void copy(B b) { super.copy(b); z = b.z; } } void main() { B foo = new B; B bar = new B; } You get this (badly written) error: test.d(10): Error: class test2.B test2.A.copy(A a) is hidden by B This works: import std.stdio: writeln; import std.string: format; template ExceptionTemplate() { this() { super(this.classinfo.name); } this(string msg) { super(this.classinfo.name ~ ": " ~ msg); } } class ArgumentException: Exception { mixin ExceptionTemplate; } class A { int x, y; this(int xx, int yy) { this.x = xx; this.y = yy; } void copyFrom(Object other) { auto o = cast(typeof(this))other; if (o is null) throw new ArgumentException("Some message here..."); x = o.x; y = o.y; } override string toString() { return format("A(%d, %d)", this.x, this.y); } } class B : A { int z; this(int xx, int yy, int zz) { super(xx, yy); this.z = zz; } override void copyFrom(Object other) { auto o = cast(typeof(this))other; if (o is null) throw new Exception("..."); // use a more specific exception here super.copyFrom(other); z = o.z; } override string toString() { return format("B(%d, %d, %d)", this.x, this.y, this.z); } } void main() { B foo = new B(1, 2, 3); B bar = new B(4, 5, 6); writeln("foo = ", foo); writeln("bar = ", bar); foo.copyFrom(bar); writeln("foo = ", foo); writeln("bar = ", bar); } Output: foo = B(1, 2, 3) bar = B(4, 5, 6) foo = B(4, 5, 6) bar = B(4, 5, 6) An alternative implementation: import std.stdio: writeln; import std.string: format; template ExceptionTemplate() { this() { super(this.classinfo.name); } this(string msg) { super(this.classinfo.name ~ ": " ~ msg); } } class ArgumentException: Exception { mixin ExceptionTemplate; } class A { int x, y; this(int xx, int yy) { this.x = xx; this.y = yy; } void copyFrom(typeof(this) other) { x = other.x; y = other.y; } override string toString() { return format("A(%d, %d)", this.x, this.y); } } class B : A { int z; this(int xx, int yy, int zz) { super(xx, yy); this.z = zz; } override void copyFrom(typeof(super) other) { auto o = cast(typeof(this))other; if (o is null) throw new ArgumentException("Some message here..."); super.copyFrom(other); z = o.z; } override string toString() { return format("B(%d, %d, %d)", this.x, this.y, this.z); } } void main() { B foo = new B(1, 2, 3); B bar = new B(4, 5, 6); writeln("foo = ", foo); writeln("bar = ", bar); foo.copyFrom(bar); writeln("foo = ", foo); writeln("bar = ", bar); } In other languages an operator as "isa" replaces the less nice looking dynamic cast followed by null test. Bye, bearophile
May 20 2010
parent reply "Larry Luther" <larry.luther dolby.com> writes:
	charset="Windows-1252"
Content-Transfer-Encoding: quoted-printable

I did not get an error when building and running with DMD 2.042:

import std.stdio;


class A {
  int x, y;

  void copy (in A a) {
    x =3D a.x;
    y =3D a.y;
  }

  void dump (string s) {
    writefln( "%s.A =3D { %s, %s }", s, x, y);
  }
}

class B : A {
  int z;

  void copy (in B b) {
    super.copy( b);
    z =3D b.z;
  }

  void dump (string s) {
    super.dump( s);
    writefln( "%s.B =3D { %s }", s, z);
  }
}


void main () {
  B foo =3D new B;
  B bar =3D new B;

  foo.x =3D 3;
  foo.y =3D 5;
  foo.z =3D 17;

  bar.x =3D 7;
  bar.y =3D 11;
  bar.z =3D 13;

  foo.dump( "foo");
  bar.dump( "bar");

  bar.copy( foo);

}

Which prints:

foo.A =3D { 3, 5 }
foo.B =3D { 17 }
bar.A =3D { 7, 11 }
bar.B =3D { 13 }


May 20 2010
next sibling parent reply bearophile <bearophileHUGS lycos.com> writes:
Larry Luther:

 I did not get an error when building and running with DMD 2.042:
I am using dmd v2.046, and I have taken the good habit of compiling with -w (warnings on). It seems this error I see is a blocking warning. It's a warning badly written, but do you agree it is saying something important? I presume your code can lead to bugs. I don't know why this is a warning instead of a true error... On your code it also complains for a missed "override" that is a good thing that eventually will become obligatory even without -w. When you post here I suggest you to avoid using HTML and use pure text. Bye, bearophile
May 20 2010
next sibling parent bearophile <bearophileHUGS lycos.com> writes:
Another bug report:

http://d.puremagic.com/issues/show_bug.cgi?id=4216
May 20 2010
prev sibling parent reply "Larry Luther" <larry.luther dolby.com> writes:
"bearophile" <bearophileHUGS lycos.com> wrote in message 
news:ht4g3r$vue$1 digitalmars.com...
| Larry Luther:
|
| > I did not get an error when building and running with DMD 2.042:
|
| I am using dmd v2.046, and I have taken the good habit of compiling 
with -w (warnings on).
| It seems this error I see is a blocking warning. It's a warning badly 
written, but do you agree it is saying something important? I presume your 
code can lead to bugs. I don't know why this is a warning instead of a true 
error...
|
| On your code it also complains for a missed "override" that is a good 
thing that eventually will become obligatory even without -w.
|
| When you post here I suggest you to avoid using HTML and use pure text.
|
| Bye,
| bearophile

Ok, I've added -w to compilation commands and I've switched back to pure 
text.

Given:

class A {
  int x, y;

  void copy (const A a) {
    x = a.x;
    y = a.y;
  }
}

class B : A {
  int z;

  void copy (const B b) {
    super.copy( b);
    z = b.z;
  }
}

A alpha = new A;
A bravo = new A;

B charlie = new B;
B delta = new B;

-------------------------------

I don't see the problem with A.copy vs B.copy.
  alpha.copy( bravo)    -- should execute A.copy
  alpha.copy( charlie)  -- should execute A.copy
  charlie.copy( delta)  -- should execute B.copy
  charlie.copy( alpha)  -- should execute A.copy

What am I missing?

Larry
May 27 2010
parent reply bearophile <bearophileHUGS lycos.com> writes:
Larry Luther:

Ok, I've added -w to compilation commands and I've switched back to pure text.<
Good :-)
What am I missing?<
I have modified a bit your D code like this, to have something with a main() that runs: import std.c.stdio: puts; class A { int x, y; void copy(const A a) { puts("A copy"); x = a.x; y = a.y; } } class B : A { int z; void copy(const B b) { puts("B copy"); super.copy(b); z = b.z; } } void main() { A a1 = new A; A a2 = new A; B b1 = new B; B b2 = new B; a1.copy(a2); // should execute A.copy a1.copy(b1); // should execute A.copy b1.copy(b2); // should execute B.copy b1.copy(a1); // should execute A.copy } I have also translated your the code to Java, because sometimes Java designers are more "correct" thant D designers: class A { int x, y; void mycopy(A a) { System.out.println("A mycopy"); x = a.x; y = a.y; } } class B extends A { int z; void mycopy(B b) { System.out.println("B mycopy"); super.mycopy(b); z = b.z; } public static void main(String[] args) { A a1 = new A(); A a2 = new A(); B b1 = new B(); B b2 = new B(); a1.mycopy(a2); // should execute A.mycopy a1.mycopy(b1); // should execute A.mycopy b1.mycopy(b2); // should execute B.mycopy b1.mycopy(a1); // should execute A.mycopy } } The Java code compiles and runs with no errors, and prints: A mycopy A mycopy B mycopy A mycopy A mycopy But the D version is different. It seems you have found a small difference between Java and D that I didn't know about. If I comment out the last line of the main() in the D code (b1.copy(a1);) and I compile the D code with -w it generates the warning I was talking about: test.d(13): Error: class test.B test.A.copy(const const(A) a) is hidden by B If I leave that line uncommented then the compilation stops with a different error: test.d(33): Error: function test.B.copy (const const(B) b) is not callable using argument types (A) test.d(33): Error: cannot implicitly convert expression (a1) of type test.A to const(B) It seems in D the copy() of B replaces (hides) the copy() of A, even if no override is used. I don't know why D is designed this way, it can even be a design/implementation bug. But the presence of that warning suggests me this is expected, so it's probably just a difference between Java and D. If no one answers to this here then maybe later I will ask about this in the main D group. Bye, bearophile
May 27 2010
next sibling parent bearophile <bearophileHUGS lycos.com> writes:
See:
http://www.digitalmars.com/webnews/newsgroups.php?art_group=digitalmars.D&article_id=110554
May 27 2010
prev sibling parent reply =?UTF-8?B?QWxpIMOHZWhyZWxp?= <acehreli yahoo.com> writes:
bearophile wrote:
 Larry Luther:
 
 Ok, I've added -w to compilation commands and I've switched back to pure text.<
Good :-)
 What am I missing?<
I have modified a bit your D code like this, to have something with a main() that runs: import std.c.stdio: puts; class A { int x, y; void copy(const A a) { puts("A copy"); x = a.x; y = a.y; } } class B : A { int z; void copy(const B b) { puts("B copy"); super.copy(b); z = b.z; } } void main() { A a1 = new A; A a2 = new A; B b1 = new B; B b2 = new B; a1.copy(a2); // should execute A.copy a1.copy(b1); // should execute A.copy b1.copy(b2); // should execute B.copy b1.copy(a1); // should execute A.copy } I have also translated your the code to Java, because sometimes Java designers are more "correct" thant D designers: class A { int x, y; void mycopy(A a) { System.out.println("A mycopy"); x = a.x; y = a.y; } } class B extends A { int z; void mycopy(B b) { System.out.println("B mycopy"); super.mycopy(b); z = b.z; } public static void main(String[] args) { A a1 = new A(); A a2 = new A(); B b1 = new B(); B b2 = new B(); a1.mycopy(a2); // should execute A.mycopy a1.mycopy(b1); // should execute A.mycopy b1.mycopy(b2); // should execute B.mycopy b1.mycopy(a1); // should execute A.mycopy } } The Java code compiles and runs with no errors, and prints: A mycopy A mycopy B mycopy A mycopy A mycopy But the D version is different. It seems you have found a small difference between Java and D that I didn't know about. If I comment out the last line of the main() in the D code (b1.copy(a1);) and I compile the D code with -w it generates the warning I was talking about: test.d(13): Error: class test.B test.A.copy(const const(A) a) is hidden by B If I leave that line uncommented then the compilation stops with a different error: test.d(33): Error: function test.B.copy (const const(B) b) is not callable using argument types (A) test.d(33): Error: cannot implicitly convert expression (a1) of type test.A to const(B) It seems in D the copy() of B replaces (hides) the copy() of A, even if no override is used. I don't know why D is designed this way, it can even be a design/implementation bug. But the presence of that warning suggests me this is expected, so it's probably just a difference between Java and D. If no one answers to this here then maybe later I will ask about this in the main D group. Bye, bearophile
For what it's worth, here is a code that uses 'dup' instead of 'copy'. I know this is not the same thing; but I find this more intuitive: import std.stdio; import std.string; class A { int x, y; this(int x, int y) { this.x = x; this.y = y; } A dup() const { writeln("A copy"); return new A(x, y); } override string toString() const { return format("A(%s,%s)", x, y); } } class B : A { int z; this(int x, int y, int z) { super(x, y); this.z = z; } override B dup() const { writeln("B copy"); return new B(x, y, z); } override string toString() const { return format("B(%s,%s,%s)", x, y, z); } } void main() { A a1 = new A(1, 1); A a2 = new A(2, 2); B b1 = new B(11, 11, 11); B b2 = new B(22, 22, 22); a1 = a2.dup; assert((a1.x == 2) && (a1.y == 2)); a1 = b1.dup; assert((a1.x == 11) && (a1.y == 11)); assert(typeid(a1) == typeid(B)); // <-- personality change b1 = b2.dup; assert((b1.x == 22) && (b1.y == 22) && (b1.z == 22)); // b1 = a1.dup; // <-- ERROR because not all As are Bs; // but as we know that a1 is // actually a B at this point; // we can down cast: assert(cast(B)a1 !is null); b1 = (cast(B)a1).dup; assert((b1.x == 11) && (b1.y == 11) && (b1.z == 11)); writeln(a1); writeln(a2); writeln(b1); writeln(b2); } Ali
May 27 2010
parent bearophile <bearophileHUGS lycos.com> writes:
Ali:
 For what it's worth, here is a code that uses 'dup' instead of 'copy'.
Oh, right. I have forgotten to say this to the OP. In D it's better to name it dup instead of copy.
I know this is not the same thing; but I find this more intuitive:<
I agree, it's better for D.
      A dup() const
Probably this is better, seeing your usage of it: property A dup() const Bye, bearophile
May 27 2010
prev sibling parent reply bearophile <bearophileHUGS lycos.com> writes:
You are a newbie, so let me point the little flaws in your D code, this is the
D style guide:
http://www.digitalmars.com/d/2.0/dstyle.html

Regarding white space it says:
    * Use spaces instead of hardware tabs.
    * Each indentation level will be four columns.

Bye,
bearophile
May 20 2010
next sibling parent reply Ellery Newcomer <ellery-newcomer utulsa.edu> writes:
On 05/20/2010 06:23 PM, bearophile wrote:
 You are a newbie, so let me point the little flaws in your D code, this is the
D style guide:
 http://www.digitalmars.com/d/2.0/dstyle.html

 Regarding white space it says:
      * Use spaces instead of hardware tabs.
      * Each indentation level will be four columns.

 Bye,
 bearophile
Says the python nazi <g>
May 20 2010
parent bearophile <bearophileHUGS lycos.com> writes:
Ellery Newcomer:

 Says the python nazi <g>
I admit that I am more indentation-nazi after using Python for few years, but I was strict on indentation even before hearing of Python :-) (In past I used 2 spaces, for older monitors). I have not written that html page was written by Walter before seeing me in the D newsgroups :-) Having a well chosen and uniform style in a language is useful, it helps readability between different programmers of the D community of programmers. You can also take a look at the quite strict C++ style guide (they forbid tabs, but use 2 spaces). 4 spaces is better than 2 in D today because monitors are wider, and because too many indentations in the code (more than five or six) is probably a sign of badly written code (here I am talking about production code, not toy programs). Bye, bearophile
May 20 2010
prev sibling parent reply "Larry Luther" <larry.luther dolby.com> writes:
Ok, Ok,
  I'm a newbie to D, not to programming.  The first computer I got to 
program was a Digital PDP-8L.
  I'm learning D.
  I'm learning how to use SlickEdit as an IDE for D.
     I've never used it to compile, debug, and execute before.
     Therefore it will take a while before I can figure out where to add -w
       (Yes I like to turn on all warnings too).
  I'm relearning OutlookExpress for reading news.  I wanted a fixed width 
font
    and couldn't configure OutlookExpress intuitively.  The IT guy said use 
HTML
    and that worked, but you don't like it, so I've gotten heavy handed with 
OutlookExpress
    and it looks like I have it.  I don't have any option for using spaces 
for indentation,
    (my preference too).  The OutlookExpress editor must be replacing my 
spaces with tabs.
  The Association of Computing Machinery did a study of indentation a long 
time ago
    and found that indentations of 2 or 3 were best.  I picked 2.

"bearophile" <bearophileHUGS lycos.com> wrote in message 
news:ht4ga0$1099$1 digitalmars.com...
| You are a newbie, so let me point the little flaws in your D code, this is 
the D style guide:
| http://www.digitalmars.com/d/2.0/dstyle.html
|
| Regarding white space it says:
|    * Use spaces instead of hardware tabs.
|    * Each indentation level will be four columns.
|
| Bye,
| bearophile 
May 20 2010
parent reply bearophile <bearophileHUGS lycos.com> writes:
Larry Luther:

   I'm a newbie to D, not to programming.  The first computer I got to 
 program was a Digital PDP-8L.
Wow :-) On the base of your long experience do you like D so far?
   I'm learning D.
And I am listing what I think can be improved in your D code :-) You are free to not follow my advice...
 The IT guy said use HTML
     and that worked, but you don't like it,
It's the Web interface of the D groups that doesn't seem to like HTML :-)
The OutlookExpress editor must be replacing my  spaces with tabs.
I have seen 2 spaces in your code.
   The Association of Computing Machinery did a study of indentation a long 
 time ago
     and found that indentations of 2 or 3 were best.  I picked 2.
I have shown you the D style guide written by the Walter, the D main designer. Some people don't follow that style guide, but I like it and I think a language-community-wide style guide is important for a modern language. I too used to use 2 spaces religiously, but today monitors have 120+ columns, not 80 as (probably) when that ACM study was carried out. Things change as time goes on. If you use 2 columns it's a bit harder to see indentations and you can be more tempted to use many indentation levels in your code. Keeping four is a way to discourage the usage of too many indentation levels. In the end you are free to use the number of indentations you like in D snippets :-) Don't get upset, we are all learning :-) There are many things in D that I have yet to understand. Bye, bearophile
May 20 2010
parent reply "Larry Luther" <larry.luther dolby.com> writes:
"bearophile" <bearophileHUGS lycos.com> wrote in message 
news:ht4krg$17l9$1 digitalmars.com...
| On the base of your long experience do you like D so far?

There are many things that I like and I strongly agree with the failings
of C++ mentioned in the docs.  I don't like the asymmetry between structs
and classes.  I don't see why structs can't have inheritance.  I haven't
had a memory leak problem in C++ for many years so the need for a GC seems 
minor.
I can only assume that it's needed to support strings and dynamic arrays.
I'm pushing forward on the assumption that I'll discover the paradigm that
will make everything fall into place.  It's hard to get the proper picture
from the documentation available on Digital-mars.  "Tango with D" didn't
significantly help either.  For example it took a great deal of time to 
learn
that "private" can be used several ways:

  private member_function () {)

  private {
    member_function () {}
  }

  private:
    member_function () {}

  I'm anxiously waiting for something of the quality of the "Annotated C++ 
Reference Manual".

Larry 
May 27 2010
next sibling parent bearophile <bearophileHUGS lycos.com> writes:
Larry Luther:

 There are many things that I like and I strongly agree with the failings
 of C++ mentioned in the docs.
D is designed by people that have a good experience of C++, but while probably D avoids some C++ problems, it surely introduces a number of new issues :-)
 I don't like the asymmetry between structs
 and classes.  I don't see why structs can't have inheritance.
One of the first answers given here is that this D design avoids slicing bugs. See my other answer for more.
 I haven't
 had a memory leak problem in C++ for many years so the need for a GC seems 
 minor.
 I can only assume that it's needed to support strings and dynamic arrays.
A GC introduces other kind of leaks when there is a reference alive to an memory block that is supposed to be dead and not used any more. This can even be caused by the not precise nature of the current D GC, it can mismatch something for a pointer to a GC-managed memory zone, keeping it alive. For example in programs that use large associative arrays this seems a problem. If you assume the presence of the GC this changes the way you write code. In theory you can be more relaxed. In practice D GC is not... well, you have to keep your eyes open anyway.
 I'm pushing forward on the assumption that I'll discover the paradigm that
 will make everything fall into place.
If you have some Java programming experience then you probably have nothing to discover regarding this aspect of D programming (here the main difference is that Oracle Java GC is more precise and quite more efficient).
 For example it took a great deal of time to 
 learn that "private" can be used several ways:
One of the good things of D design is that it tries to be uniform/consistent, in this case all other attributes can be used in the same ways :-)
   I'm anxiously waiting for something of the quality of the "Annotated C++ 
 Reference Manual".
The problem here is that D is not very strictly defined in the first place, so it's harder to write a very strict D reference manual :-) But Andrei book is about to come out, that can be seen as a kind of official D2 reference. Bye, bearophile
May 27 2010
prev sibling parent reply "Steven Schveighoffer" <schveiguy yahoo.com> writes:
On Thu, 27 May 2010 17:04:35 -0400, Larry Luther <larry.luther dolby.com>  
wrote:

 "bearophile" <bearophileHUGS lycos.com> wrote in message
 news:ht4krg$17l9$1 digitalmars.com...
 | On the base of your long experience do you like D so far?

 There are many things that I like and I strongly agree with the failings
 of C++ mentioned in the docs.  I don't like the asymmetry between structs
 and classes.  I don't see why structs can't have inheritance.
Because of the slicing problem. It's basically something like this: struct A {virtual void foo();}; struct B : A {virtual void foo();}; void bar(A a) { a.foo(); } void baz() { B b; bar(b); // b is "sliced" down to an A, and bar will now call A.foo instead of the expected B.foo. } The really bad part about this is, b might have set up its private variables so that to call A.foo would cause an error. Same thing happens when returning by value. The general issue is that inheritance and value types don't mix. But reference types (that is, types that are always passed by reference) never have the slicing problem. So classes in D (which are reference types) can inherit, while structs (which *can be* value types) cannot inherit. I have hoped that at some point, structs can be auto-composed, without a vtable, but you still have to do this manually. Luckily, it's not so much of a chore now that alias this is around.
  I haven't
 had a memory leak problem in C++ for many years so the need for a GC  
 seems
 minor.
 I can only assume that it's needed to support strings and dynamic arrays.
 I'm pushing forward on the assumption that I'll discover the paradigm  
 that
 will make everything fall into place.
Yes, what it took for me is to write a project in C++ that could really have used a GC. Essentially, here was my issue: I had a protocol implemented with various messages with a function readMessage, which returned a newly-allocated message (which was a derivative of some base message type). Then I used RTTI to cast the message to the right type to deal with the data. However, what sucked is how I always had to free the message after receiving it. I really just wanted to process the message and go to the next one. A GC is great for this because memory management is not strewn throughout your code, you don't have to remember to free things you should free and leave things you should not. On top of that, in my case, I had to figure out that I was responsible for freeing a message via documentation -- the language didn't really tell me. In D, no matter where it comes from, you just forget about it, and whoever is responsible (GC or owner) cleans it up later. It makes for much more readable and less error-prone code. There are many other designs which work well with GC, and some which don't. D has some power over the memory management so you can force your will upon it, but I've found that the best way to write D code is to embrace the GC. Note also that memory leaks are not the worst problem with non-GC code. Freeing memory you weren't supposed to is worse.
   I'm anxiously waiting for something of the quality of the "Annotated  
 C++
 Reference Manual".
The D Programming Language is hitting bookstores soon, I think it will be a very good reference and educational book. -Steve
May 27 2010
next sibling parent reply bearophile <bearophileHUGS lycos.com> writes:
Steven Schveighoffer:
 I have hoped that at some point, structs can be auto-composed,
 without a vtable, but you still have to do this manually.
I don't understand what you mean here :-) Bye, bearophile
May 27 2010
parent reply "Steven Schveighoffer" <schveiguy yahoo.com> writes:
On Thu, 27 May 2010 17:47:20 -0400, bearophile <bearophileHUGS lycos.com>  
wrote:

 Steven Schveighoffer:
 I have hoped that at some point, structs can be auto-composed,
 without a vtable, but you still have to do this manually.
I don't understand what you mean here :-)
I mean simple inheritance. In C, there has always been manual inheritance. You can see it with the sockaddr system, and even with the X toolkit widget system. essentially, when you derive type B from type A in C++, you get this: struct B { A _a; } A is always put first, that way, a pointer to a B can always be used as a pointer to an A. The other thing that happens is that function calls on B also use A as well. This is not so easy in C, but in D it is currently quite trivial: struct B { A _a; alias _a this; } Then a call like b.methodOfA(); gets translated statically to b._a.methodOfA(). But there are things I don't like about this, such as you can *set* _a. To get around that, you define a property getter, but not a setter: struct B { private A _a; property ref A a() {return _a;} alias a this; } What I would like is a common-sense approach to inheritance for structs that just does not allow virtual methods or interfaces, and which does not cast implicitly to the base (explicit cast is OK). I think some designs would benefit greatly from this simple feature. I think it's more tricky than I've described, but I think with some diligence it can be done. -Steve
May 27 2010
parent reply bearophile <bearophileHUGS lycos.com> writes:
Thank you Steven for your explanations, I have done similar things in C and D,
but I didn't understand what you meant.

A is always put first, that way, a pointer to a B can always be used as a
pointer to an A.<
Are you sure C specs say doing this leads to defined behaviour? (Recently from a discussion with Walter I have learnt that D follows exactly C specs regarding such defined/undefined things.)
 What I would like is a common-sense approach to inheritance for structs  
 that just does not allow virtual methods or interfaces, and which does not  
 cast implicitly to the base (explicit cast is OK).  I think some designs  
 would benefit greatly from this simple feature.  I think it's more tricky  
 than I've described, but I think with some diligence it can be done.
I have desired some form of inheritance in D structs (before the creation of alias this, that so far I have not used much). I think Walter will not love this idea, but you can think more about its details, and then you can post it in the main D newsgroup. Bye, bearophile
May 27 2010
parent "Steven Schveighoffer" <schveiguy yahoo.com> writes:
On Thu, 27 May 2010 18:41:19 -0400, bearophile <bearophileHUGS lycos.com>  
wrote:

 Thank you Steven for your explanations, I have done similar things in C  
 and D, but I didn't understand what you meant.

 A is always put first, that way, a pointer to a B can always be used as  
 a pointer to an A.<
Are you sure C specs say doing this leads to defined behaviour? (Recently from a discussion with Walter I have learnt that D follows exactly C specs regarding such defined/undefined things.)
Xt and the Berkeley sockets library relies on it. If it's not in the spec, then it's a de-facto standard. I think the defined behavior is that the first member of a struct is positioned at the same address as the struct itself.
 What I would like is a common-sense approach to inheritance for structs
 that just does not allow virtual methods or interfaces, and which does  
 not
 cast implicitly to the base (explicit cast is OK).  I think some designs
 would benefit greatly from this simple feature.  I think it's more  
 tricky
 than I've described, but I think with some diligence it can be done.
I have desired some form of inheritance in D structs (before the creation of alias this, that so far I have not used much). I think Walter will not love this idea, but you can think more about its details, and then you can post it in the main D newsgroup.
Sadly, I think it is not much more than a wish. I don't think Walter's mind can be changed on this. -Steve
May 27 2010
prev sibling parent reply "Larry Luther" <larry.luther dolby.com> writes:
"Steven Schveighoffer" <schveiguy yahoo.com> wrote in message 
news:op.vddvhs17eav7ka localhost.localdomain...
| On Thu, 27 May 2010 17:04:35 -0400, Larry Luther <larry.luther dolby.com>
| wrote:
|
| > "bearophile" <bearophileHUGS lycos.com> wrote in message
| > news:ht4krg$17l9$1 digitalmars.com...
| > | On the base of your long experience do you like D so far?
| >
| > There are many things that I like and I strongly agree with the failings
| > of C++ mentioned in the docs.  I don't like the asymmetry between 
structs
| > and classes.  I don't see why structs can't have inheritance.
|
| Because of the slicing problem.  It's basically something like this:
|
| struct A {virtual void foo();};
|
| struct B : A {virtual void foo();};
|
| void bar(A a)
| {
|   a.foo();
| }
|
| void baz()
| {
|   B b;
|   bar(b); // b is "sliced" down to an A, and bar will now call A.foo
| instead of the expected B.foo.
| }
|
| The really bad part about this is, b might have set up its private
| variables so that to call A.foo would cause an error.
|
| Same thing happens when returning by value.  The general issue is that
| inheritance and value types don't mix.  But reference types (that is,
| types that are always passed by reference) never have the slicing
| problem.  So classes in D (which are reference types) can inherit, while
| structs (which *can be* value types) cannot inherit.  I have hoped that at
| some point, structs can be auto-composed, without a vtable, but you still
| have to do this manually.  Luckily, it's not so much of a chore now that
| alias this is around.
...

I have verified the slicing problem within C++.
The problem disappears (in accordance with your statements) if "bar"
is declared such that it's argument is passed by reference.
The bottom line is that C++ overcame this sufficiently to allow
inheritance for structs. 
Jun 02 2010
parent "Steven Schveighoffer" <schveiguy yahoo.com> writes:
On Wed, 02 Jun 2010 13:47:35 -0400, Larry Luther <larry.luther dolby.com>  
wrote:

 I have verified the slicing problem within C++.
 The problem disappears (in accordance with your statements) if "bar"
 is declared such that it's argument is passed by reference.
 The bottom line is that C++ overcame this sufficiently to allow
 inheritance for structs.
C++ did not overcome it, as you just discovered, it still can happen. What they did is put a band-aid on it, and said "always use the band-aid." That is not a good solution. Plus, you must always use references to truly solve the problem. In D classes are always passed by reference, so the problem cannot occur. Structs cannot have inheritance so the problem cannot occur. In D, the problem cannot occur, so D has successfully overcome the problem. BTW, from Day 1, C++ structs and classes are equivalent except for one notion -- the default protection in classes is private, the default protection in structs is public. There is no other difference. So it's not like they waited to add inheritance to structs until they added references to solve the slicing problem. -Steve
Jun 02 2010
prev sibling parent bearophile <bearophileHUGS lycos.com> writes:
Larry Luther:
     void copy (in A a) {
In D2 it's better to use "const" or "immutable" instead of "in". Here's one version of the code const-aware: import std.stdio: writeln; import std.string: format; template ExceptionTemplate() { this() { super(this.classinfo.name); } this(string msg) { super(this.classinfo.name ~ ": " ~ msg); } } class ArgumentException: Exception { mixin ExceptionTemplate; } class A { int x, y; this(int xx, int yy) { this.x = xx; this.y = yy; } void copyFrom(const Object other) { const o = cast(typeof(this))other; if (o is null) throw new ArgumentException("Some message here..."); x = o.x; y = o.y; } override string toString() const { return format("A(%d, %d)", this.x, this.y); } } class B : A { int z; this(int xx, int yy, int zz) { super(xx, yy); this.z = zz; } override void copyFrom(const Object other) { const o = cast(typeof(this))other; if (o is null) throw new ArgumentException("Some message here..."); super.copyFrom(other); z = o.z; } override string toString() const { return format("B(%d, %d, %d)", this.x, this.y, this.z); } } void main() { B foo = new B(1, 2, 3); B bar = new B(4, 5, 6); writeln("foo = ", foo); writeln("bar = ", bar); foo.copyFrom(bar); writeln("foo = ", foo); writeln("bar = ", bar); } Bye, bearophile
May 20 2010