Thread: RE: [GD-General] Re: Simulating constrained genericity in C++
Brought to you by:
vexxed72
From: Brian H. <bri...@py...> - 2001-12-26 23:19:33
|
>This approach is fundamentally wrong: unlike Eiffel C++ doesn't use >classes to model attributes like being assignable or addable. So what does C++ use? Anything at all? Near as I can tell, from my ages old usage of templates, C++ is like a static version of Obj-C -- if the function call/operator overload doesn't exist that you need, you get a compile time warning (in Obj-C you can still send a message that isn't understood, which is unbelievably powerful). >of this templates work more like objects in a dynamic language and >rely on name commonality. Right. And I'm not sure if relying on name commonality is really a good design decision. For example, some STL classes require operator < to be overloaded, which goes back to a thread on another mailing list where I said that function name overloading is a Bad Idea so long as you're using text as your representation because you can't find, for example, every place you're doing a comparison between two specific types. There are reasonable reasons for saying: Foo a, b; if ( a < b ) return true; is prettier than: Foo a, b; if ( a.compareToFoo( b ) < 0 ) return true; But at least when I need to know where I'm comparing Foos I can find it immediately with grep. But that's neither here nor there =) >Eiffel reminds me of Modula-2: a really nice implementation of >yesterdays paradigm. But it just doesn't offer enough over C++ to be >compelling (for me at least). I don't know if Eiffel's implementation of constrained genericity is yesterday's paradigm; it's just a different way of doing things. Eiffel still has significant other problems, but I think its support of constrained genericity looks pretty elegant (at least, on paper). For me, the incremental advantages over C/C++ are probably to be found in Obj-C and Java. The former provides fully dynamic typing/message passing; the latter provides garbage collection and a shit load of stock libraries and cross-platform compatibility. Brian |
From: Brian H. <bri...@py...> - 2001-12-27 00:47:13
|
> Maybe, but think about how you'd write generic code in a language > like Smalltalk or Ruby. You'd use untyped objects and rely on the > methods being present, just like you would with templates. Well, it's not quite THAT anarchistic. In Java you could require that the objects conform to a specific interface; in Obj-C the objects would have to conform to a formal protocol; and in Eiffel they would have to be derived from the appropriate type (same goes for C++ -- my complaint here is that in C++ you're reinventing things like an ultimate "Object" class whereas this basic functionality is provided by other languages 'natively'). > You're in good company with that argument. :-) I don't quite buy it > myself. It's awfully nice to be able to create new types and have > them behave like the primitive types (think point, vector, complex > classes). It's even nicer if you can do this without incurring a big > performance hit. Well, this is one of those few areas where I'm willing to agree to disagree =) I know some people, for example, hate having to remember separate "setFloatValue()" vs. "setIntValue()" vs. "setCharValue()" methods. I'm one of those people that happens to hate having the wrong damn function called because of some implicit cast. And, not to beat a dead horse, as long as I'm stuck fundamentally editing text, I'd like to be able to grep my source in a meaningful way. Even my own frameworks still have lots of inadvertent name overloading. For example, I have HFile::create() which is completely and utterly unrelated to HAudioDevice::create(), but when I want to find every place I'm creating a file, I have to grep for "create(", which of course will give me "_create(" or "recreate(". If I look for ".create(" I'll miss ">create(". And, of course, I'm going to find my "device->create(" calls when I want my "file.create(" calls. Blech. This is where better tools would just really help a lot when trying to refactor source code. > might be right now, but I suspect it would include support for > dynamic types and reflection. Well, that's the rather big bitch now. The people designing languages often have these really focused philosophies: "You don't pay for what you don't use" "Everything is an object" "Static typing and design-by-contract reduce programmer error" "Dynamic typing and reflection are powerful" "Separate interface from implementation" Often they're incompatible philosophically, so the notion of adding reflection and dynamic typing to, say, C++ or Eiffel would cause those advocates to freak out that you're asking for "the wrong thing". Sort of like asking Java people for pointer math. Obj-C is probably the closest I've seen to a good compromise. If you statically type something, the compiler will warn when you're pitching a message that the receiver won't understand. But if you don't statically type it, you can use it however you want, and if ask the object what its class is and/or whether it recognizes a particular message. Damn brilliant language. Brian |
From: Jesse J. <jes...@mi...> - 2001-12-27 01:52:48
|
At 4:47 PM -0800 12/26/01, Brian Hook wrote: > > Maybe, but think about how you'd write generic code in a language >> like Smalltalk or Ruby. You'd use untyped objects and rely on the >> methods being present, just like you would with templates. > >Well, it's not quite THAT anarchistic. In Java you could require that >the objects conform to a specific interface; in Obj-C the objects would >have to conform to a formal protocol; and in Eiffel they would have to >be derived from the appropriate type Which is why I was careful to say Smalltalk and Ruby. :-) >(same goes for C++ -- my complaint >here is that in C++ you're reinventing things like an ultimate "Object" >class whereas this basic functionality is provided by other languages >'natively'). Fundamentally C++ is just one great big hack. It's a pretty good hack, but when it comes to things like elegance it just can't compete with languages that were designed with less historical baggage. > > You're in good company with that argument. :-) I don't quite buy it >> myself. It's awfully nice to be able to create new types and have >> them behave like the primitive types (think point, vector, complex >> classes). It's even nicer if you can do this without incurring a big >> performance hit. > >Well, this is one of those few areas where I'm willing to agree to >disagree =) I know some people, for example, hate having to remember >separate "setFloatValue()" vs. "setIntValue()" vs. "setCharValue()" >methods. I'm one of those people that happens to hate having the wrong >damn function called because of some implicit cast. And, not to beat a >dead horse, as long as I'm stuck fundamentally editing text, I'd like to >be able to grep my source in a meaningful way. Sure, but most people don't overload that many methods. Just stuff like the operators and an occasional named method now and then. >Even my own frameworks still have lots of inadvertent name overloading. >For example, I have HFile::create() which is completely and utterly >unrelated to HAudioDevice::create(), but when I want to find every place >I'm creating a file, I have to grep for "create(", which of course will >give me "_create(" or "recreate(". If I look for ".create(" I'll miss >">create(". And, of course, I'm going to find my "device->create(" >calls when I want my "file.create(" calls. > >Blech. > >This is where better tools would just really help a lot when trying to >refactor source code. Yeah, tools are just pitiful. It doesn't help matters that C++ is so damned hard to parse... > > might be right now, but I suspect it would include support for >> dynamic types and reflection. > >Well, that's the rather big bitch now. The people designing languages >often have these really focused philosophies: > >"You don't pay for what you don't use" >"Everything is an object" >"Static typing and design-by-contract reduce programmer error" >"Dynamic typing and reflection are powerful" >"Separate interface from implementation" > >Often they're incompatible philosophically, so the notion of adding >reflection and dynamic typing to, say, C++ or Eiffel would cause those >advocates to freak out that you're asking for "the wrong thing". Sort >of like asking Java people for pointer math. Yup. It's kind of funny. What has it been? 50 years that people have been designing general purpose programming languages? And what have language designers come to agreement on? Goto's are bad? Even garbage collection still has a bad reputation in many places... >Obj-C is probably the closest I've seen to a good compromise. If you >statically type something, the compiler will warn when you're pitching a >message that the receiver won't understand. But if you don't statically >type it, you can use it however you want, and if ask the object what its >class is and/or whether it recognizes a particular message. > >Damn brilliant language. Yeah, I really need to look into Cocoa. I'm a bit put off though because it seems to be much more C-like than C++ if that makes any sense. :-) -- Jesse |
From: Brian H. <bri...@py...> - 2001-12-27 02:36:53
|
> Sure, but most people don't overload that many methods. Just stuff > like the operators and an occasional named method now and then. I wouldn't quite say that. The problem with C++ is that there's a temptation to use everything, and unfortunately a lot of people do. Real, true expert C++ practitioners that A.) understand the language; B.) understand how to build frameworks; and C.) know what esoteric features should NOT be used so as not to alienate their users are few and far between. > Yup. It's kind of funny. What has it been? 50 years that people have > been designing general purpose programming languages? And what have > language designers come to agreement on? Goto's are bad? Even garbage > collection still has a bad reputation in many places... Well, high level languages also had a bad reputation for a long time. Crap, in my lifetime there were arguments over function pointers vs. direct calls and how this would slow things down intolerably. > Yeah, I really need to look into Cocoa. I'm a bit put off though > because it seems to be much more C-like than C++ if that makes any > sense. :-) Obj-C, to me, is far more object oriented than C++. Well, that's not necessarily true, since that's open to interpretation. I WILL say it's a lot cleaner and more expressive for the stuff that I want to do. You can learn Obj-C in a day. It's that simple. And yet it still feels more powerful than C++ because it just ditches the dogma of "static type checking rules all" and says "screw it, we're doing this dynamically, deal". Lack of GC is my number one complaint, followed by its lack of popularity. Support by GCC is a pretty big benefit however, and it can seamlessly work with C and C++ code these days, which is HUGE when compared to other language's interface to C (Java is probably the most pleasant, but even then, it's a mess). There's a free PDF on Obj-C at Apple's Web site (developer.apple.com). I really can't recommend it highly enough. Add in Cocoa, which are a gloriously beautiful set of frameworks (GUI and otherwise), and it's a slam dunk. Too bad about that whole PowerMac thing though. *sigh* Brian |
From: Brian H. <bri...@py...> - 2001-12-27 17:40:44
|
I realize I may have inadvertently given the idea that Obj-C doesn't support static type checking very well (given my examples were that a client could query if an object supported a message and also what to do when a message wasn't understood). Just to be clear, Obj-C also has static type checking just like C++ if you statically type something. In addition, Obj-C has the notion of formal protocols which seem to be pretty much identical to Java interfaces. A class can conform to a formal protocol, and if it does conform than a client knows that all the protocol messages can be accepted. Brian |
From: Jesse J. <jes...@mi...> - 2001-12-27 00:31:22
|
At 3:19 PM -0800 12/26/01, Brian Hook wrote: > >This approach is fundamentally wrong: unlike Eiffel C++ doesn't use >>classes to model attributes like being assignable or addable. > >So what does C++ use? Anything at all? Near as I can tell, from my >ages old usage of templates, C++ is like a static version of Obj-C -- if >the function call/operator overload doesn't exist that you need, you get >a compile time warning (in Obj-C you can still send a message that isn't >understood, which is unbelievably powerful). Exactly. > >of this templates work more like objects in a dynamic language and >>rely on name commonality. > >Right. And I'm not sure if relying on name commonality is really a good >design decision. Maybe, but think about how you'd write generic code in a language like Smalltalk or Ruby. You'd use untyped objects and rely on the methods being present, just like you would with templates. If this is OK for those languages why is it a problem for templates? Of course you could say that one of the advantages of C++ is that you can use the type system to catch errors and templates don't follow this model. But I don't see this as a problem, especially given that not everything in C++ is an object. >For example, some STL classes require operator < to be >overloaded, which goes back to a thread on another mailing list where I >said that function name overloading is a Bad Idea so long as you're >using text as your representation because you can't find, for example, >every place you're doing a comparison between two specific types. You're in good company with that argument. :-) I don't quite buy it myself. It's awfully nice to be able to create new types and have them behave like the primitive types (think point, vector, complex classes). It's even nicer if you can do this without incurring a big performance hit. > >Eiffel reminds me of Modula-2: a really nice implementation of >>yesterdays paradigm. But it just doesn't offer enough over C++ to be >>compelling (for me at least). > >I don't know if Eiffel's implementation of constrained genericity is >yesterday's paradigm; it's just a different way of doing things. Eiffel >still has significant other problems, but I think its support of >constrained genericity looks pretty elegant (at least, on paper). In a language that is statically typed and OO from the ground up constrained genericity *is* good. But that's not what I was getting at. Eiffel is a very nice statically typed OO language. But I want something more than C++ in a nicer package. I don't know what that might be right now, but I suspect it would include support for dynamic types and reflection. -- Jesse |
From: Patrick M D. <pa...@wa...> - 2001-12-27 07:24:50
|
On Wed, 26 Dec 2001, Brian Hook wrote: > So what does C++ use? Anything at all? Near as I can tell, from my > ages old usage of templates, C++ is like a static version of Obj-C -- if > the function call/operator overload doesn't exist that you need, you get > a compile time warning (in Obj-C you can still send a message that isn't > understood, which is unbelievably powerful). What happens to these messages that are not understood? Are these messages simply ignored? Does the recipient simply pass them on to another more knowledgeable target? Or can the recipient decode the types and content of the message? BTW, I'm curious to see something unbelievably powerful! :) > >of this templates work more like objects in a dynamic language and > >rely on name commonality. > > Right. And I'm not sure if relying on name commonality is really a good > design decision. When a language relies on inheritence for subtyping relationsips, then additional effort is required when an interface is created after a particular implementation is coded. One possible solution to this problem is to make a subclass of the implementation and specify the new interface that it satisfies. Unfortunately, this technique may not work when the object is created by a factory. So, using names (and their associated types) as a criteria for subtyping can be a more flexible approach that enables more code reuse. > For example, some STL classes require operator < to be overloaded, > which goes back to a thread on another mailing list where I said that > function name overloading is a Bad Idea so long as you're using text > as your representation because you can't find, for example, every > place you're doing a comparison between two specific types. Don't virtual methods suffer from the same problem. or do you consider that another form of function name overloading? Patrick |
From: Brian H. <bri...@py...> - 2001-12-27 07:51:23
|
At 02:24 AM 12/27/2001 -0500, Patrick M Doane wrote: >What happens to these messages that are not understood? Run-time error. >Are these messages simply ignored? Does the recipient simply pass them on >to another more >knowledgeable target? Or can the recipient decode the types and content of >the message? First off, the caller can verify that the message receiver understands the message: if ( [obj respondsToSelector:@selector(someMessage)] ) [obj someMessage]; Second, the recipient can optionally forward stuff. Before the run-time system pukes because a message isn't understood, it will actually send another message, forwardInvocation:, to the message's target with an NSInvocation object, at which point you can try to recover or do something else appropriate. In fact, the way the system pukes is it actually calls up to the default NSObject -forwardInvocation message which executes -doesNotRecognizeSelector. So you can override this message and do the appropriate thing (like multiplex unknown messages to delegates). That, to me, is very cool. http://developer.apple.com/techpubs/macosx/Cocoa/ObjectiveC/5RunTime/Forwarding.html >BTW, I'm curious to see something unbelievably powerful! :) FWIW, I don't know Obj-C very well, at all. The docs do a much better job at describing this. I'm not a language lawyer by any means, I just happen to "click" with Obj-C much more than I do with other languages in terms of what feels intuitively right. >When a language relies on inheritence for subtyping relationsips, then >additional effort is required when an interface is created after a >particular implementation is coded. Can you give an example of this? > > For example, some STL classes require operator < to be overloaded, > > which goes back to a thread on another mailing list where I said that > > function name overloading is a Bad Idea so long as you're using text > > as your representation because you can't find, for example, every > > place you're doing a comparison between two specific types. > >Don't virtual methods suffer from the same problem. or do you consider >that another form of function name overloading? Yes, virtual functions definitely suffer from the same problem, but in my experience the frequency of name collisions is much lower through inheritance than through function name overloading (either "real" overloading or just using the same names). Brian |
From: Patrick M D. <pa...@wa...> - 2001-12-27 17:49:53
|
On Wed, 26 Dec 2001, Brian Hook wrote: > >When a language relies on inheritence for subtyping relationsips, then > >additional effort is required when an interface is created after a > >particular implementation is coded. > > Can you give an example of this? Sure, here is the initiale code in Java (or something fairly close, I'll probably get the syntax wrong): interface point3d { public float x(); public float y(); public float z(); } class point3d_impl implements point3d { ... } Now somebody has written some code that only works on 2d points, like so: public int dist(point2d p1, point2d p2) where point2d has the expected interface definition. Can we pass objects of type point3_impl to this method? If inheritence defines subtyping (with the implements keyword being a form of inheritence) then one cannot. If names and their types define the subtyping relationship then this is perfectly legal. This isn't the best motivating example, but it should be easy enough to understand the principle. Patrick |
From: Joe <dar...@ya...> - 2001-12-27 19:39:54
|
So assuming point2d was an interface too, wouldn't you just do interface point3d extends point2d{ public float z(); } and then your old implimentation should work > >Sure, here is the initiale code in Java (or something fairly close, I'll >probably get the syntax wrong): > >interface point3d { > public float x(); > public float y(); > public float z(); >} > >class point3d_impl implements point3d { ... } > >Now somebody has written some code that only works on 2d points, like so: > > public int dist(point2d p1, point2d p2) > >where point2d has the expected interface definition. Can we pass objects >of type point3_impl to this method? If inheritence defines subtyping >(with the implements keyword being a form of inheritence) then one >cannot. If names and their types define the subtyping relationship then >this is perfectly legal. > >This isn't the best motivating example, but it should be easy enough to >understand the principle. > >Patrick > > >_______________________________________________ >Gamedevlists-general mailing list >Gam...@li... >https://lists.sourceforge.net/lists/listinfo/gamedevlists-general > |
From: Patrick M D. <pa...@wa...> - 2001-12-27 19:45:05
|
On Thu, 27 Dec 2001, Joe wrote: > So assuming point2d was an interface too, wouldn't you > just do > interface point3d extends point2d{ > public float z(); > } > and then your old implimentation should work Of course, but you may not always be able to make such modifications in practice. Patrick |
From: Patrick M D. <pa...@wa...> - 2001-12-27 20:14:36
|
On Thu, 27 Dec 2001, Joe wrote: > So assuming point2d was an interface too, wouldn't you > just do > interface point3d extends point2d{ > public float z(); > } > and then your old implimentation should work > Here is a more compelling example directly from the JDK. The interface AbstractCollection contains a number of methods that require more functionality than is actually required: public boolean containsAll(Collection c) public boolean addAll(Collection c) public boolean removeAll(Collection c) public boolean retainAll(Collection c) Each of these could be implemented on a class that implemented a single method (e.g. 'contains' or 'iterator'). There was a proposal to introduce additional interfaces like so: public boolean containsAll(Container c) public boolean addAll(Iterable c) public boolean removeAll(Container c) public boolean retainAll(Container c) But this was rejected because it increased the number of interfaces that were required. There's nothing the end-programmer can really do about this without breaking compatibility with the standard libraries. This kind of problem is pervasive throughout languages like Java and C++ that do not separate the notions of inheritance and subtyping. I suspect that you could find similar problems in Eiffel libraries as well. Patrick |