digitalmars.D - what was wrong with struct & class in C++?
- BC (9/9) Dec 12 2007 I thought it was a great idea to allow both value semantics
- Russell Lewis (11/20) Dec 12 2007 I 99% agree with you, but I would like to debate just a smidgeon. I
- Gregor Richards (21/30) Dec 12 2007 No. No no no no no. I hate seeing this argument.
- Russell Lewis (19/53) Dec 12 2007 In an abstract, academic sense, maybe you're right. As a practical
- Martin Hinsch (36/74) Dec 12 2007 I don't mean to be offensive, but I totally don't see the point. Of cour...
- Gregor Richards (35/110) Dec 12 2007 This is irrelevant. Whether OO is good or not, D aims to be OO.
- Kirk McDonald (21/47) Dec 12 2007 I feel strangely compelled to describe Python's behavior in this case,
- Martin Hinsch (43/125) Dec 12 2007 So, you're basically saying follow the crowd since everybody does...
-
BC
(40/158)
Dec 12 2007
On Wed, 12 Dec 2007 22:59:16 -0000, Gregor Richards
... - Yigal Chripun (38/39) Dec 12 2007 I'd like to clarify your question:
- BC (18/58) Dec 12 2007 i should have been clearer, i meant a. although i'd argue it's the same
- Don Clugston (3/12) Dec 13 2007 How? What matrix operations are polymorphic?
- Yigal Chripun (9/22) Dec 13 2007 i can't think of any... you're right, my bad.
- Walter Bright (3/6) Dec 13 2007 You're right. Any object designed for inheritance or polymorphism should...
- Christopher Wright (9/16) Dec 14 2007 Is this because it is too difficult to implement polymorphism for value
- Walter Bright (9/10) Dec 15 2007 C++ still has well-known slicing problems, even with overloading assignm...
- Bill Baxter (33/46) Dec 16 2007 At least C++ lets you control copying behavior completely in all
- Yigal Chripun (34/87) Dec 16 2007 In java you also need to implement a clonable interface which is a
- Bill Baxter (17/108) Dec 16 2007 Is there really any benefit in making it an interface in D since you can...
- Bill Baxter (9/117) Dec 16 2007 There is one problem with that solution. classinfo.create only works if...
- Bill Baxter (10/20) Dec 16 2007 Another problem is that it's far too easy for derived classes to forget
- Yigal Chripun (11/35) Dec 16 2007 here's a guide about securing Java code, it has a paragraph related to
- guslay (4/15) Dec 16 2007 It would be nice to have an attribute to say "this method must be implem...
- Bill Baxter (9/28) Dec 16 2007 Or like 'abstract' that never gets turned off. "super abstract"!
- Christopher Wright (22/34) Dec 16 2007 Yes, of course. You'd need to have a reference type behaving as a value
- Bruce Adams (21/27) Dec 13 2007 I think the problem with C++ (problem may be too harsh) is you are force...
- Leandro Lucarella (10/13) Dec 12 2007 Value-semantics has the "slicing problem" and works very poorly with
- Walter Bright (20/29) Dec 12 2007 It's a good question. D structs are designed to represent value types,
- BC (21/51) Dec 13 2007 I admit I exaggerated in the original post (or was completely wrong. D
- BC (5/60) Dec 13 2007 oops, typo, i meant reference counting, obviously.
- Walter Bright (12/34) Dec 13 2007 Yes, in C++ you can do all that. The issue is that when both value and
- Russell Lewis (6/31) Dec 13 2007 Hear, hear! Good arguments, all! It still doesn't answer why we left
- Walter Bright (2/7) Dec 13 2007 It would make writing generic code unnecessarily difficult.
- Russell Lewis (6/14) Dec 13 2007 ???? I thought that one of the key arguments for putting the syntax
- Walter Bright (3/18) Dec 13 2007 Because if I replace a struct with a class, then I have to go through
- James Dennett (14/33) Dec 26 2007 That's the *problem* that we have in D today, that this
- Russell Lewis (41/53) Dec 26 2007 If Walter will permit me, I will put some words in his mouth. I think
- James Dennett (29/32) Dec 26 2007 More generally, in *design*, a class either has entity semantics
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
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
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
Gregor Richards wrote:BC wrote: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.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).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
On Wed, 12 Dec 2007 12:24:44 -0800, Gregor Richards wrote:BC wrote: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 HarenI 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
Martin Hinsch wrote:On Wed, 12 Dec 2007 12:24:44 -0800, Gregor Richards wrote:Consensus and history.BC wrote: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,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 Richardsb) that it's inherently a great thingThis 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 DSeriously? 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
Gregor Richards wrote:Martin Hinsch wrote: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.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.(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
On Wed, 12 Dec 2007 14:59:16 -0800, Gregor Richards wrote:Martin Hinsch wrote:[snip]On Wed, 12 Dec 2007 12:24:44 -0800, Gregor Richards wrote:So, you're basically saying follow the crowd since everybody does...Consensus and history.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 RichardsI 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,I thought D had the much more modest goal to be the best language ever ;-).b) that it's inherently a great thingThis is irrelevant. Whether OO is good or not, D aims to be OO.see above. I don't think that consensus is a good argument (except maybe in politics).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.I see no reasons to treat operators anything different than as sugarified function calls. [snip]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 withIntrinsic 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.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.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.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]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 DSeriously? 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.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
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 =r =pointer semantics for the same types in C++ by the simple addition of a * o=e&. What was wrong with that that needed to be fixed in D? GC could hav=wbeen done without changing this. Now in D template code doesn't kno=which semantics apply and structs can't inherit. If you write =as =something as a struct and then realise you need inheritance you're stuck. D h=theso 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 =insense to duplicate contexts. Contexts are an abstraction of an environment=herwhich to perform actions, and it makes no sense to duplicate that environment whenever you happen to pass it from one function to anot=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?(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 RichardsI don't mean to be offensive, but I totally don't see the point. Of =dcourse 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 thingThis 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.dIMO 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=ed* or not, a returns a new one,=it* has no reason to modify its * argument */ In other words, this code is irrelevant.e.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='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)along with a-- --a +=3D -=3D *=3D /=3D %=3D &=3D |=3D ~=3D ^=3D <<=3D >>= =3D >>>=3Dg =You can easily make up a way to specify which way to use a function arg (like, say, 'in' and 'inout') without havin=sto 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 DSeriously? 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.d(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=f =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=r =scenario.cheers Martin- Gregor Richards
Dec 12 2007
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
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
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?[snip]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
Don Clugston wrote:Yigal Chripun wrote: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?> 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?[snip]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
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
Walter Bright wrote:Yigal Chripun wrote: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).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 14 2007
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
Walter Bright wrote:Christopher Wright wrote: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. --bbThat 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 16 2007
Bill Baxter wrote:Walter Bright wrote: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; }Christopher Wright wrote: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. --bbThat 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 16 2007
Yigal Chripun wrote:Bill Baxter wrote: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?Walter Bright wrote: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.Christopher Wright wrote: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. --bbThat 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?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
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. --bbBill Baxter wrote: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?Walter Bright wrote: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.Christopher Wright wrote: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. --bbThat 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?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.
Dec 16 2007
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
Bill Baxter wrote:Bill Baxter wrote: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.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
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. --bbIt 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
guslay wrote:Bill Baxter Wrote: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.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. --bbIt 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.I agree. D is supposed to be about preventing you from making mistakes like that. --bb
Dec 16 2007
Walter Bright wrote:Christopher Wright wrote: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.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?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
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
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
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
On Thu, 13 Dec 2007 05:10:04 -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. 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.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 13 2007
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: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?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. 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 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 13 2007
BC wrote: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.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.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.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
"Walter Bright" <newshound1 digitalmars.com> wrote in message news:fjs7dt$2civ$1 digitalmars.com...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?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
On Thu, 13 Dec 2007 21:13:43 -0000, Walter Bright <newshound1 digitalmars.com> wrote:BC wrote: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?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.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.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.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
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
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
Walter Bright wrote:Russell Lewis 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.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
Russell Lewis wrote:Walter Bright wrote:Because if I replace a struct with a class, then I have to go through every use of the struct and add a *.Russell Lewis 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.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
Walter Bright wrote:Russell Lewis wrote: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? -- JamesWalter Bright wrote:Because if I replace a struct with a class, then I have to go through every use of the struct and add a *.Russell Lewis 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.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 26 2007
James Dennett wrote: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. :)(snip) Maybe examples are needed if people are talking past each other this obviously????? 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 26 2007
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