www.digitalmars.com         C & C++   DMDScript  

D - casts

reply "Pavel Minayev" <evilone omen.ru> writes:
The specification says that all casts of objects are
done with run-time checking - and that's good - and
if the type doesn't match, null is returned! I don't
think it's a good idea, since you could make a cast
in one place of the program, and any error would get
unnoticed, and then try to use the null-pointer in
other place somewhere hundred lines after... could
be VERY hard to find the actual reason of crash. Maybe
it's better to throw an exception on bad cast - like
it's done in C++ (and in Delphi by the way).

On other hand, we need an "is" or "instanceof" operator
then...
Nov 23 2001
parent reply "Walter" <walter digitalmars.com> writes:
But all the "is" operator would do is check the cast for a null return.  It
seems redundant.

"Pavel Minayev" <evilone omen.ru> wrote in message
news:9tmb6p$2snv$1 digitaldaemon.com...
 The specification says that all casts of objects are
 done with run-time checking - and that's good - and
 if the type doesn't match, null is returned! I don't
 think it's a good idea, since you could make a cast
 in one place of the program, and any error would get
 unnoticed, and then try to use the null-pointer in
 other place somewhere hundred lines after... could
 be VERY hard to find the actual reason of crash. Maybe
 it's better to throw an exception on bad cast - like
 it's done in C++ (and in Delphi by the way).

 On other hand, we need an "is" or "instanceof" operator
 then...

Nov 23 2001
parent reply "Pavel Minayev" <evilone omen.ru> writes:
"Walter" <walter digitalmars.com> wrote in message
news:9tmi2t$31hm$1 digitaldaemon.com...

 But all the "is" operator would do is check the cast for a null return.

 seems redundant.

No. My proposal is to throw an exception on bad cast rather than return null, to prevent further errors.
Nov 23 2001
next sibling parent "Walter" <walter digitalmars.com> writes:
ok, I understand.

"Pavel Minayev" <evilone omen.ru> wrote in message
news:9tni37$ir0$1 digitaldaemon.com...
 "Walter" <walter digitalmars.com> wrote in message
 news:9tmi2t$31hm$1 digitaldaemon.com...

 But all the "is" operator would do is check the cast for a null return.

 seems redundant.

No. My proposal is to throw an exception on bad cast rather than return null, to prevent further errors.

Nov 23 2001
prev sibling parent reply Russell Borogove <kaleja estarcion.com> writes:
Pavel Minayev wrote:

 "Walter" <walter digitalmars.com> wrote in message
 news:9tmi2t$31hm$1 digitaldaemon.com...
 
 
But all the "is" operator would do is check the cast for a null return.

seems redundant.

No. My proposal is to throw an exception on bad cast rather than return null, to prevent further errors.

Whether we have casts return null or throw exception, it's possible to wrap the language's standard cast in order to simulate the other behavior. Considered from that point, it's (a) slightly easier to have the standard cast return null and write a wrapper that throws than vice versa[1], and (b) faster in run-time to return null than to throw an exception. The same argument can be made regarding memory allocation[2], of course, or file I/O, and then we're back to testing the return value from every function.[3] So it really comes down to whether or not you consider throwing a bunch of objects which have "void in common" into the same container to be good programming practice, or no. -RB [1] Two ways come to mind: "if (obj == null) throw" or "obj->Method()". [2] Which is why my memory manager offers an interface which includes a "this allocation is non-critical, so don't work too hard, and return NULL if you have to" flag as an option. [3] ...the way god intended you to write code.
Nov 24 2001
parent reply "Pavel Minayev" <evilone omen.ru> writes:
"Russell Borogove" <kaleja estarcion.com> wrote in message
news:3BFF539D.40808 estarcion.com...

 Whether we have casts return null or throw exception, it's possible
 to wrap the language's standard cast in order to simulate the other
 behavior. Considered from that point, it's (a) slightly easier to
 have the standard cast return null and write a wrapper that throws
 than vice versa[1], and (b) faster in run-time to return null than to
 throw an exception.

The problem is somewhat different. In general, I suppose that an object to be casted should always conform to the new type, for classes at least - what are you going to do with a wrongly-casted pointer anyhow? So it is a deliberate error, and is supposed to never occur in a final version of the program (dreams...). So in most cases you don't in fact write any checking code, you just rely on it always working properly. And when debugging the program, if the cast is wrong, you'd most likely want to know about it - and you will if it throws an exception. On other hand, null pointer is just silently accepted and at some later point you get an access violation. If you do the cast in a function call, and the function transfers it further... you could end with an exception in some completely different module (long, long ago in a function far, far away =)) staring on the screen and thinking, "WHAT the hell can be wrong with this???" - well you might know the feeling.... Or maybe introduce two cast keywords - one for safe and one for unsafe cast? Pascal syntax, maybe? LOL
Nov 24 2001
parent reply Russell Borogove <kaleja estarcion.com> writes:
Pavel Minayev wrote:

 "Russell Borogove" <kaleja estarcion.com> wrote in message
 news:3BFF539D.40808 estarcion.com...
 
 
Whether we have casts return null or throw exception, it's possible
to wrap the language's standard cast in order to simulate the other
behavior. Considered from that point, it's (a) slightly easier to
have the standard cast return null and write a wrapper that throws
than vice versa[1], and (b) faster in run-time to return null than to
throw an exception.

The problem is somewhat different. In general, I suppose that an object to be casted should always conform to the new type, for classes at least - what are you going to do with a wrongly-casted pointer anyhow? So it is a deliberate error, and is supposed to never occur in a final version of the program (dreams...).

I do understand your point, but some people design their code differently. It may or may not be an error. Say we're talking about game code, and you're iterating over a collection of objects derived from class Entity, checking to see if they're castable to class ThinkingEntity, and if so, calling their Think() method. There are any number of good reasons to maintain all your entities in a single collection even though they're not completely compatible in some contexts. The alternative is to bloat the base class with a bunch of virtual functions which will be no-op in the majority of cases. (Not to mention that sometimes you won't be allowed to mess with the base class.) My point is that from a single cast-else-null primitive, you can build cast-else-throw and and is-safe-to-cast? operations easily and efficiently; if the primitive is cast-else-throw, then the is-safe-to-cast? operation _must_ (as you noted) be provided by the language in a way that doesn't internally generate an exception, for performance reasons. -RB
Nov 24 2001
parent reply "Pavel Minayev" <evilone omen.ru> writes:
"Russell Borogove" <kaleja estarcion.com> wrote in message
news:3BFFFBB3.4070707 estarcion.com...

 It may or may not be an error. Say we're talking about game code, and
 you're iterating over a collection of objects derived from class Entity,
 checking to see if they're castable to class ThinkingEntity, and if so,
 calling their Think() method. There are any number of good reasons to
 maintain all your entities in a single collection even though they're
 not completely compatible in some contexts. The alternative is to bloat
 the base class with a bunch of virtual functions which will be no-op in
 the majority of cases. (Not to mention that sometimes you won't be
 allowed to mess with the base class.)

IMHO it's better to make a separate operator to check for the class. Again, an excelent (IMHO) implementation of this is Delphi: Class(object) // cast to Class, no type-check object as Class // cast to Class, throw exception on error object is Class // true if object is instance of Class So to call ThinkingEntity.Think(), you'd require the following code, supposing that "is" operator is supported in D: if (entity is ThinkingEntity) entity.Think(); Your code would look very similar: if (cast(ThinkingEntity) Entity) entity.Think(); So no real advantage here.
 My point is that from a single cast-else-null primitive, you can build
 cast-else-throw and and is-safe-to-cast? operations easily and
 efficiently; if the primitive is cast-else-throw, then the

The problem is that every cast operation must be wrapped in an if-block to throw exceptions. This is like, say, wrapping each C function call checking if it returns an error code and throwing an exception. IMHO, typechecking and typecasting should be _separate_ operations, not mixed. Because typecasting is usually performed on the generic pointer actually pointing to the child class when you know for sure that it does, and typechecking is used when you don't know what object your pointer references. These are two distinct things, let's not mix them.
 is-safe-to-cast? operation _must_ (as you noted) be provided by the
 language in a way that doesn't internally generate an exception, for
 performance reasons.

BTW I've just thought that this operation is already supported by D due to its RTTI system: if (entity.class == ClassThinkingEntity) entity.Think(); Personally, I like the "is" operator more, but even now D already has support for typechecking.
Nov 24 2001
next sibling parent reply Russell Borogove <kaleja estarcion.com> writes:
Pavel Minayev wrote:

 IMHO, typechecking and typecasting should be _separate_ operations,
 not mixed. Because typecasting is usually performed on the generic
 pointer actually pointing to the child class when you know for sure
 that it does, and typechecking is used when you don't know what object
 your pointer references. These are two distinct things, let's not
 mix them.

It's possible that I've been poisoned by years of working with functions that return a pointer or NULL on failure, but that's a familiar way for me to frame the idiom "if object supports X() then do X() on object," so we may just be up against personal style issues here.
 BTW I've just thought that this operation is already supported by
 D due to its RTTI system:
 
     if (entity.class == ClassThinkingEntity)
         entity.Think();
 
 Personally, I like the "is" operator more, but even now D already
 has support for typechecking.

But we need to know if the entity is a ThinkingEntity or any class derived from that. The class library I'm working with at my job has two separate operations (macros, actually) -- IsA(), which checks exact class membership, and IsKindOf(), which checks for membership in any derived class. Those would be the minimum primitives necessary in conjunction with a throwing cast to support the idiom. -RB
Nov 24 2001
parent reply "Pavel Minayev" <evilone omen.ru> writes:
"Russell Borogove" <kaleja estarcion.com> wrote in message
news:3C0003D0.9070305 estarcion.com...

 It's possible that I've been poisoned by years of working with
 functions that return a pointer or NULL on failure, but that's a
 familiar way for me to frame the idiom "if object supports X() then
 do X() on object," so we may just be up against personal style
 issues here.

It's a common style that I use as well, I just ain't satisfied with the syntax.
 But we need to know if the entity is a ThinkingEntity or any class
 derived from that.

Oh yes, I missed that. However, I'm pretty sure that classes of classes will have some way to determine the parent, for example: if (entity.class.extends(ClassThinkingEntity)) ... Or sumthing like that. Probably Walter will tell us if this works or not as he comes by this thread =)
 The class library I'm working with at my job has two separate
 operations (macros, actually) -- IsA(), which checks exact
 class membership, and IsKindOf(), which checks for membership
 in any derived class.

 Those would be the minimum primitives necessary in conjunction
 with a throwing cast to support the idiom.

In most cases, you actually use IsKindOf() or its equvalent. So I believe the exact typechecking could be left in its current form, and base class check should of course be introduced, hopefully in form of "is" operator.
Nov 24 2001
parent reply "Walter" <walter digitalmars.com> writes:
"Pavel Minayev" <evilone omen.ru> wrote in message
news:9tptu2$22je$1 digitaldaemon.com...
 "Russell Borogove" <kaleja estarcion.com> wrote in message
 news:3C0003D0.9070305 estarcion.com...
     if (entity.class.extends(ClassThinkingEntity)) ...

 Or sumthing like that. Probably Walter will tell us if this
 works or not as he comes by this thread =)

No, I had missed that. It's wierd how from time to time I discover interesting things in my own design <g>.
Nov 25 2001
parent "Sean L. Palmer" <spalmer iname.com> writes:
This sounds like fine syntax to me.  I still think that once you've
determined that an object is a subclass of some base class, you should be
able to do the typecast without checking the same thing again.

if (entity.class.extends(ClassThinkingEntity))
   (static_cast(ClassThinkingEntity) entity).Think();

or

(dynamic_cast(ClassThinkingEntity) entity).Think(); // dynamic_cast must
throw an exception if it can't do it.

But I'd hate to pay the RTTI lookup overhead twice.

Sean

"Walter" <walter digitalmars.com> wrote in message
news:9trei7$euv$1 digitaldaemon.com...
 "Pavel Minayev" <evilone omen.ru> wrote in message
 news:9tptu2$22je$1 digitaldaemon.com...
 "Russell Borogove" <kaleja estarcion.com> wrote in message
 news:3C0003D0.9070305 estarcion.com...
     if (entity.class.extends(ClassThinkingEntity)) ...

 Or sumthing like that. Probably Walter will tell us if this
 works or not as he comes by this thread =)

No, I had missed that. It's wierd how from time to time I discover interesting things in my own design <g>.

Nov 29 2001
prev sibling parent reply "Sean L. Palmer" <spalmer iname.com> writes:
 IMHO, typechecking and typecasting should be _separate_ operations,
 not mixed. Because typecasting is usually performed on the generic
 pointer actually pointing to the child class when you know for sure
 that it does, and typechecking is used when you don't know what object
 your pointer references. These are two distinct things, let's not
 mix them.

I agree with this. Dynamic typecasting can be almost zero overhead if the object is already known to be of such type or a descendant.
 is-safe-to-cast? operation _must_ (as you noted) be provided by the
 language in a way that doesn't internally generate an exception, for
 performance reasons.

BTW I've just thought that this operation is already supported by D due to its RTTI system: if (entity.class == ClassThinkingEntity) entity.Think(); Personally, I like the "is" operator more, but even now D already has support for typechecking.

Nope, that won't catch the case where entity is actually a class derived from ClassThinkingEntity. Maybe an 'is' operator here would be nice. Only takes class types as arguments (an object being implicitly convertible to its class type) Sean
Nov 29 2001
parent reply a <a b.c> writes:
	This post just gave me a thought.  It probably isn't appropriate for D
but I'll spit it out anyway.  Each type is going to have a property that
tells you the type right?  (Humor me.  Say yes.)  Likewise we could also
put such a property on the class identifier itself.  The property could
be the RTTI info.  I don't really care as long as it is a definite type
that the compiler can recognize.

	So you have:

		Class1;      // some class defined somewhere
		Class1 Obj1; // an instance of Class1
		Class2;	     // another class
		Class2 Obj2; // you know the drill

With the property I described above (I'll call it type) and some type
comparison operators we could have:

		// true iff Obj1 & Obj2 are instances 
		// of the same class
		Obj1.type == Obj2.type;

		// true iff Obj1 is a descendent of Obj2
		Obj1.type > Obj2.type;

		// true iff Obj1 is a parent of Obj2
		Obj1.type < Obj2.type;

Since classes have the property you could also say:

		// true iff Obj is an instance of Class
		Obj.type == Class.type;

You could also have all of the >=, <=, !=, <> type comparison too.  This
could be done in the compiler but I imagine the detractors of operator
overloading might also be offended by this.  It's clear (to me at least)
concise, and does require new keywords or syntax.

Thought?
Dan

"Sean L. Palmer" wrote:
 
 IMHO, typechecking and typecasting should be _separate_ operations,
 not mixed. Because typecasting is usually performed on the generic
 pointer actually pointing to the child class when you know for sure
 that it does, and typechecking is used when you don't know what object
 your pointer references. These are two distinct things, let's not
 mix them.

I agree with this. Dynamic typecasting can be almost zero overhead if the object is already known to be of such type or a descendant.
 is-safe-to-cast? operation _must_ (as you noted) be provided by the
 language in a way that doesn't internally generate an exception, for
 performance reasons.

BTW I've just thought that this operation is already supported by D due to its RTTI system: if (entity.class == ClassThinkingEntity) entity.Think(); Personally, I like the "is" operator more, but even now D already has support for typechecking.

Nope, that won't catch the case where entity is actually a class derived from ClassThinkingEntity. Maybe an 'is' operator here would be nice. Only takes class types as arguments (an object being implicitly convertible to its class type) Sean

Nov 29 2001
parent reply Russell Borogove <kaleja estarcion.com> writes:
a wrote:

 	So you have:
 
 		Class1;      // some class defined somewhere
 		Class1 Obj1; // an instance of Class1
 		Class2;	     // another class
 		Class2 Obj2; // you know the drill
 
 With the property I described above (I'll call it type) and some type
 comparison operators we could have:
 
 		// true iff Obj1 & Obj2 are instances 
 		// of the same class
 		Obj1.type == Obj2.type;
 
 		// true iff Obj1 is a descendent of Obj2
 		Obj1.type > Obj2.type;
 
 		// true iff Obj1 is a parent of Obj2
 		Obj1.type < Obj2.type;
 
 Since classes have the property you could also say:
 
 		// true iff Obj is an instance of Class
 		Obj.type == Class.type;
 
 You could also have all of the >=, <=, !=, <> type comparison too.  This
 could be done in the compiler but I imagine the detractors of operator
 overloading might also be offended by this.  It's clear (to me at least)
 concise, and does require new keywords or syntax.

I like it, but I will be perpetually confused because I'll think of parents as "greater than" children, or see the > as a directional indicator of the direction of descendance instead of the direction of inheritance. Also, it's unclear what the right answers are when obj1 and obj2 are completely unrelated classes -- probably all relationals should return false? -RB
Nov 29 2001
parent reply a <a b.c> writes:
Russell Borogove wrote:
 
 a wrote:
 
       So you have:

               Class1;      // some class defined somewhere
               Class1 Obj1; // an instance of Class1
               Class2;      // another class
               Class2 Obj2; // you know the drill

 With the property I described above (I'll call it type) and some type
 comparison operators we could have:

               // true iff Obj1 & Obj2 are instances
               // of the same class
               Obj1.type == Obj2.type;

               // true iff Obj1 is a descendent of Obj2
               Obj1.type > Obj2.type;

               // true iff Obj1 is a parent of Obj2
               Obj1.type < Obj2.type;

 Since classes have the property you could also say:

               // true iff Obj is an instance of Class
               Obj.type == Class.type;

 You could also have all of the >=, <=, !=, <> type comparison too.  This
 could be done in the compiler but I imagine the detractors of operator
 overloading might also be offended by this.  It's clear (to me at least)
 concise, and does require new keywords or syntax.

I like it, but I will be perpetually confused because I'll think of parents as "greater than" children, or see the > as a directional indicator of the direction of descendance instead of the direction of inheritance.

I saw it as meaning the child class was everything the parent was and more. A superset of sorts. However, swapping the operators probably wouldn't hurt. It would just have to have a good meaning that capture the inheritance relation and how the parent is 'greater'. (Age doesn't count.)
 Also, it's unclear what the right answers are when obj1 and obj2
 are completely unrelated classes -- probably all relationals should
 return false?

I guess I was thinking of that as being treated like the NaN cases with floats. They aren't equal and neither one is greater than the other. Isn't there a difference between != and <> for floats? Dan
Nov 29 2001
next sibling parent reply Russ Lewis <spamhole-2001-07-16 deming-os.org> writes:
a wrote:

 I saw it as meaning the child class was everything the parent was and
 more.  A superset of sorts.  However, swapping the operators probably
 wouldn't hurt.  It would just have to have a good meaning that capture
 the inheritance relation and how the parent is 'greater'.  (Age doesn't
 count.)

If you think of a class as a set of objects, then the parent is the larger, since it encompasses a larger set and each child class encompasses a subset of the parent set. That's how I've seen OOP theory taught. However, from the view of a programmer, a class is a definition of a set of functionality, and so the child is a larger set. I don't see any way to ensure clarity...other than just make a definition. That's too bad, since I think that these operators are a very Good Idea. Maybe it's worth the confusion...
 Also, it's unclear what the right answers are when obj1 and obj2
 are completely unrelated classes -- probably all relationals should
 return false?

I guess I was thinking of that as being treated like the NaN cases with floats. They aren't equal and neither one is greater than the other. Isn't there a difference between != and <> for floats?

How about throw an exception if they're not comparable? -- The Villagers are Online! http://villagersonline.com .[ (the fox.(quick,brown)) jumped.over(the dog.lazy) ] .[ (a version.of(English).(precise.more)) is(possible) ] ?[ you want.to(help(develop(it))) ]
Nov 29 2001
parent reply a <a b.c> writes:
Russ Lewis wrote:
 If you think of a class as a set of objects, then the parent is the larger,
since
 it encompasses a larger set and each child class encompasses a subset of the
parent
 set.  That's how I've seen OOP theory taught.
 
 However, from the view of a programmer, a class is a definition of a set of
 functionality, and so the child is a larger set.
 
 I don't see any way to ensure clarity...other than just make a definition. 
That's
 too bad, since I think that these operators are a very Good Idea.  Maybe it's
worth
 the confusion...

It would have to be an arbitrary rule. Just pick a semantic model. I can live with that.
 Also, it's unclear what the right answers are when obj1 and obj2
 are completely unrelated classes -- probably all relationals should
 return false?

I guess I was thinking of that as being treated like the NaN cases with floats. They aren't equal and neither one is greater than the other. Isn't there a difference between != and <> for floats?

How about throw an exception if they're not comparable?

If the test to see if they are not at all related isn't any more complicated than test for specific relation ships (i.e.. parent of, child of, same) then I'd hate to burden the operation with an exception throw. Are we sure that there will never be a case where someone would reasonably want to test if two objects are not related?
Nov 29 2001
parent reply Russ Lewis <spamhole-2001-07-16 deming-os.org> writes:
a wrote:

         If the test to see if they are not at all related isn't any more
 complicated than test for specific relation ships (i.e.. parent of,
 child of, same) then I'd hate to burden the operation with an exception
 throw.  Are we sure that there will never be a case where someone would
 reasonably want to test if two objects are not related?

It's thinking about this that caused me to ponder the greatestCommonClass idea described in my subsequent post. The test to see if two objects are related could be: if( greatestCommonClass(ptr1,ptr2) != Object) -- The Villagers are Online! http://villagersonline.com .[ (the fox.(quick,brown)) jumped.over(the dog.lazy) ] .[ (a version.of(English).(precise.more)) is(possible) ] ?[ you want.to(help(develop(it))) ]
Nov 30 2001
parent reply a <a b.c> writes:
Russ Lewis wrote:
 
 a wrote:
 
         If the test to see if they are not at all related isn't any more
 complicated than test for specific relation ships (i.e.. parent of,
 child of, same) then I'd hate to burden the operation with an exception
 throw.  Are we sure that there will never be a case where someone would
 reasonably want to test if two objects are not related?

It's thinking about this that caused me to ponder the greatestCommonClass idea described in my subsequent post. The test to see if two objects are related could be: if( greatestCommonClass(ptr1,ptr2) != Object)

A nifty idea, but it seems more appropriate in a dynamically typed language like perl or python. That still doesn't tell you that ptr1 is not ptr2's parent or visa versa. I was thinking that ptr1 <> ptr2; might mean true if neither of them is the descendant of the other. Frankly, I don't care if their closest common ancestor is Object or something closer. The greatest common class really doesn't provide that. You could implement <> with it, but you could also just use the <= & >= operators just as well. I'm trying to figure out how some one might reasonable use the function you describe, but I know the operators I've suggested would not be enough to implement it in any optimal fashion. Will/should D allow a programmer to use rtti to determine what type of object to allocate at runtime? If so, it feels like it could seriously compromise the abilities of compile time type checking severely. It could just be the cost of play with the operation. I suspect it could be very useful with regard to generic programming depending on how that is implemented. Dan
Nov 30 2001
next sibling parent reply Russ Lewis <spamhole-2001-07-16 deming-os.org> writes:
a wrote:

 Russ Lewis wrote:
 a wrote:

         If the test to see if they are not at all related isn't any more
 complicated than test for specific relation ships (i.e.. parent of,
 child of, same) then I'd hate to burden the operation with an exception
 throw.  Are we sure that there will never be a case where someone would
 reasonably want to test if two objects are not related?

It's thinking about this that caused me to ponder the greatestCommonClass idea described in my subsequent post. The test to see if two objects are related could be: if( greatestCommonClass(ptr1,ptr2) != Object)

A nifty idea, but it seems more appropriate in a dynamically typed language like perl or python. That still doesn't tell you that ptr1 is not ptr2's parent or visa versa.

Sure it does! switch( greatestCommonClass(ptr1,ptr2) ) { case ptr1.class: // ptr1 is a parent of ptr2 case ptr2.class: // ptr2 is a parent of ptr1 case Object: // no commonality default: // they share a common base class };
 I was thinking that

         ptr1 <> ptr2;

 might mean true if neither of them is the descendant of the other.
 Frankly, I don't care if their closest common ancestor is Object or
 something closer.  The greatest common class really doesn't provide
 that.  You could implement <> with it, but you could also just use the
 <= & >= operators just as well.
         I'm trying to figure out how some one might reasonable use the function
 you describe, but I know the operators I've suggested would not be
 enough to implement it in any optimal fashion.
         Will/should D allow a programmer to use rtti to determine what type of
 object to allocate at runtime?  If so, it feels like it could seriously
 compromise the abilities of compile time type checking severely.  It
 could just be the cost of play with the operation.  I suspect it could
 be very useful with regard to generic programming depending on how that
 is implemented.

I don't know the details of RTTI (I don't know *a lot* of the detail of compilers, but wouldn't the vtable pointer be a good RTTI identifier? Two objects of the same class would have identical vtable pointers. If you wanted, at runtime, the class name, then the vtable could include a pointer to a class name field. Even if the class doesn't need a vtable, you could include a vtable pointer and use it to gain access to the class name and do RTTI. As for the usefulness of greatestCommonClass (I'd love to call it gcc...if the acronym wasn't taken...what about 'gcp' for 'greatestCommonParent'?), I offered a very simple snippet from a generic container class in the original post. Would it be useful for me to flesh this example out? -- The Villagers are Online! http://villagersonline.com .[ (the fox.(quick,brown)) jumped.over(the dog.lazy) ] .[ (a version.of(English).(precise.more)) is(possible) ] ?[ you want.to(help(develop(it))) ]
Nov 30 2001
parent reply a <a b.c> writes:
Russ Lewis wrote:
 
 a wrote:
 
 Russ Lewis wrote:
 a wrote:

         If the test to see if they are not at all related isn't any more
 complicated than test for specific relation ships (i.e.. parent of,
 child of, same) then I'd hate to burden the operation with an exception
 throw.  Are we sure that there will never be a case where someone would
 reasonably want to test if two objects are not related?

It's thinking about this that caused me to ponder the greatestCommonClass idea described in my subsequent post. The test to see if two objects are related could be: if( greatestCommonClass(ptr1,ptr2) != Object)

A nifty idea, but it seems more appropriate in a dynamically typed language like perl or python. That still doesn't tell you that ptr1 is not ptr2's parent or visa versa.

Sure it does! switch( greatestCommonClass(ptr1,ptr2) ) { case ptr1.class: // ptr1 is a parent of ptr2 case ptr2.class: // ptr2 is a parent of ptr1 case Object: // no commonality default: // they share a common base class };

I hadn't thought of that. I don't know if switch was intended to work with anything other than integral types and maybe strings. It might be nice if it did, but depending on how switch is done, there might be other nasty side effects. Also, I thought the case values had to be constants know at compile time.
 I was thinking that

         ptr1 <> ptr2;

 might mean true if neither of them is the descendant of the other.
 Frankly, I don't care if their closest common ancestor is Object or
 something closer.  The greatest common class really doesn't provide
 that.  You could implement <> with it, but you could also just use the
 <= & >= operators just as well.
         I'm trying to figure out how some one might reasonable use the function
 you describe, but I know the operators I've suggested would not be
 enough to implement it in any optimal fashion.
         Will/should D allow a programmer to use rtti to determine what type of
 object to allocate at runtime?  If so, it feels like it could seriously
 compromise the abilities of compile time type checking severely.  It
 could just be the cost of play with the operation.  I suspect it could
 be very useful with regard to generic programming depending on how that
 is implemented.

I don't know the details of RTTI (I don't know *a lot* of the detail of compilers, but wouldn't the vtable pointer be a good RTTI identifier? Two objects of the same class would have identical vtable pointers. If you wanted, at runtime, the class name, then the vtable could include a pointer to a class name field. Even if the class doesn't need a vtable, you could include a vtable pointer and use it to gain access to the class name and do RTTI. As for the usefulness of greatestCommonClass (I'd love to call it gcc...if the acronym wasn't taken...what about 'gcp' for 'greatestCommonParent'?), I offered a very simple snippet from a generic container class in the original post. Would it be useful for me to flesh this example out?

I'm not concerned with how rtti will be implemented. I was thinking that the best use I could find for such a function would be to get type information at runtime in order to allocate a new object of this dynamically determined type. base* ptr = greatestCommonClass(ptr2,ptr3).new(swizzle(ptr2,ptr3)); Ya, I know the syntax is broken, but hopeful the idea was clear. In any case. I find the above dangerous. It's not as bad as creating a type at runtime in perl, but it feel like it is in the same arena. Of course, dangerous does not mean useless. I just like to understand when I have enough rope to hang myself. I guess greatestCommonClass would also be useful for determining if two classes have a common ancestor other that Object. That's about the only uses I see for it. You could it to see if two classes have a common ancestor that is a descendent of some known base like so: if(greatestCommonClass(p1, p2) >= WigetBase.class) meaningfulThings(); but you would get the same results with: if(p1.class >= WigetBase.class && p2.class >= WidetBase.class) meaningfulThings(); I'm not saying there aren't more uses. I'm just too dense to see them. Dan
Nov 30 2001
next sibling parent reply Russ Lewis <spamhole-2001-07-16 deming-os.org> writes:
How about this class hierarchy:

interface Comparable;
class Apples : Comparable;
class ApplesChild1 : Apples;
class ApplesChild2 : Apples;
class Oranges : Comparable;
class OrangesChild1 : Oranges;
class OrangesChild2 : Oranges;

The idea here is that both Apples and Oranges implement the functions of
interface Comparable.
Obviously, you can't compare apples and oranges. :)

So here's a snippet from a container class:

int Compare(Object *ptr1, Object *ptr2)
{
    Class common = gcp(ptr1,ptr2); // greatestCommonParent

    if(common = Object)
        throw Exception("Cannot compare objects: no common parent classes");
    if( !common.implements(Comparable) )
        throw Exception("Cannot compare objects: common parent class does not
implement interface
Comparable");
    return ptr1->Compare(ptr2);
}



Does this code make sense to anybody but me?

--
The Villagers are Online! http://villagersonline.com

.[ (the fox.(quick,brown)) jumped.over(the dog.lazy) ]
.[ (a version.of(English).(precise.more)) is(possible) ]
?[ you want.to(help(develop(it))) ]
Dec 01 2001
parent reply a <a b.c> writes:
Russ Lewis wrote:
 
 How about this class hierarchy:
 
 interface Comparable;
 class Apples : Comparable;
 class ApplesChild1 : Apples;
 class ApplesChild2 : Apples;
 class Oranges : Comparable;
 class OrangesChild1 : Oranges;
 class OrangesChild2 : Oranges;
 
 The idea here is that both Apples and Oranges implement the functions of
interface Comparable.
 Obviously, you can't compare apples and oranges. :)
 
 So here's a snippet from a container class:
 
 int Compare(Object *ptr1, Object *ptr2)
 {
     Class common = gcp(ptr1,ptr2); // greatestCommonParent
 
     if(common = Object)
         throw Exception("Cannot compare objects: no common parent classes");
     if( !common.implements(Comparable) )
         throw Exception("Cannot compare objects: common parent class does not
implement interface
 Comparable");
     return ptr1->Compare(ptr2);
 }
 
 Does this code make sense to anybody but me?

I see how it works, but it is not very general purpose. If Apple and Orange had some other common base (say fruit) this suddenly breaks. This container simply uses the first ancestor after Object to divide types into equivalence classes. A general container should not make this type of assumption. A domain specific probably ought to deal more with the base classes it was designed for. I guess you could have a common base other than object (again Fruit) and make it a design decision that direct children of that class are not comparable. I would also put the requirement to implement compare in that class. I guess I see how it can be used. I don't like it, but it will take a while for me to decide if it's just because it's new to me. You do know that such a design will require the eventual addition of multiple inheritance once the biological engineers make a fruit that can be compared to it's own kind as well as apples and oranges. :-) Also your error message in the first if is wrong. All classes in D have a common parent class. I see you are trying to simulate a C++ style object model in this sense but it may clash with D code that does not differentiate between Objects that only have a parent of Object. Dan
Dec 01 2001
parent Russ Lewis <spamhole-2001-07-16 deming-os.org> writes:
a wrote:

 I see how it works, but it is not very general purpose.  If Apple and
 Orange had some other common base (say fruit) this suddenly breaks.

But if the programmer has declared (by the class structure) that Fruit are Comparable, then why not use that to compare them?
 This container simply uses the first ancestor after Object to divide
 types into equivalence classes.  A general container should not make
 this type of assumption.  A domain specific probably ought to deal more
 with the base classes it was designed for.

I'm not sure why this is a problem. Examples, please, so I can understand better?
         I guess you could have a common base other than object (again Fruit)
 and make it a design decision that direct children of that class are not
 comparable.  I would also put the requirement to implement compare in
 that class.  I guess I see how it can be used.  I don't like it, but it
 will take a while for me to decide if it's just because it's new to me.
         You do know that such a design will require the eventual addition of
 multiple inheritance once the biological engineers make a fruit that can
 be compared to it's own kind as well as apples and oranges.  :-)

???? If fruit are comparable, then why not just declare Fruit as implementing interface Comparable? I'm lost again.
         Also your error message in the first if is wrong.  All classes in D
 have a common parent class.  I see you are trying to simulate a C++
 style object model in this sense but it may clash with D code that does
 not differentiate between Objects that only have a parent of Object.

True, it was kind of rough. Acutally, the message should be "Cannot compare objects: no common parent class other than Object" -- The Villagers are Online! villagersonline.com .[ (the fox.(quick,brown)) jumped.over(the dog.lazy) ] .[ (a version.of(English).(precise.more)) is(possible) ] ?[ you want.to(help(develop(it))) ]
Dec 03 2001
prev sibling parent reply Russ Lewis <spamhole-2001-07-16 deming-os.org> writes:
a wrote:

 I hadn't thought of that.  I don't know if switch was intended to work
 with anything other than integral types and maybe strings.  It might be
 nice if it did, but depending on how switch is done, there might be
 other nasty side effects.  Also, I thought the case values had to be
 constants know at compile time.

heh. Well, it can be an if...else if otherwise. Would be cool if switch would work on this, though... -- The Villagers are Online! http://villagersonline.com .[ (the fox.(quick,brown)) jumped.over(the dog.lazy) ] .[ (a version.of(English).(precise.more)) is(possible) ] ?[ you want.to(help(develop(it))) ]
Dec 01 2001
parent reply "Sean L. Palmer" <spalmer iname.com> writes:
Walter says D's switch statement allows strings.  I don't see why it has to
be limited to only constants, since a switch can always be rewritten as a
series of if .. else if ... else clauses (with a break for a case being
simulated with a goto) and if's are not limited to only constants.

Sean

"Russ Lewis" <spamhole-2001-07-16 deming-os.org> wrote in message
news:3C08E67F.664DE454 deming-os.org...
 a wrote:

 I hadn't thought of that.  I don't know if switch was intended to work
 with anything other than integral types and maybe strings.  It might be
 nice if it did, but depending on how switch is done, there might be
 other nasty side effects.  Also, I thought the case values had to be
 constants know at compile time.

heh. Well, it can be an if...else if otherwise. Would be cool if switch

 though...

Dec 02 2001
parent reply a <a b.c> writes:
"Sean L. Palmer" wrote:
 
 Walter says D's switch statement allows strings.  I don't see why it has to
 be limited to only constants, since a switch can always be rewritten as a
 series of if .. else if ... else clauses (with a break for a case being
 simulated with a goto) and if's are not limited to only constants.
 
 Sean
 
 "Russ Lewis" <spamhole-2001-07-16 deming-os.org> wrote in message
 news:3C08E67F.664DE454 deming-os.org...
 a wrote:

 I hadn't thought of that.  I don't know if switch was intended to work
 with anything other than integral types and maybe strings.  It might be
 nice if it did, but depending on how switch is done, there might be
 other nasty side effects.  Also, I thought the case values had to be
 constants know at compile time.

heh. Well, it can be an if...else if otherwise. Would be cool if switch

 though...


I believe if was suppose to be the high level equivalent of a conditional branch. Switch was supposed to be the equivalent of a jump table. The is a performance difference. C made it pretty easy to look at the code and know what the compiler was going to do to it. Dan
Dec 02 2001
parent reply "Sean L. Palmer" <spalmer iname.com> writes:
There's nothing stopping a D compiler from turning a suitable switch into a
jump table.  In the string case the compiler could build a hidden static
constant associative array to a jump address, or it could turn it into a
series of if-else statements.  For integers or enums it should be able to
optimize as much as C can.  I can't think of a reason anyone would want to
switch on the value of a float unless one could specify ranges for each
case.  I have no idea if switches on class instances would be legal or not,
as it seems like we won't be able to override class operator ==.

What is the defined behavior for comparing two object instances?  Compare
the actual pointers, and if they're the same return true.  Otherwise compare
their vtable pointers, and if they're the same, compare the actual data
member by member.

I suppose memberwise assignment of an object to another isn't possible,
since it seems it would just assign the reference pointer?  What about
structs?

Sean

"a" <a b.c> wrote in message news:3C0B150D.E8A5ADAC b.c...
 I believe if was suppose to be the high level equivalent of a
 conditional branch.  Switch was supposed to be the equivalent of a jump
 table.  The is a performance difference.  C made it pretty easy to look
 at the code and know what the compiler was going to do to it.

 Dan

Dec 03 2001
next sibling parent "Pavel Minayev" <evilone omen.ru> writes:
"Sean L. Palmer" <spalmer iname.com> wrote in message
news:9ufi76$2k1b$1 digitaldaemon.com...

 What is the defined behavior for comparing two object instances?  Compare
 the actual pointers, and if they're the same return true.  Otherwise

 their vtable pointers, and if they're the same, compare the actual data
 member by member.

I believe pointers are compared. Otherwise, there's no way to find out if two references point to the same object.
 I suppose memberwise assignment of an object to another isn't possible,
 since it seems it would just assign the reference pointer?  What about
 structs?

Since structs are not references, it seems just logical for them to be copied on assignment, like in C/C++.
Dec 03 2001
prev sibling parent reply "Walter" <walter digitalmars.com> writes:
"Sean L. Palmer" <spalmer iname.com> wrote in message
news:9ufi76$2k1b$1 digitaldaemon.com...
 What is the defined behavior for comparing two object instances?  Compare
 the actual pointers, and if they're the same return true.  Otherwise

 their vtable pointers, and if they're the same, compare the actual data
 member by member.

Compare the pointers. There is a root overridable method called cmp(), however. cmp() will get used for sorting arrays, for building associative arrays out of objects, etc.
 I suppose memberwise assignment of an object to another isn't possible,
 since it seems it would just assign the reference pointer?

Correct.
 What about structs?

It'll always do a bit copy - based on the notion that structs are simple aggregations. I've always been uncomfortable with the C++ notion that arbitrary functions can be executed when copying structs.
Dec 05 2001
next sibling parent reply Russell Borogove <kaleja estarcion.com> writes:
Walter wrote:

 "Sean L. Palmer" <spalmer iname.com> wrote in message
 news:9ufi76$2k1b$1 digitaldaemon.com...
I suppose memberwise assignment of an object to another isn't possible,
since it seems it would just assign the reference pointer?

Correct.

Does this mean the language should provide a deep-copy or clone operation?
What about structs?

It'll always do a bit copy - based on the notion that structs are simple aggregations.

Will that break structs that include class members? -R
Dec 05 2001
parent reply "Pavel Minayev" <evilone omen.ru> writes:
"Russell Borogove" <kaleja estarcion.com> wrote in message
news:3C0E62DD.803 estarcion.com...

 Does this mean the language should provide a deep-copy or clone operation?

I've also thought of it... maybe a built-in assign() method or a new operator (":=" ?) or something like that?
 It'll always do a bit copy - based on the notion that structs are simple
 aggregations.

Will that break structs that include class members?

Why? It'll simply copy the pointer, so you get two structs referencing one object - not a problem.
Dec 05 2001
parent reply Russell Borogove <kaleja estarcion.com> writes:
Pavel Minayev wrote:
 
 "Russell Borogove" <kaleja estarcion.com> wrote in message
 news:3C0E62DD.803 estarcion.com...
 
 Does this mean the language should provide a deep-copy or clone operation?

I've also thought of it... maybe a built-in assign() method or a new operator (":=" ?) or something like that?
 It'll always do a bit copy - based on the notion that structs are simple
 aggregations.

Will that break structs that include class members?

Why? It'll simply copy the pointer, so you get two structs referencing one object - not a problem.

As long as the referenced object gets its refcount increased, I suppose so.[1] I guess I'm still thinking in terms of the bastardized C/C++ hybrid code that I deal with daily, where references and pointers are mixed at whim, and you have to play "guess the semantic" everywhere. (Especially when the compiler randomly decides that reference members in structs somehow make struct initializers not work. Grrrrrr.) -RB [1] Walter, does updating ref counts qualify as "arbitrary functions [...] executed when copying structs?"
Dec 05 2001
parent "Pavel Minayev" <evilone omen.ru> writes:
"Russell Borogove" <kaleja estarcion.com> wrote in message
news:3C0E7447.E2DA8E9F estarcion.com...

 [1] Walter, does updating ref counts qualify as "arbitrary functions
 [...] executed when copying structs?"

Ref counts? I've always thought there are none in D, and different techniques are used...
Dec 05 2001
prev sibling parent reply "Sean L. Palmer" <spalmer iname.com> writes:
Walter, the more I hear you talk, the more I think D may not be for me.
People that afraid of code being executed that isn't specified right there
in the source file are people that want big bloated source files, and that
isn't for me.  I want the compiler to do stuff automatically for me, if it
is so instructed or designed.  It's not like people can't figure out what's
going on by stepping through the code in the debugger.

It seems to me that all the nicest and most powerful features of C++ have
been dropped just to end up with a language that cleans up just a few of
C/C++'s worst things (header files, memory leaks) without really adding much
besides native string support and associative and dynamic arrays.  Guess
what... you can already do all that stuff in C++, using templates and
operator overloading, 2 of the things you seems to abhor.

Walter, I think you and I just have a basic philosophical difference.  D
sounded nice at first but the more I listen to you the more I realize it's
just not the right language for me.  Maybe C# version 2.0 will be ok...

Sean


"Walter" <walter digitalmars.com> wrote in message
news:9ukrph$k78$1 digitaldaemon.com...
 "Sean L. Palmer" <spalmer iname.com> wrote in message
 news:9ufi76$2k1b$1 digitaldaemon.com...
 What is the defined behavior for comparing two object instances?


 the actual pointers, and if they're the same return true.  Otherwise

 their vtable pointers, and if they're the same, compare the actual data
 member by member.

Compare the pointers. There is a root overridable method called cmp(), however. cmp() will get used for sorting arrays, for building associative arrays out of objects, etc.
 I suppose memberwise assignment of an object to another isn't possible,
 since it seems it would just assign the reference pointer?

Correct.
 What about structs?

It'll always do a bit copy - based on the notion that structs are simple aggregations. I've always been uncomfortable with the C++ notion that arbitrary

 can be executed when copying structs.

Dec 06 2001
parent "Walter" <walter digitalmars.com> writes:
It's entirely possible we have different philosophies on what makes a great
language. And that's fine, the give and take here is valuable. I have read
your posts with great interest.

One crucial feature of D you didn't mention, and which unfortuately isn't
obvious, is the design by contract and unit testing features. I've tried to
do them in my C++ code, but the results aren't pretty or satisfactory. When
I have used them, I discovered that the code was much more bug-free,
reliable, and development was faster.

-Walter

"Sean L. Palmer" <spalmer iname.com> wrote in message
news:9unc1g$ed$1 digitaldaemon.com...
 Walter, the more I hear you talk, the more I think D may not be for me.
 People that afraid of code being executed that isn't specified right there
 in the source file are people that want big bloated source files, and that
 isn't for me.  I want the compiler to do stuff automatically for me, if it
 is so instructed or designed.  It's not like people can't figure out

 going on by stepping through the code in the debugger.

 It seems to me that all the nicest and most powerful features of C++ have
 been dropped just to end up with a language that cleans up just a few of
 C/C++'s worst things (header files, memory leaks) without really adding

 besides native string support and associative and dynamic arrays.  Guess
 what... you can already do all that stuff in C++, using templates and
 operator overloading, 2 of the things you seems to abhor.

 Walter, I think you and I just have a basic philosophical difference.  D
 sounded nice at first but the more I listen to you the more I realize it's
 just not the right language for me.  Maybe C# version 2.0 will be ok...

 Sean


 "Walter" <walter digitalmars.com> wrote in message
 news:9ukrph$k78$1 digitaldaemon.com...
 "Sean L. Palmer" <spalmer iname.com> wrote in message
 news:9ufi76$2k1b$1 digitaldaemon.com...
 What is the defined behavior for comparing two object instances?


 the actual pointers, and if they're the same return true.  Otherwise

 their vtable pointers, and if they're the same, compare the actual



 member by member.

Compare the pointers. There is a root overridable method called cmp(), however. cmp() will get used for sorting arrays, for building


 arrays out of objects, etc.

 I suppose memberwise assignment of an object to another isn't



 since it seems it would just assign the reference pointer?

Correct.
 What about structs?

It'll always do a bit copy - based on the notion that structs are simple aggregations. I've always been uncomfortable with the C++ notion that arbitrary

 can be executed when copying structs.


Dec 11 2001
prev sibling parent reply "Sean L. Palmer" <spalmer iname.com> writes:
 Will/should D allow a programmer to use rtti to determine what type of
 object to allocate at runtime?  If so, it feels like it could seriously
 compromise the abilities of compile time type checking severely.  It
 could just be the cost of play with the operation.  I suspect it could
 be very useful with regard to generic programming depending on how that
 is implemented.

The ability to store an object's type to a file and then once you reload that type use some kind of virtual constructor scheme to use the loaded type to construct a new object. Probably would want to pass the file as a constructor parameter. If you can support this, it will make storing out object hierarchies much easier. I suppose we could make our own Streamable class which provides this functionality so long as we can get a storable value from the RTTI system from an object to represent its type, and make 'new' able to understand such values. It'd probably end up returning an Object reference since the compiler couldn't know what exact type it would be. One could always then inquire the object's type if we needed to work with it, or we could just typecast it to a known base class type. (similar to malloc) Sean
Dec 02 2001
parent "Pavel Minayev" <evilone omen.ru> writes:
Speaking of virtual constructors (which are probably
the most important point in this scheme), Walter
said that it could be a "good idea". Hopefully this
means that we'll see them in D.
Dec 02 2001
prev sibling parent Russ Lewis <spamhole-2001-07-16 deming-os.org> writes:
Another idea...it might be useful to include some sort of "greatest common
class"
function.

Object* ptr1,ptr2;
Class foo = greatestCommonClass(ptr1,ptr2);

It could be useful for generics:

Object* ptr1,ptr2;
Class base = greatestCommonClass(ptr1,ptr2);
if( !base.isChildOf(Comparable) )
   throw InvalidClassException("pointer cannot be compared");

if( (base*)ptr1.isLessThan((base*)ptr2) )
   etc...

--
The Villagers are Online! http://villagersonline.com

.[ (the fox.(quick,brown)) jumped.over(the dog.lazy) ]
.[ (a version.of(English).(precise.more)) is(possible) ]
?[ you want.to(help(develop(it))) ]
Nov 29 2001