www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - what was wrong with struct & class in C++?

reply BC <NOTmi_emayl_adrez hotmail.com.remove.not> writes:
I thought it was a great idea to allow both value semantics
and pointer semantics for the same types in C++ by the simple
addition of a * or &. What was wrong with that that needed
to be fixed in D? GC could have been done without changing
this. Now in D template code doesn't know which
semantics apply and structs can't inherit. If you write
something as a struct and then realise you need inheritance
you're stuck. D has so many good ideas it's a shame to see
it broken in this way...
Dec 12 2007
next sibling parent Russell Lewis <webmaster villagersonline.com> writes:
BC wrote:
 I thought it was a great idea to allow both value semantics
 and pointer semantics for the same types in C++ by the simple
 addition of a * or &. What was wrong with that that needed
 to be fixed in D? GC could have been done without changing
 this. Now in D template code doesn't know which
 semantics apply and structs can't inherit. If you write
 something as a struct and then realise you need inheritance
 you're stuck. D has so many good ideas it's a shame to see
 it broken in this way...

I 99% agree with you, but I would like to debate just a smidgeon. I dislike the fact that in C++ (unless you're very careful) your "structs" are really heavyweight classes (vtbl, etc.) I like the fact that in D you can explicitly differentiate between heavyweight entities (classes) and lightweight ones (structs). But I very much agree that for clarity, consistency, and template-ease, the syntax for "pointer/reference to a class" should be identical to the syntax for "pointer/reference to a struct". So I would argue that there was something broken with C++ structs and classes. D fixed it, but then broke something else. :(
Dec 12 2007
prev sibling next sibling parent reply Gregor Richards <Richards codu.org> writes:
BC wrote:
 I thought it was a great idea to allow both value semantics
 and pointer semantics for the same types in C++ by the simple
 addition of a * or &. What was wrong with that that needed
 to be fixed in D? GC could have been done without changing
 this. Now in D template code doesn't know which
 semantics apply and structs can't inherit. If you write
 something as a struct and then realise you need inheritance
 you're stuck. D has so many good ideas it's a shame to see
 it broken in this way...

No. No no no no no. I hate seeing this argument. structs are a shim. They are a way of getting lower-level access to the organization of memory. In C++, this is ANYTHING but simple: class A { ... } void someidiotfunction(A a); void someidiotfunction2(A *a); void someidiotfunction3(A &a); You now have three ways of passing an "object", with different semantics and different possible results! In proper object oriented design, objects are contexts. It makes no sense to duplicate contexts. Contexts are an abstraction of an environment in which to perform actions, and it makes no sense to duplicate that environment whenever you happen to pass it from one function to another (oh except when you don't). D is not the problem. C++'s insistence on forcing details of implementation down the users' throat is the problem. - Gregor Richards
Dec 12 2007
next sibling parent Russell Lewis <webmaster villagersonline.com> writes:
Gregor Richards wrote:
 BC wrote:
 I thought it was a great idea to allow both value semantics
 and pointer semantics for the same types in C++ by the simple
 addition of a * or &. What was wrong with that that needed
 to be fixed in D? GC could have been done without changing
 this. Now in D template code doesn't know which
 semantics apply and structs can't inherit. If you write
 something as a struct and then realise you need inheritance
 you're stuck. D has so many good ideas it's a shame to see
 it broken in this way...

No. No no no no no. I hate seeing this argument. structs are a shim. They are a way of getting lower-level access to the organization of memory.

In an abstract, academic sense, maybe you're right. As a practical matter, they are (in D, at least) a way to organize your data without the runtime overhead of classes. At least, that is one perfectly valid way to use them.
 In C++, this is ANYTHING but simple:
 
 class A {
 ...
 }
 
 void someidiotfunction(A a);
 void someidiotfunction2(A *a);
 void someidiotfunction3(A &a);
 
 You now have three ways of passing an "object", with different semantics 
 and different possible results!
 
 In proper object oriented design, objects are contexts. It makes no 
 sense to duplicate contexts. Contexts are an abstraction of an 
 environment in which to perform actions, and it makes no sense to 
 duplicate that environment whenever you happen to pass it from one 
 function to another (oh except when you don't).

I proposed a simple solution to this a while ago: disallow copying of classes, including passing them by value. So, in your example above, I would propose that only the 2nd function should be valid, and the other 2 should be syntax errors. It's simple. It's consistent with structs. It enforces the "don't copy a context" paradigm you are talking about. And it trivially teaches a newbie how to do things in D: Newbie: Write code that passes classes by value Compiler: Syntax Error! You must pass classes by pointer Newbie: Hmm. OK. Can do. Ofc, we'll get newbies on the newsgroup flaming us, asking why pass-class-by-value is disallowed, and we'll have to teach them. But currently, what we have is a design that looks good at first (when writing trivial code) but which gets ugly when you write advanced code (like templates, or refactoring).
Dec 12 2007
prev sibling parent reply Martin Hinsch <m.hinsch rug.nl> writes:
On Wed, 12 Dec 2007 12:24:44 -0800, Gregor Richards wrote:

 BC wrote:
 I thought it was a great idea to allow both value semantics and pointer
 semantics for the same types in C++ by the simple addition of a * or &.
 What was wrong with that that needed to be fixed in D? GC could have
 been done without changing this. Now in D template code doesn't know
 which semantics apply and structs can't inherit. If you write something
 as a struct and then realise you need inheritance you're stuck. D has so
 many good ideas it's a shame to see it broken in this way...

No. No no no no no. I hate seeing this argument. structs are a shim. They are a way of getting lower-level access to the organization of memory. In C++, this is ANYTHING but simple: class A { ... } } void someidiotfunction(A a); void someidiotfunction2(A *a); void someidiotfunction3(A &a); You now have three ways of passing an "object", with different semantics and different possible results! In proper object oriented design, objects are contexts. It makes no sense to duplicate contexts. Contexts are an abstraction of an environment in which to perform actions, and it makes no sense to duplicate that environment whenever you happen to pass it from one function to another (oh except when you don't). D is not the problem. C++'s insistence on forcing details of implementation down the users' throat is the problem. - Gregor Richards

I don't mean to be offensive, but I totally don't see the point. Of course C++ structs (being inherited from C) are too low-level. Still, I would argue that this is a property of C++ not of value-semantics. And who decides a) what "proper object-oriented design" means, b) that it's inherently a great thing and c) that it is tied to reference semantics? IMO it makes perfect sense to treat objects as just a more complicated kind of value. Look at the following code: int a=1; a++; int b = a + 10; In this piece of code the variable a is used once as a reference (i.e. modified in place) and once as a value (i.e. copied). In the same way it can make sense for "real" objects to use them as values in some operations and as references in others. You can easily make up a way to specify which way to use a function arg (like, say, 'in' and 'inout') without having to know anything about values or references. To sum it up - I think although C++ structs + pointers + references is messy, that doesn't mean that OOP and value vs. reference semantics aren't completely orthogonal concepts. And by the way this is the main point keeping me from using D (and the fact that the gcc frontend is so slow in catching up). Since I started to read the newsgroup I got the impression that the special syntactic treatment of object references has been *the* major hurdle for many of the more difficult/controversial language extensions (const comes to mind). cheers Martin -- Martin Hinsch m.hinsch rug.nl +31 50 363 8097 CEES Centre for Ecological and Evolutionary Studies Biologisch Centrum Kerklaan 30 Postbus 14 9750 AA Haren
Dec 12 2007
next sibling parent reply Gregor Richards <Richards codu.org> writes:
Martin Hinsch wrote:
 On Wed, 12 Dec 2007 12:24:44 -0800, Gregor Richards wrote:
 
 BC wrote:
 I thought it was a great idea to allow both value semantics and pointer
 semantics for the same types in C++ by the simple addition of a * or &.
 What was wrong with that that needed to be fixed in D? GC could have
 been done without changing this. Now in D template code doesn't know
 which semantics apply and structs can't inherit. If you write something
 as a struct and then realise you need inheritance you're stuck. D has so
 many good ideas it's a shame to see it broken in this way...

No. No no no no no. I hate seeing this argument. structs are a shim. They are a way of getting lower-level access to the organization of memory. In C++, this is ANYTHING but simple: class A { ... } } void someidiotfunction(A a); void someidiotfunction2(A *a); void someidiotfunction3(A &a); You now have three ways of passing an "object", with different semantics and different possible results! In proper object oriented design, objects are contexts. It makes no sense to duplicate contexts. Contexts are an abstraction of an environment in which to perform actions, and it makes no sense to duplicate that environment whenever you happen to pass it from one function to another (oh except when you don't). D is not the problem. C++'s insistence on forcing details of implementation down the users' throat is the problem. - Gregor Richards

I don't mean to be offensive, but I totally don't see the point. Of course C++ structs (being inherited from C) are too low-level. Still, I would argue that this is a property of C++ not of value-semantics. And who decides a) what "proper object-oriented design" means,

Consensus and history.
 b) that it's
 inherently a great thing

This is irrelevant. Whether OO is good or not, D aims to be OO.
 and c) that it is tied to reference semantics?

Consensus. C++ is popular, but it is the exception to the rule in this regard. Virtually no other language has this bizarre property.
 IMO it makes perfect sense to treat objects as just a more complicated
 kind of value. Look at the following code:
 
 int a=1;
 a++;
 int b = a + 10;

This code has no function calls. And, if it was all implemented with operator overloading, it would be the same whether it had value or reference semantics: Integer a = new Integer(/*magic internal data representing the value 1*/); a.opIncrement(); /* no imperative language would implement this as a * copy, as then all objects would be immutable */ Integer b = a.opAdd(new Integer(10)); /* whether the integer is copied * or not, a returns a new one, it * has no reason to modify its * argument */ In other words, this code is irrelevant.
 
 In this piece of code the variable a is used once as a reference (i.e.
 modified in place) and once as a value (i.e. copied).

No it isn't.
 In the same way it
 can make sense for "real" objects to use them as values in some operations
 and as references in others.

Intrinsic values do not mutate in the same way that objects do. In an object oriented sense, they're immutable. When you say a = a + 5, it's rebinding the variable a to a new value, computed as a + 5. The actual data representing its original value is not modified, a is simply rebound. This has the same result whether by-value or by-reference. Because they're immutable, there's no distinction between by-value and by-reference passing. If all the intrinsic types were actually by-ref objects, there would be no difference. (Note: a++ and ++a are sort of exceptions to this rule)
 You can easily make up a way to specify which
 way to use a function arg (like, say, 'in' and 'inout') without having to
 know anything about values or references.

Yes - D already has one.
 To sum it up - I think although C++ structs + pointers + references is
 messy, that doesn't mean that OOP and value vs. reference semantics aren't
 completely orthogonal concepts.
 And by the way this is the main point keeping me from using D

Seriously? You are /way/ too much of a C++'er. Go use any other OO language, and you will find your opinion very quickly in the minority. "I don't mean to be offensive," but that's just silly.
 (and the
 fact that the gcc frontend is so slow in catching up). Since I started
 to read the newsgroup I got the impression that the special syntactic
 treatment of object references has been *the* major hurdle for many of the
 more difficult/controversial language extensions (const comes to mind).

This is a purely syntactic hurdle, and the giant ugly mess that is by-value objects is not worth the slightly simpler syntax. Which, incidentally, would be more complicated syntax in basically every other scenario.
 
 cheers
 Martin
 

- Gregor Richards
Dec 12 2007
next sibling parent Kirk McDonald <kirklin.mcdonald gmail.com> writes:
Gregor Richards wrote:
 Martin Hinsch wrote:
 IMO it makes perfect sense to treat objects as just a more complicated
 kind of value. Look at the following code:

 int a=1;
 a++;
 int b = a + 10;

This code has no function calls. And, if it was all implemented with operator overloading, it would be the same whether it had value or reference semantics: Integer a = new Integer(/*magic internal data representing the value 1*/); a.opIncrement(); /* no imperative language would implement this as a * copy, as then all objects would be immutable */ Integer b = a.opAdd(new Integer(10)); /* whether the integer is copied * or not, a returns a new one, it * has no reason to modify its * argument */ In other words, this code is irrelevant.

I feel strangely compelled to describe Python's behavior in this case, although I admit I'm not entirely clear what the point is. In Python, integers are immutable objects.
 a = 1 # (1)
 a += 1 # (2) Python has no increment/decrement operators
 b = a + 10 # (3)



(1) Binds an int object with the value 1 to the name 'a'. (2) In-place addition is strange in Python. Numbers, being immutable, cannot actually be modified. Instead, 'a += 1' actually creates a new int object [see note 1] with the value 2, and binds that object to the name 'a'. (Thus removing the old reference to the object with the value 1.) (3) Does exactly what you'd expect: Creates a new int object [see note 1] with the value 12, and binds it to the name 'b'. Notes: [1] In fact, it doesn't create a new object: Integer objects below 100 are cached and reused for performance reasons. So here it's /really/ grabbing an object from the cache. This is considered an implementation detail, however, and is usually best ignored. -- Kirk McDonald http://kirkmcdonald.blogspot.com Pyd: Connecting D and Python http://pyd.dsource.org
Dec 12 2007
prev sibling next sibling parent Martin Hinsch <m.hinsch rug.nl> writes:
On Wed, 12 Dec 2007 14:59:16 -0800, Gregor Richards wrote:

 Martin Hinsch wrote:
 On Wed, 12 Dec 2007 12:24:44 -0800, Gregor Richards wrote:
 


[snip]
 In C++, this is ANYTHING but simple:

 class A {
 ...
 }
 }
 void someidiotfunction(A a);
 void someidiotfunction2(A *a);
 void someidiotfunction3(A &a);

 You now have three ways of passing an "object", with different
 semantics and different possible results!

 In proper object oriented design, objects are contexts. It makes no
 sense to duplicate contexts. Contexts are an abstraction of an
 environment in which to perform actions, and it makes no sense to
 duplicate that environment whenever you happen to pass it from one
 function to another (oh except when you don't).

 D is not the problem. C++'s insistence on forcing details of
 implementation down the users' throat is the problem.

   - Gregor Richards

I don't mean to be offensive, but I totally don't see the point. Of course C++ structs (being inherited from C) are too low-level. Still, I would argue that this is a property of C++ not of value-semantics. And who decides a) what "proper object-oriented design" means,

Consensus and history.

So, you're basically saying follow the crowd since everybody does...
 
 b) that it's
 inherently a great thing

This is irrelevant. Whether OO is good or not, D aims to be OO.

I thought D had the much more modest goal to be the best language ever ;-).
 
 and c) that it is tied to reference semantics?

Consensus. C++ is popular, but it is the exception to the rule in this regard. Virtually no other language has this bizarre property.

see above. I don't think that consensus is a good argument (except maybe in politics).
 IMO it makes perfect sense to treat objects as just a more complicated
 kind of value. Look at the following code:
 
 int a=1;
 a++;
 int b = a + 10;

This code has no function calls. And, if it was all implemented with

I see no reasons to treat operators anything different than as sugarified function calls. [snip]
 
 Intrinsic values do not mutate in the same way that objects do. In an
 object oriented sense, they're immutable. When you say a = a + 5, it's
 rebinding the variable a to a new value, computed as a + 5. The actual
 data representing its original value is not modified, a is simply
 rebound. This has the same result whether by-value or by-reference.
 Because they're immutable, there's no distinction between by-value and
 by-reference passing. If all the intrinsic types were actually by-ref
 objects, there would be no difference.
 
 (Note: a++ and ++a are sort of exceptions to this rule)

Ok, you have sort of a point. I think what I was talking about was mutable vs. immutable objects which is a kind of related (and interesting as well) but still different topic.
 
 You can easily make up a way to specify which way to use a function arg
 (like, say, 'in' and 'inout') without having to know anything about
 values or references.

Yes - D already has one.

I know, I was trying (and obviously failing ;-) to be sarcastic. Anyways, the point I was trying to make is that your example of all the different possibilities to pass objects as function arguments in C++ has nothing to do with values vs. references but rather with the fact that C++ (as you said) doesn't properly abstract from implementation details. Therefore you have to "construct" the different ways to pass arguments (modifiable/non-modifiable) from the low-level machinery (i.e. pointers) or the slightly more high-level half-hearted add-on (i.e. references (&)). If the language offers you a way to properly describe the semantics of argument passing this becomes a non-issue.
 
 To sum it up - I think although C++ structs + pointers + references is
 messy, that doesn't mean that OOP and value vs. reference semantics
 aren't completely orthogonal concepts. And by the way this is the main
 point keeping me from using D

Seriously? You are /way/ too much of a C++'er. Go use any other OO language, and you will find your opinion very quickly in the minority.

so, minority == wrong? Actually my first OO language was Objective C which is as referency as you can get without going directly to SmallTalk. I've also done quite some programming in Java and Ruby both of which also belong to this category (albeit one with static and the other one with dynamic typing). I really liked both, Ruby and Objective C (I strongly dislike Java but for entirely different reasons). Still, the distinction between object references on one hand and plain type values on the other hand always seemed quite artificial to me. I think it makes kind of sense in a dynamic, "scripty" language where you would like to ignore types most of the time. As soon as you enter the statically typed world there is in my opinion not really a good reason anymore to make objects references per default. You can get everything you want from OOP (data hiding, inheritance, polymorphism) with value types as default. [snip]
 This is a purely syntactic hurdle, and the giant ugly mess that is
 by-value objects is not worth the slightly simpler syntax. Which,
 incidentally, would be more complicated syntax in basically every other
 scenario.

Please explain (seriously). Martin Hinsch
Dec 12 2007
prev sibling parent reply BC <NOTmi_emayl_adrez hotmail.com.remove.not> writes:
On Wed, 12 Dec 2007 22:59:16 -0000, Gregor Richards <Richards codu.org> =
 =

wrote:

 Martin Hinsch wrote:
 On Wed, 12 Dec 2007 12:24:44 -0800, Gregor Richards wrote:

 BC wrote:
 I thought it was a great idea to allow both value semantics and  =




 pointer
 semantics for the same types in C++ by the simple addition of a * o=




 &.
 What was wrong with that that needed to be fixed in D? GC could hav=




 been done without changing this. Now in D template code doesn't kno=




 which semantics apply and structs can't inherit. If you write  =




 something
 as a struct and then realise you need inheritance you're stuck. D h=




 so
 many good ideas it's a shame to see it broken in this way...

No. No no no no no. I hate seeing this argument. structs are a shim. They are a way of getting lower-level access to =



 organization of memory.

 In C++, this is ANYTHING but simple:

 class A {
 ...
 }
 }
 void someidiotfunction(A a);
 void someidiotfunction2(A *a);
 void someidiotfunction3(A &a);

 You now have three ways of passing an "object", with different  =



 semantics
 and different possible results!

 In proper object oriented design, objects are contexts. It makes no =



 sense
 to duplicate contexts. Contexts are an abstraction of an environment=



 which to perform actions, and it makes no sense to duplicate that
 environment whenever you happen to pass it from one function to anot=



 (oh except when you don't).

 D is not the problem. C++'s insistence on forcing details of
 implementation down the users' throat is the problem.



I don't see how C++ is forcing anything. C++ is the one that gives you the choice. If I want two closely related types to be viewed through a common interface or to share implementation in D, I have to accept reference semantics as well, everywhere. If you have an aversion to by-value objects you can always stick to pointers in C++. Use an alias. We're not trying to take anything away, it's an addition. a question: should matrices be structs or classes?
   - Gregor Richards



 course
 C++ structs (being inherited from C) are too low-level. Still, I woul=


 argue that this is a property of C++ not of value-semantics. And who
 decides a) what "proper object-oriented design" means,

Consensus and history.
 b) that it's
 inherently a great thing

This is irrelevant. Whether OO is good or not, D aims to be OO.
 and c) that it is tied to reference semantics?

Consensus. C++ is popular, but it is the exception to the rule in this=

 regard. Virtually no other language has this bizarre property.

 IMO it makes perfect sense to treat objects as just a more complicate=


 kind of value. Look at the following code:
  int a=3D1;
 a++;
 int b =3D a + 10;

This code has no function calls. And, if it was all implemented with =

 operator overloading, it would be the same whether it had value or  =

 reference semantics:

 Integer a =3D new Integer(/*magic internal data representing the value=

 1*/);
 a.opIncrement(); /* no imperative language would implement this as a
                    * copy, as then all objects would be immutable */
 Integer b =3D a.opAdd(new Integer(10)); /* whether the integer is copi=

                                         * or not, a returns a new one,=

                                         * has no reason to modify its
                                         * argument */

 In other words, this code is irrelevant.

  In this piece of code the variable a is used once as a reference (i.=


 modified in place) and once as a value (i.e. copied).

No it isn't.
 In the same way it
 can make sense for "real" objects to use them as values in some  =


 operations
 and as references in others.

Intrinsic values do not mutate in the same way that objects do. In an =

 object oriented sense, they're immutable. When you say a =3D a + 5, it=

 rebinding the variable a to a new value, computed as a + 5. The actual=

 data representing its original value is not modified, a is simply  =

 rebound. This has the same result whether by-value or by-reference.  =

 Because they're immutable, there's no distinction between by-value and=

 by-reference passing. If all the intrinsic types were actually by-ref =

 objects, there would be no difference.

 (Note: a++ and ++a are sort of exceptions to this rule)

along with a-- --a +=3D -=3D *=3D /=3D %=3D &=3D |=3D ~=3D ^=3D <<=3D >>= =3D >>>=3D
 You can easily make up a way to specify which
 way to use a function arg (like, say, 'in' and 'inout') without havin=


 to
 know anything about values or references.

Yes - D already has one.
 To sum it up - I think although C++ structs + pointers + references i=


 messy, that doesn't mean that OOP and value vs. reference semantics  =


 aren't
 completely orthogonal concepts.
 And by the way this is the main point keeping me from using D

Seriously? You are /way/ too much of a C++'er. Go use any other OO =

 language, and you will find your opinion very quickly in the minority.=

 "I don't mean to be offensive," but that's just silly.

 (and the
 fact that the gcc frontend is so slow in catching up). Since I starte=


 to read the newsgroup I got the impression that the special syntactic=


 treatment of object references has been *the* major hurdle for many o=


 the
 more difficult/controversial language extensions (const comes to mind=


 This is a purely syntactic hurdle, and the giant ugly mess that is  =

 by-value objects is not worth the slightly simpler syntax. Which,  =

 incidentally, would be more complicated syntax in basically every othe=

 scenario.

  cheers
 Martin

- Gregor Richards

Dec 12 2007
next sibling parent reply Yigal Chripun <yigal100 gmail.com> writes:
 a question: should matrices be structs or classes?

I'd like to clarify your question: do you want to ask - a) whether they should be value types or reference types? or b) whether they should be implemented with D structs or D classes? if you mean a than it really doesn't matter. you are quite right that you can implement OOP features with both reference and value semantics. doing both is redundant because they achieve the same goal. i.e. passing by value achieves the same as const reference and passing the address of the value achieves the same as passing by mutable reference. so it's just a question of implementation and most languages choose reference semantics due to the more efficient memory use. so in this case the consensus is the better option. also note that you can mix-n-match: you can implement one semantic with the other: ref to value: you can have a dup method (clone in java, but you knew that already) although it should be avoided and probably isn't needed in the general case. value to ref: you can pass addresses of values to simulate references. if you meant b than i think that classes should be used. I'm new to D, but from what little i know, if you want OOP behavior (and matrices would benefit from polymorphism) than you need to use a class and not a struct. the semantics shouldn't matter as it's a matter of implementation and not interface. (ideally you'd just use "in" and "inout" and let the compiler optimize when you pass around parameters). if you want a matrix to be a value type you can simulate that with a dup method, i think. D structs should only be used for PODs. a good example would be a struct containing an internet address where you need it to be in a specific memory layout (endian-ness). also, if you care about heap allocation vs. stack, than you could always use scope classes which would be stack based. that's all from a theoretical POV of course, practically, there maybe other issues that influence that decision. also const still has a few kinks that need to be sorted out in D. Just my 2 cents..
Dec 12 2007
next sibling parent BC <NOTmi_emayl_adrez hotmail.com.remove.not> writes:
On Thu, 13 Dec 2007 01:51:13 -0000, Yigal Chripun <yigal100 gmail.com>  
wrote:

  > a question: should matrices be structs or classes?

 I'd like to clarify your question:
 do you want to ask -
 a) whether they should be value types or reference types?
 or
 b) whether they should be implemented with D structs or D classes?

i should have been clearer, i meant a. although i'd argue it's the same question.
 if you mean a than it really doesn't matter.
 you are quite right that you can implement OOP features with both  
 reference and value semantics. doing both is redundant because they  
 achieve the same goal.
 i.e. passing by value achieves the same as const reference and passing  
 the address of the value achieves the same as passing by mutable  
 reference.
 so it's just a question of implementation and most languages choose  
 reference semantics due to the more efficient memory use.
 so in this case the consensus is the better option.

 also note that you can mix-n-match: you can implement one semantic with  
 the other:
 ref to value: you can have a dup method (clone in java, but you knew  
 that already) although it should be avoided and probably isn't needed in  
 the general case.

in template code, you have to dup everything, just in case. and then it may not have a dup method, or a copy constructor (which aren't generated automatically, and if it has both which do you use?)
 value to ref: you can pass addresses of values to simulate references.

but then you need to change everything to pointer syntax. it would be nice to sneak in references as opposed to pointers. i suppose this comes under the header of 'c++ dirty tricks'
 if you meant b than i think that classes should be used.
 I'm new to D, but from what little i know, if you want OOP behavior (and  
 matrices would benefit from polymorphism) than you need to use a class  
 and not a struct.

 the semantics shouldn't matter as it's a matter of implementation and  
 not interface. (ideally you'd just use "in" and "inout" and let the  
 compiler optimize when you pass around parameters).
 if you want a matrix to be a value type you can simulate that with a dup  
 method, i think.

it's so easy to miss one dup out and get very weird behaviour. code working on matrices with dups all over the place is not something i relish. i don't know, i'm not entirely convinced of my own argument. i just think things could be 'excellent' or envelope-pushing rather than just 'ok'.
 D structs should only be used for PODs. a good example would be a struct  
 containing an internet address where you need it to be in a specific  
 memory layout (endian-ness).

i think there's a distinction between something that's a value type and something that's POD. matrices, by their nature, are value types - their identity is irrelevant. but they could do with inheritance, or at least interfaces.
 also, if you care about heap allocation vs. stack, than you could always  
 use scope classes which would be stack based.

 that's all from a theoretical POV of course, practically, there maybe  
 other issues that influence that decision. also const still has a few  
 kinks that need to be sorted out in D.

 Just my 2 cents..

Dec 12 2007
prev sibling parent reply Don Clugston <dac nospam.com.au> writes:
Yigal Chripun wrote:
  > a question: should matrices be structs or classes?
 
 I'd like to clarify your question:
 do you want to ask -
 a) whether they should be value types or reference types?
 or
 b) whether they should be implemented with D structs or D classes?

 I'm new to D, but from what little i know, if you want OOP behavior (and 
 matrices would benefit from polymorphism) 

How? What matrix operations are polymorphic?
Dec 13 2007
parent reply Yigal Chripun <yigal100 gmail.com> writes:
Don Clugston wrote:
 Yigal Chripun wrote:
  > a question: should matrices be structs or classes?

 I'd like to clarify your question:
 do you want to ask -
 a) whether they should be value types or reference types?
 or
 b) whether they should be implemented with D structs or D classes?

 I'm new to D, but from what little i know, if you want OOP behavior 
 (and matrices would benefit from polymorphism) 

How? What matrix operations are polymorphic?

i can't think of any... you're right, my bad. so in that case it does makes sense to use D structs. My point was that for polymorphic types you need to use classes only, and that can be much simpler to achieve with reference semantics by default (although that's just an implementation detail). for me, the distinction between PODs and "objects" should be the polymorphic behavior, not the size of it (maybe i didn't explain myself properly). Am i completely wrong here?
Dec 13 2007
parent reply Walter Bright <newshound1 digitalmars.com> writes:
Yigal Chripun wrote:
 for me, the distinction between PODs and "objects" should be the 
 polymorphic behavior, not the size of it (maybe i didn't explain myself 
 properly). Am i completely wrong here?

You're right. Any object designed for inheritance or polymorphism should properly be a reference type.
Dec 13 2007
parent reply Christopher Wright <dhasenan gmail.com> writes:
Walter Bright wrote:
 Yigal Chripun wrote:
 for me, the distinction between PODs and "objects" should be the 
 polymorphic behavior, not the size of it (maybe i didn't explain 
 myself properly). Am i completely wrong here?

You're right. Any object designed for inheritance or polymorphism should properly be a reference type.

Is this because it is too difficult to implement polymorphism for value types safely and efficiently right now? There's the slicing problem, of course, but there should be ways around that: worst case, you could rebuild the current stack frame each time you assign a polymorphic struct, but the effects on performance would be unpleasant. More happily, you could handle polymorphic structs like classes, but copy on (non-const) assignment. That would be allowed in D, if you could overload T.opAssign(T).
Dec 14 2007
parent reply Walter Bright <newshound1 digitalmars.com> writes:
Christopher Wright wrote:
 That would be allowed in D, if you could overload T.opAssign(T).

C++ still has well-known slicing problems, even with overloading assignment. The question is not "can this be done", it's more "is there a compelling reason to support such behavior". I think the answer is no. Do you really want a language where a class designer feels compelled to define an opassign overload, then make it private to prevent people from using the class as a value type? Where the base class designer needs to know what the derived classes are doing so he can make the destructor virtual or not? Where you cannot have arrays of base classes?
Dec 15 2007
next sibling parent reply Bill Baxter <dnewsgroup billbaxter.com> writes:
Walter Bright wrote:
 Christopher Wright wrote:
 That would be allowed in D, if you could overload T.opAssign(T).

C++ still has well-known slicing problems, even with overloading assignment. The question is not "can this be done", it's more "is there a compelling reason to support such behavior". I think the answer is no. Do you really want a language where a class designer feels compelled to define an opassign overload, then make it private to prevent people from using the class as a value type? Where the base class designer needs to know what the derived classes are doing so he can make the destructor virtual or not? Where you cannot have arrays of base classes?

At least C++ lets you control copying behavior completely in all circumstances. And documents what you are supposed to do clearly. Right now D gives you the choice of 1) [structs] copy by value only, with no option to customize or intercept copy operations 2) [classes] no defined copy behavior whatsoever 1) makes interesting ref counting wrappers etc impossible, but I think you're planning to fix that one. I think 2) is a problem also. It means that developers will each come up with their own way to copy objects because there's no clear way to do it. I think some particular scheme should be crowned, like any class that wants to be copyable should provide a the pair of methods dup(), and copy(ThisType). And maybe there should be something in Object to this effect also, even if they don't do much of anything. Looks like java implements a clone() method in the base Object class. But it looks like that just makes it so you get a runtime exception if the class you're trying to clone doesn't support cloning. That doesn't seem like an improvement over a compile time error. So I'm not really sure what should be done, but I definitely think something should be done to specify "the D way" to polymorphically copy objects. Built-ins mostly have .dup properties, but I don't think the spec actually says anywhere that user classes that want to be copyable should have a .dup. But even specifying a .dup is not enough I think, because if I derive from some class A, I have to create my derived class, then get class A to copy the A parts of the new object somehow, say via a method like A.copy(A copy_from). C++ may have problems regarding copying classes, but D's solution is effectively to just remove the C++ functionality good and bad. Ok the slicing problem is gone, but so is copying. There should be one obvious way to make classes in D copyable, whether it be enforced by the language, compiler, or simply the spec and D community. --bb
Dec 16 2007
parent reply Yigal Chripun <yigal100 gmail.com> writes:
Bill Baxter wrote:
 Walter Bright wrote:
 Christopher Wright wrote:
 That would be allowed in D, if you could overload T.opAssign(T).

C++ still has well-known slicing problems, even with overloading assignment. The question is not "can this be done", it's more "is there a compelling reason to support such behavior". I think the answer is no. Do you really want a language where a class designer feels compelled to define an opassign overload, then make it private to prevent people from using the class as a value type? Where the base class designer needs to know what the derived classes are doing so he can make the destructor virtual or not? Where you cannot have arrays of base classes?

At least C++ lets you control copying behavior completely in all circumstances. And documents what you are supposed to do clearly. Right now D gives you the choice of 1) [structs] copy by value only, with no option to customize or intercept copy operations 2) [classes] no defined copy behavior whatsoever 1) makes interesting ref counting wrappers etc impossible, but I think you're planning to fix that one. I think 2) is a problem also. It means that developers will each come up with their own way to copy objects because there's no clear way to do it. I think some particular scheme should be crowned, like any class that wants to be copyable should provide a the pair of methods dup(), and copy(ThisType). And maybe there should be something in Object to this effect also, even if they don't do much of anything. Looks like java implements a clone() method in the base Object class. But it looks like that just makes it so you get a runtime exception if the class you're trying to clone doesn't support cloning. That doesn't seem like an improvement over a compile time error. So I'm not really sure what should be done, but I definitely think something should be done to specify "the D way" to polymorphically copy objects. Built-ins mostly have .dup properties, but I don't think the spec actually says anywhere that user classes that want to be copyable should have a .dup. But even specifying a .dup is not enough I think, because if I derive from some class A, I have to create my derived class, then get class A to copy the A parts of the new object somehow, say via a method like A.copy(A copy_from). C++ may have problems regarding copying classes, but D's solution is effectively to just remove the C++ functionality good and bad. Ok the slicing problem is gone, but so is copying. There should be one obvious way to make classes in D copyable, whether it be enforced by the language, compiler, or simply the spec and D community. --bb

In java you also need to implement a clonable interface which is a marker interface. I think that adding a clonable interface to the standard library should be enough. something like: --- interface clonable { object dup(); } --- now you need to implement it to support copying. I don't think a dup method should be added to object. as you said, it isn't really an improvement, and usually Java experts recommend avoiding clone(). also, i don't see why a copy method is required. for example, check the following code: --- class Base : clonable { Base dup() { auto ret = cast(Base) this.classinfo.create; ret.x = this.x; ret.y = this.y; return ret; } int x = 6; double y = 8; } class DerivedA : Base { override DerivedA dup() { auto ret = cast(DerivedA) super.dup; ret.w = this.w; return ret; } long w = 42; }
Dec 16 2007
parent reply Bill Baxter <dnewsgroup billbaxter.com> writes:
Yigal Chripun wrote:
 Bill Baxter wrote:
 Walter Bright wrote:
 Christopher Wright wrote:
 That would be allowed in D, if you could overload T.opAssign(T).

C++ still has well-known slicing problems, even with overloading assignment. The question is not "can this be done", it's more "is there a compelling reason to support such behavior". I think the answer is no. Do you really want a language where a class designer feels compelled to define an opassign overload, then make it private to prevent people from using the class as a value type? Where the base class designer needs to know what the derived classes are doing so he can make the destructor virtual or not? Where you cannot have arrays of base classes?

At least C++ lets you control copying behavior completely in all circumstances. And documents what you are supposed to do clearly. Right now D gives you the choice of 1) [structs] copy by value only, with no option to customize or intercept copy operations 2) [classes] no defined copy behavior whatsoever 1) makes interesting ref counting wrappers etc impossible, but I think you're planning to fix that one. I think 2) is a problem also. It means that developers will each come up with their own way to copy objects because there's no clear way to do it. I think some particular scheme should be crowned, like any class that wants to be copyable should provide a the pair of methods dup(), and copy(ThisType). And maybe there should be something in Object to this effect also, even if they don't do much of anything. Looks like java implements a clone() method in the base Object class. But it looks like that just makes it so you get a runtime exception if the class you're trying to clone doesn't support cloning. That doesn't seem like an improvement over a compile time error. So I'm not really sure what should be done, but I definitely think something should be done to specify "the D way" to polymorphically copy objects. Built-ins mostly have .dup properties, but I don't think the spec actually says anywhere that user classes that want to be copyable should have a .dup. But even specifying a .dup is not enough I think, because if I derive from some class A, I have to create my derived class, then get class A to copy the A parts of the new object somehow, say via a method like A.copy(A copy_from). C++ may have problems regarding copying classes, but D's solution is effectively to just remove the C++ functionality good and bad. Ok the slicing problem is gone, but so is copying. There should be one obvious way to make classes in D copyable, whether it be enforced by the language, compiler, or simply the spec and D community. --bb

In java you also need to implement a clonable interface which is a marker interface. I think that adding a clonable interface to the standard library should be enough. something like: --- interface clonable { object dup(); } --- now you need to implement it to support copying.

Is there really any benefit in making it an interface in D since you can just do a static if check to see if it has a dup method?
 I don't think a dup 
 method should be added to object. as you said, it isn't really an 
 improvement, and usually Java experts recommend avoiding clone().

What do you mean they recommend avoiding it? What do they recommend instead?
 also, i don't see why a copy method is required.
 for example, check the following code:
 ---
 class Base : clonable {
     Base dup() {
        auto ret = cast(Base) this.classinfo.create;
        ret.x = this.x;
        ret.y = this.y;
        return ret;
     }
     int x = 6;
     double y = 8;
 }
 
 class DerivedA : Base {
     override DerivedA dup() {
        auto ret = cast(DerivedA) super.dup;
        ret.w = this.w;
        return ret;
     }
     long w = 42;
 }

Ah ha! Ok, that's great. This is exactly what I was looking for over on the thread I started on D.learn ("implementing dup/clone for class hierarchies"). Steven suggested using classinfo.create, but he left his solution using a separate copy() method (or I just misunderstood him), and that just ends up in a lot of casting. But your method seems to work. So that seems good. The trouble then is just that it is not obvious and there are no examples in the documentation showing how to do this. For now I've added this example to the wiki DocComments page for 'class': http://www.prowiki.org/wiki4d/wiki.cgi?DocComments/Class#section8 But I really think an example like this belongs in the main doc. --bb
Dec 16 2007
parent reply Bill Baxter <dnewsgroup billbaxter.com> writes:
Bill Baxter wrote:
 Yigal Chripun wrote:
 Bill Baxter wrote:
 Walter Bright wrote:
 Christopher Wright wrote:
 That would be allowed in D, if you could overload T.opAssign(T).

C++ still has well-known slicing problems, even with overloading assignment. The question is not "can this be done", it's more "is there a compelling reason to support such behavior". I think the answer is no. Do you really want a language where a class designer feels compelled to define an opassign overload, then make it private to prevent people from using the class as a value type? Where the base class designer needs to know what the derived classes are doing so he can make the destructor virtual or not? Where you cannot have arrays of base classes?

At least C++ lets you control copying behavior completely in all circumstances. And documents what you are supposed to do clearly. Right now D gives you the choice of 1) [structs] copy by value only, with no option to customize or intercept copy operations 2) [classes] no defined copy behavior whatsoever 1) makes interesting ref counting wrappers etc impossible, but I think you're planning to fix that one. I think 2) is a problem also. It means that developers will each come up with their own way to copy objects because there's no clear way to do it. I think some particular scheme should be crowned, like any class that wants to be copyable should provide a the pair of methods dup(), and copy(ThisType). And maybe there should be something in Object to this effect also, even if they don't do much of anything. Looks like java implements a clone() method in the base Object class. But it looks like that just makes it so you get a runtime exception if the class you're trying to clone doesn't support cloning. That doesn't seem like an improvement over a compile time error. So I'm not really sure what should be done, but I definitely think something should be done to specify "the D way" to polymorphically copy objects. Built-ins mostly have .dup properties, but I don't think the spec actually says anywhere that user classes that want to be copyable should have a .dup. But even specifying a .dup is not enough I think, because if I derive from some class A, I have to create my derived class, then get class A to copy the A parts of the new object somehow, say via a method like A.copy(A copy_from). C++ may have problems regarding copying classes, but D's solution is effectively to just remove the C++ functionality good and bad. Ok the slicing problem is gone, but so is copying. There should be one obvious way to make classes in D copyable, whether it be enforced by the language, compiler, or simply the spec and D community. --bb

In java you also need to implement a clonable interface which is a marker interface. I think that adding a clonable interface to the standard library should be enough. something like: --- interface clonable { object dup(); } --- now you need to implement it to support copying.

Is there really any benefit in making it an interface in D since you can just do a static if check to see if it has a dup method?
 I don't think a dup method should be added to object. as you said, it 
 isn't really an improvement, and usually Java experts recommend 
 avoiding clone().

What do you mean they recommend avoiding it? What do they recommend instead?
 also, i don't see why a copy method is required.
 for example, check the following code:
 ---
 class Base : clonable {
     Base dup() {
        auto ret = cast(Base) this.classinfo.create;
        ret.x = this.x;
        ret.y = this.y;
        return ret;
     }
     int x = 6;
     double y = 8;
 }

 class DerivedA : Base {
     override DerivedA dup() {
        auto ret = cast(DerivedA) super.dup;
        ret.w = this.w;
        return ret;
     }
     long w = 42;
 }

Ah ha! Ok, that's great. This is exactly what I was looking for over on the thread I started on D.learn ("implementing dup/clone for class hierarchies"). Steven suggested using classinfo.create, but he left his solution using a separate copy() method (or I just misunderstood him), and that just ends up in a lot of casting. But your method seems to work.

There is one problem with that solution. classinfo.create only works if the object being duplicated has a default constructor. If you split it into dup and copy, then you can have the most derived class create the instance, calling whatever constructor it wants to. I'm not sure why classinfo.create returns null though. The doc for it only says "Create instance of Object represented by 'this'.". Doesn't mention anything about ever returning null. --bb
Dec 16 2007
parent reply Bill Baxter <dnewsgroup billbaxter.com> writes:
Bill Baxter wrote:
 Bill Baxter wrote:
 Yigal Chripun wrote:


 There is one problem with that solution.  classinfo.create only works if 
 the object being duplicated has a default constructor.  If you split it 
 into dup and copy, then you can have the most derived class create the 
 instance, calling whatever constructor it wants to.
 
 I'm not sure why classinfo.create returns null though.  The doc for it 
 only says "Create instance of Object represented by 'this'.".  Doesn't 
 mention anything about ever returning null.

Another problem is that it's far too easy for derived classes to forget that they need to implement their own dup(), so you end up with what's effectively another variation of the slicing problem. You get an object that's a Derived but for some reason all the Derived-specific members are bogus. I suppose it's not quite as bad as the real slicing problem though, because once detected it can always be fixed at the source, whereas with slicing, the line of code needing fixing could be anywhere. And that problem exists with the split dup/copy solution too. --bb
Dec 16 2007
next sibling parent Yigal Chripun <yigal100 gmail.com> writes:
Bill Baxter wrote:
 Bill Baxter wrote:
 Bill Baxter wrote:
 Yigal Chripun wrote:


 There is one problem with that solution.  classinfo.create only works 
 if the object being duplicated has a default constructor.  If you 
 split it into dup and copy, then you can have the most derived class 
 create the instance, calling whatever constructor it wants to.

 I'm not sure why classinfo.create returns null though.  The doc for it 
 only says "Create instance of Object represented by 'this'.".  Doesn't 
 mention anything about ever returning null.

Another problem is that it's far too easy for derived classes to forget that they need to implement their own dup(), so you end up with what's effectively another variation of the slicing problem. You get an object that's a Derived but for some reason all the Derived-specific members are bogus. I suppose it's not quite as bad as the real slicing problem though, because once detected it can always be fixed at the source, whereas with slicing, the line of code needing fixing could be anywhere. And that problem exists with the split dup/copy solution too. --bb

here's a guide about securing Java code, it has a paragraph related to this discussion about dup (clone in java) http://www.securingjava.com/chapter-seven/chapter-seven-1.html I think it applies to D as well. another solution to this problem is a copy constructor as in c++. ( it should be only provided by the class designer so no compiler generated copy c-tors, and should be explicitly called by the client code.) also I think that const in D will remove a large portion of the need for dup because you could pass a const reference to your object instead of dupping it.
Dec 16 2007
prev sibling parent reply guslay <guslay gmail.com> writes:
Bill Baxter Wrote:

 Another problem is that it's far too easy for derived classes to forget 
 that they need to implement their own dup(), so you end up with what's 
 effectively another variation of the slicing problem.  You get an object 
 that's a Derived but for some reason all the Derived-specific members 
 are bogus.  I suppose it's not quite as bad as the real slicing problem 
 though, because once detected it can always be fixed at the source, 
 whereas with slicing, the line of code needing fixing could be anywhere.
 
 And that problem exists with the split dup/copy solution too.
 
 --bb

It would be nice to have an attribute to say "this method must be implemented or re-implemented in the most derived class". It's basically the opposite of final, it's a must-override. This would be very useful when implementing cloning, serialization. For instance, if you derive class B from a concrete class A, but fail to provide a reimplementation of dup(), class B will be abstract. There are other ways to do this (never derive from concrete class, always carry the parent's interface when inheriting, documentation), but they don't offer strong guarantees.
Dec 16 2007
parent Bill Baxter <dnewsgroup billbaxter.com> writes:
guslay wrote:
 Bill Baxter Wrote:
 
 Another problem is that it's far too easy for derived classes to forget 
 that they need to implement their own dup(), so you end up with what's 
 effectively another variation of the slicing problem.  You get an object 
 that's a Derived but for some reason all the Derived-specific members 
 are bogus.  I suppose it's not quite as bad as the real slicing problem 
 though, because once detected it can always be fixed at the source, 
 whereas with slicing, the line of code needing fixing could be anywhere.

 And that problem exists with the split dup/copy solution too.

 --bb

It would be nice to have an attribute to say "this method must be implemented or re-implemented in the most derived class". It's basically the opposite of final, it's a must-override. This would be very useful when implementing cloning, serialization.

Or like 'abstract' that never gets turned off. "super abstract"! (Stolen from Walter's original idea of "super const" for what is now called "invariant" in D2 ;-)) But seriously, the feature sounds useful, and like it would be not too difficult to implement.
 For instance, if you derive class B from a concrete class A, but fail to
provide a reimplementation of dup(), class B will be abstract.
 
 There are other ways to do this (never derive from concrete class, always
carry the parent's interface when inheriting, documentation), but they don't
offer strong guarantees.

I agree. D is supposed to be about preventing you from making mistakes like that. --bb
Dec 16 2007
prev sibling parent Christopher Wright <dhasenan gmail.com> writes:
Walter Bright wrote:
 Christopher Wright wrote:
 That would be allowed in D, if you could overload T.opAssign(T).

C++ still has well-known slicing problems, even with overloading assignment.

Yes, of course. You'd need to have a reference type behaving as a value type, with copy on assignment, in order to get rid of the issue. Though I'm not sure you're allowed to overload assignment from a reference or pointer to another reference or pointer in C++. Besides which, people are going to use polymorphic types by value (actually by value, rather than some by-reference-and-by-value stuff) for performance reasons. Overload all you want, it won't help you when there's only 32 bytes for your struct on the stack and you need to pack in 48 bytes.
 The question is not "can this be done", it's more "is there a compelling 
 reason to support such behavior". I think the answer is no. Do you 
 really want a language where a class designer feels compelled to define 
 an opassign overload, then make it private to prevent people from using 
 the class as a value type? Where the base class designer needs to know 
 what the derived classes are doing so he can make the destructor virtual 
 or not? Where you cannot have arrays of base classes?

You'd need to create an opAssign overload in order to use a class as a value type, so you'd have to do zero work to prevent a class from being used as a value type. If you mean, create one and make it private in order for derived classes not to be used as value types, well, there'd better be a compelling reason for that. And making a private opAssign wouldn't help matters; I'd just make a new one on my derived class. I'm not sure how arrays would be hindered by this. Of course, if it's too much of an issue, you could define a construct 'ref struct' that allows inheritance but has value semantics. If you think that programmers will have too much trouble with overloading assignment with classes. But that's introducing a new language construct rather than eliminating an exception to a rule.
Dec 16 2007
prev sibling parent "Bruce Adams" <tortoise_74 yeah.who.co.uk> writes:
On Thu, 13 Dec 2007 00:59:33 -0000, BC  
<NOTmi_emayl_adrez hotmail.com.remove.not> wrote:

 I don't see how C++ is forcing anything. C++ is the one that gives you
 the choice. If I want two closely related types to be viewed through a
 common interface or to share implementation in D, I have to accept
 reference semantics as well, everywhere. If you have an aversion to
 by-value objects you can always stick to pointers in C++. Use an alias.
 We're not trying to take anything away, it's an addition.

I think the problem with C++ (problem may be too harsh) is you are forced to choose between pass by reference and pass by value semantics. Though in practice you rarely choose value semantics. There seems to be a lot of toing and froing over which is best. In my opinion there should be one default calling convention and the compiler decides whether to pass by reference or value as it ought to know best which will be more efficient. As programmer you just want to get on and code. Note the ought though. I think there should be an option to choose one or the other when you as programmer have good reason to believe one will perform better than the other. I'm not sure that D makes this easy. The important part of the contract is whether the object passed is mutable or not and this is why we need const. Regards, Bruce. (drunk and sleepy)
Dec 13 2007
prev sibling parent Leandro Lucarella <llucax gmail.com> writes:
Martin Hinsch, el 12 de diciembre a las 22:02 me escribiste:
 I don't mean to be offensive, but I totally don't see the point. Of course
 C++ structs (being inherited from C) are too low-level. Still, I would
 argue that this is a property of C++ not of value-semantics.

Value-semantics has the "slicing problem" and works very poorly with virtual methods. It adds a lot of complexity. -- Leandro Lucarella (luca) | Blog colectivo: http://www.mazziblog.com.ar/blog/ ---------------------------------------------------------------------------- GPG Key: 5F5A8D05 (F8CD F9A7 BF00 5431 4145 104C 949E BFB6 5F5A 8D05) ---------------------------------------------------------------------------- De las generaciones venideras espero, nada más, que vengan. -- Ricardo Vaporeso
Dec 12 2007
prev sibling parent reply Walter Bright <newshound1 digitalmars.com> writes:
BC wrote:
 I thought it was a great idea to allow both value semantics
 and pointer semantics for the same types in C++ by the simple
 addition of a * or &. What was wrong with that that needed
 to be fixed in D? GC could have been done without changing
 this. Now in D template code doesn't know which
 semantics apply and structs can't inherit. If you write
 something as a struct and then realise you need inheritance
 you're stuck. D has so many good ideas it's a shame to see
 it broken in this way...

It's a good question. D structs are designed to represent value types, and classes as reference types. The two have very different uses and characteristics. C++ allows them to be mixed up together, with program bugs as the usual result. For example, the slicing problem. If you inherit from a value type, and then add members, then everyone who uses the value type by value "slices" off the additional members. Virtual functions make no sense for value types, and non-virtual functions are a recipe for disaster in reference types (hence the exhortation to not forget to make your destructors virtual if deriving from them, a lame bit of advice because base classes cannot control how they are used). In C++, one designs a class to be a reference type or a value type. Interestingly, I've never once seen in documentation for a C++ class whether it is supposed to be used by reference or by value. Clearly distinguishing value types from reference types: 1) Indicates to the user how a type is to be used 2) Allows for the correct semantic defaults 3) Eliminates whole categories of bugs that are impractical to detect in C++
Dec 12 2007
next sibling parent reply BC <NOTmi_emayl_adrez hotmail.com.remove.not> writes:
On Thu, 13 Dec 2007 05:10:04 -0000, Walter Bright  
<newshound1 digitalmars.com> wrote:

 BC wrote:
 I thought it was a great idea to allow both value semantics
 and pointer semantics for the same types in C++ by the simple
 addition of a * or &. What was wrong with that that needed
 to be fixed in D? GC could have been done without changing
 this. Now in D template code doesn't know which
 semantics apply and structs can't inherit. If you write
 something as a struct and then realise you need inheritance
 you're stuck. D has so many good ideas it's a shame to see
 it broken in this way...

It's a good question. D structs are designed to represent value types, and classes as reference types. The two have very different uses and characteristics. C++ allows them to be mixed up together, with program bugs as the usual result. For example, the slicing problem. If you inherit from a value type, and then add members, then everyone who uses the value type by value "slices" off the additional members. Virtual functions make no sense for value types, and non-virtual functions are a recipe for disaster in reference types (hence the exhortation to not forget to make your destructors virtual if deriving from them, a lame bit of advice because base classes cannot control how they are used). In C++, one designs a class to be a reference type or a value type. Interestingly, I've never once seen in documentation for a C++ class whether it is supposed to be used by reference or by value. Clearly distinguishing value types from reference types: 1) Indicates to the user how a type is to be used 2) Allows for the correct semantic defaults 3) Eliminates whole categories of bugs that are impractical to detect in C++

I admit I exaggerated in the original post (or was completely wrong. D isn't broken) Perhaps we could consider all this as just thinking out loud. I have to say I've never really had a problem with slicing (well, maybe when I was first learning.) Assigning related value types to each other is conversion, not polymorphism, if you accept that you're ok. You could make things more interesting though (or a complete hack). You could make all your container types descend from one, with all virtual functions. In C++ you could then use them as value types, and all the functions get called non-virtually or as a reference and they're all virtual. Admittedly this rules out the non-virtual case if using null pointers to save memory for empty containers. You could possibly imagine a third way where a reference (giving you polymorphism) simulates value semantics by dupping on every assignment (so you don't have to worry if two share data). I can almost hear you cringing as I type this. So many dirty tricks you can do in C++! I have a question: aren't reference types mostly an implementation detail, for speed/memory reasons? Anyway perhaps I should leave you all alone and go learn brainfuck.
Dec 13 2007
parent reply BC <NOTmi_emayl_adrez hotmail.com.remove.not> writes:
On Thu, 13 Dec 2007 08:02:13 -0000, BC  
<NOTmi_emayl_adrez hotmail.com.remove.not> wrote:

 On Thu, 13 Dec 2007 05:10:04 -0000, Walter Bright  
 <newshound1 digitalmars.com> wrote:

 BC wrote:
 I thought it was a great idea to allow both value semantics
 and pointer semantics for the same types in C++ by the simple
 addition of a * or &. What was wrong with that that needed
 to be fixed in D? GC could have been done without changing
 this. Now in D template code doesn't know which
 semantics apply and structs can't inherit. If you write
 something as a struct and then realise you need inheritance
 you're stuck. D has so many good ideas it's a shame to see
 it broken in this way...

It's a good question. D structs are designed to represent value types, and classes as reference types. The two have very different uses and characteristics. C++ allows them to be mixed up together, with program bugs as the usual result. For example, the slicing problem. If you inherit from a value type, and then add members, then everyone who uses the value type by value "slices" off the additional members. Virtual functions make no sense for value types, and non-virtual functions are a recipe for disaster in reference types (hence the exhortation to not forget to make your destructors virtual if deriving from them, a lame bit of advice because base classes cannot control how they are used). In C++, one designs a class to be a reference type or a value type. Interestingly, I've never once seen in documentation for a C++ class whether it is supposed to be used by reference or by value. Clearly distinguishing value types from reference types: 1) Indicates to the user how a type is to be used 2) Allows for the correct semantic defaults 3) Eliminates whole categories of bugs that are impractical to detect in C++

I admit I exaggerated in the original post (or was completely wrong. D isn't broken) Perhaps we could consider all this as just thinking out loud. I have to say I've never really had a problem with slicing (well, maybe when I was first learning.) Assigning related value types to each other is conversion, not polymorphism, if you accept that you're ok. You could make things more interesting though (or a complete hack). You could make all your container types descend from one, with all virtual functions. In C++ you could then use them as value types, and all the functions get called non-virtually or as a reference and they're all virtual. Admittedly this rules out the non-virtual case if using null pointers to save memory for empty containers. You could possibly imagine a third way where a reference (giving you polymorphism) simulates value semantics by dupping on every assignment (so you don't have to worry if two share data).

oops, typo, i meant reference counting, obviously. would it be possible to have a way of changing the behaviour of a struct slightly without having to forward all the calls?
Dec 13 2007
parent reply Walter Bright <newshound1 digitalmars.com> writes:
BC wrote:
 I admit I exaggerated in the original post (or was completely wrong. D 
 isn't
 broken) Perhaps we could consider all this as just thinking out loud.
 I have to say I've never really had a problem with slicing (well, maybe
 when I was first learning.) Assigning related value types to each 
 other is
 conversion, not polymorphism, if you accept that you're ok. You could
 make things more interesting though (or a complete hack). You could make
 all your container types descend from one,
 with all virtual functions. In C++ you could then use them as value 
 types,
 and all the functions get called non-virtually or as a reference and
 they're all virtual. Admittedly this rules out the non-virtual case if
 using null pointers to save memory for empty containers.


Yes, in C++ you can do all that. The issue is that when both value and reference semantics are mixed together in one class is that it's almost certainly a broken class design. Complicating the problem is the users of a class have to be careful to only use it as a value type, or only use it as a reference type, per the class implementation. I propose that this is an unnecessary burden on both the class designer and the class user, and certainly C++ code is susceptible to many insidious bugs because of it that are very hard to protect against.
 You could
 possibly imagine a third way where a reference (giving you polymorphism)
 simulates value semantics by dupping on every assignment (so you don't
 have to worry if two share data).

oops, typo, i meant reference counting, obviously. would it be possible to have a way of changing the behaviour of a struct slightly without having to forward all the calls?

The current ideas on doing reference counting in D involve having a struct wrap a class reference. Please note that current C++ reference counting designs do the same thing - C++ offers no efficiency advantage.
Dec 13 2007
next sibling parent "Jb" <jb nowhere.com> writes:
"Walter Bright" <newshound1 digitalmars.com> wrote in message 
news:fjs7dt$2civ$1 digitalmars.com...
 oops, typo, i meant reference counting, obviously.
 would it be possible to have a way of changing the behaviour of a struct
 slightly without having to forward all the calls?

The current ideas on doing reference counting in D involve having a struct wrap a class reference. Please note that current C++ reference counting designs do the same thing - C++ offers no efficiency advantage.

Why not have ref counting built in (but optional) for classes, like 'scope' is. If the class is defined as such, or the declared as such, then it becomes a ref counted object. The ref count could follow the vtable pointer, and if it is < 0 then the object is not ref counted, if >= 0 then it is. Then any (de)assignments, become a matter of checking the class ref count, and a locked inc / dec it if it is a counted class. Or have ref counted objects as only assignable to ref count referances. Eg.. Foo a; counted Foo b; counted Foo c = new Foo(); a = b; // error b = c; // Ok c = a; // error I guess it wouldnt be popular if it added too much overhead to assignments. But it can be optimized for the non ref counted situation, the ref counted situation would require a lock instruction to be thread safe, and they are expensive anyway. Can the wrapping of a class referance in a struct decrease the ref count on scope exit? Eg.. if (somthing) { counted Foo f = this.getFoo(); <- Inc ref count // ... } <-- can the struct wrapping class dec ref count here as f goes out of scope?
Dec 13 2007
prev sibling parent BC <NOTmi_emayl_adrez hotmail.com.remove.not> writes:
On Thu, 13 Dec 2007 21:13:43 -0000, Walter Bright  
<newshound1 digitalmars.com> wrote:

 BC wrote:
 I admit I exaggerated in the original post (or was completely wrong. D  
 isn't
 broken) Perhaps we could consider all this as just thinking out loud.
 I have to say I've never really had a problem with slicing (well, maybe
 when I was first learning.) Assigning related value types to each  
 other is
 conversion, not polymorphism, if you accept that you're ok. You could
 make things more interesting though (or a complete hack). You could  
 make
 all your container types descend from one,
 with all virtual functions. In C++ you could then use them as value  
 types,
 and all the functions get called non-virtually or as a reference and
 they're all virtual. Admittedly this rules out the non-virtual case if
 using null pointers to save memory for empty containers.


Yes, in C++ you can do all that. The issue is that when both value and reference semantics are mixed together in one class is that it's almost certainly a broken class design. Complicating the problem is the users of a class have to be careful to only use it as a value type, or only use it as a reference type, per the class implementation. I propose that this is an unnecessary burden on both the class designer and the class user, and certainly C++ code is susceptible to many insidious bugs because of it that are very hard to protect against.
 You could
 possibly imagine a third way where a reference (giving you  
 polymorphism)
 simulates value semantics by dupping on every assignment (so you don't
 have to worry if two share data).

would it be possible to have a way of changing the behaviour of a struct slightly without having to forward all the calls?

The current ideas on doing reference counting in D involve having a struct wrap a class reference. Please note that current C++ reference counting designs do the same thing - C++ offers no efficiency advantage.

I guess wrapping has most of the same effect as inheritance with non-virtual calls. Does that mean it will be possible to overload struct T.opAssign(T) to make the counting more robust? PS. Will dmc++0x be out early due to already implementing most of it once?
Dec 13 2007
prev sibling next sibling parent reply Russell Lewis <webmaster villagersonline.com> writes:
Walter Bright wrote:
 It's a good question. D structs are designed to represent value types, 
 and classes as reference types. The two have very different uses and 
 characteristics. C++ allows them to be mixed up together, with program 
 bugs as the usual result.
 
 For example, the slicing problem. If you inherit from a value type, and 
 then add members, then everyone who uses the value type by value 
 "slices" off the additional members.
 
 Virtual functions make no sense for value types, and non-virtual 
 functions are a recipe for disaster in reference types (hence the 
 exhortation to not forget to make your destructors virtual if deriving 
 from them, a lame bit of advice because base classes cannot control how 
 they are used).
 
 In C++, one designs a class to be a reference type or a value type. 
 Interestingly, I've never once seen in documentation for a C++ class 
 whether it is supposed to be used by reference or by value.
 
 Clearly distinguishing value types from reference types:
 
 1) Indicates to the user how a type is to be used
 2) Allows for the correct semantic defaults
 3) Eliminates whole categories of bugs that are impractical to detect in 
 C++

Hear, hear! Good arguments, all! It still doesn't answer why we left out the little star on class-reference variable assignments. As I've argued before, the question of syntax is orthogonal to the question of legal operations. Make value-style operations illegal for classes, and keep the star there, IMHO.
Dec 13 2007
parent reply Walter Bright <newshound1 digitalmars.com> writes:
Russell Lewis wrote:
 Hear, hear!  Good arguments, all!  It still doesn't answer why we left 
 out the little star on class-reference variable assignments.  As I've 
 argued before, the question of syntax is orthogonal to the question of 
 legal operations.  Make value-style operations illegal for classes, and 
 keep the star there, IMHO.

It would make writing generic code unnecessarily difficult.
Dec 13 2007
parent reply Russell Lewis <webmaster villagersonline.com> writes:
Walter Bright wrote:
 Russell Lewis wrote:
 Hear, hear!  Good arguments, all!  It still doesn't answer why we left 
 out the little star on class-reference variable assignments.  As I've 
 argued before, the question of syntax is orthogonal to the question of 
 legal operations.  Make value-style operations illegal for classes, 
 and keep the star there, IMHO.

It would make writing generic code unnecessarily difficult.

???? I thought that one of the key arguments for putting the syntax back to the C++ way was that it would make generic code easier to write. Right now, when we perform an assignment, generic code can't know (without specialization) whether it is assigning a value or a reference type.
Dec 13 2007
parent reply Walter Bright <newshound1 digitalmars.com> writes:
Russell Lewis wrote:
 Walter Bright wrote:
 Russell Lewis wrote:
 Hear, hear!  Good arguments, all!  It still doesn't answer why we 
 left out the little star on class-reference variable assignments.  As 
 I've argued before, the question of syntax is orthogonal to the 
 question of legal operations.  Make value-style operations illegal 
 for classes, and keep the star there, IMHO.

It would make writing generic code unnecessarily difficult.

???? I thought that one of the key arguments for putting the syntax back to the C++ way was that it would make generic code easier to write. Right now, when we perform an assignment, generic code can't know (without specialization) whether it is assigning a value or a reference type.

Because if I replace a struct with a class, then I have to go through every use of the struct and add a *.
Dec 13 2007
parent reply James Dennett <jdennett acm.org> writes:
Walter Bright wrote:
 Russell Lewis wrote:
 Walter Bright wrote:
 Russell Lewis wrote:
 Hear, hear!  Good arguments, all!  It still doesn't answer why we
 left out the little star on class-reference variable assignments. 
 As I've argued before, the question of syntax is orthogonal to the
 question of legal operations.  Make value-style operations illegal
 for classes, and keep the star there, IMHO.

It would make writing generic code unnecessarily difficult.

???? I thought that one of the key arguments for putting the syntax back to the C++ way was that it would make generic code easier to write. Right now, when we perform an assignment, generic code can't know (without specialization) whether it is assigning a value or a reference type.

Because if I replace a struct with a class, then I have to go through every use of the struct and add a *.

That's the *problem* that we have in D today, that this change of syntax is intended to solve. The wonderful thing is that you would NOT have to do this with explicit dereferencing a la C++, but you DO have to do this if you change a class to a struct with D today because D lacks uniform ways to access value and reference types (as dereferencing is implicit in one case and explicit in the other). D uses the same syntax to mean utterly different things depending on context, and that's anathema to genericity. Maybe examples are needed if people are talking past each other this obviously? -- James
Dec 26 2007
parent Russell Lewis <webmaster villagersonline.com> writes:
James Dennett wrote:
 ????  I thought that one of the key arguments for putting the syntax
 back to the C++ way was that it would make generic code easier to
 write.  Right now, when we perform an assignment, generic code can't
 know (without specialization) whether it is assigning a value or a
 reference type.

every use of the struct and add a *.

(snip) Maybe examples are needed if people are talking past each other this obviously?

If Walter will permit me, I will put some words in his mouth. I think that I understand what Walter is saying, but I disagree with him. :) Walter has argued that structs should represent value-semantics and classes should represent reference-semantics. Basically, the idea is that you can have the code: Foo thing = <whatever>; Foo other_thing = thing; and have it work magically, correctly. For a struct, the assignment will copy the values from one to the other; for a class, it will copy only the reference. This is very useful for generic programming because you can then implement a linked list like this: struct LinkedList(T) { T val; LinkedList!(T) next; }; The point being that it doesn't matter whether T is a struct or a class, you will store the "right" think in the linked list node. I see what he's saying there: LinkedList!(MyClass) linkedList_with_reference_semantics; LinkedList!(MyStruct) linkedList_with_value_semantics; But, IMHO, the definition of "right" thing should be up to the *user* of the template, not the structure of the language. In my hoped-for-world, where a class reference needed the little '*' just like a pointer-to-struct did, we could still use the template above. However, it would mean that the user of the template would need to use the template wisely: LinkedList!(MyClass*) linkedList_with_reference_semantics; LinkedList!(MyStruct) linkedList_with_value_semantics; I like the idea that the user of the template gets the power (and responsibility) to use it wisely. More importantly to me, I think that the consistency of syntax allows us to automatically solve any number of other problems. Say that you have a template which absolutely *must* use reference semantics. In my hoped-for-world, you could declare a single template: struct MyRefSemanticsTemplate(T*) {} But in current D, you have to implement at least 2 different templates, one for classes, and another for structs. Ick. Walter, please step in if I have misrepresented your position. Or, better yet, everybody else please assume that I've misrepresented his position unless he agrees. :)
Dec 26 2007
prev sibling parent James Dennett <jdennett acm.org> writes:
Walter Bright wrote:

[snip]

 In C++, one designs a class to be a reference type or a value type.

More generally, in *design*, a class either has entity semantics (what you call "reference" types and C++ calls "polymorphic" types) or value semantics. That's the key terminology here, really. What you call reference types, C++ calls polymorphic types. This is clearly documented in every decent C++ library I've seen, and it just does not lead to confusion for any competent C++ programmers. (Possible exception: smart pointers have handle semantics: they are copyable values, but act *like* references.)
 Interestingly, I've never once seen in documentation for a C++ class
 whether it is supposed to be used by reference or by value.

Interesting. You've *never* seen any competent documentation for C++ code, or you've just never been able to work out from the documentation whether a type was polymorphic or not? Or is this the *much* less interesting claim that the terminology used is not the same, or that this is so obvious that it's not explicitly flagged? The C++ code I see (and I might say that's a fair amount) is entirely clear on this. I just picked a random type for which I lacked prior knowledge of the documentation, and found http://www.boost.org/doc/html/date_time/gregorian.html#date_time.gregorian.date_class which says explicitly "The class is specifically designed to NOT contain virtual functions. This design allows for efficient calculation and memory usage with large collections of dates.", and defines the semantics of copying. In your terminology, that's saying "this is a value type". Let D stand on its own feet there's no need for misleading FUD about C++ or the C++ community. -- James
Dec 26 2007