|
Archives
D Programming
D
D.gnu
digitalmars.D
digitalmars.D.bugs
digitalmars.D.dtl
digitalmars.D.dwt
digitalmars.D.announce
digitalmars.D.learn
digitalmars.D.debugger
C/C++ Programming
c++
c++.announce
c++.atl
c++.beta
c++.chat
c++.command-line
c++.dos
c++.dos.16-bits
c++.dos.32-bits
c++.idde
c++.mfc
c++.rtl
c++.stl
c++.stl.hp
c++.stl.port
c++.stl.sgi
c++.stlsoft
c++.windows
c++.windows.16-bits
c++.windows.32-bits
c++.wxwindows
digitalmars.empire
digitalmars.DMDScript
|
D - casts
↑ ↓ ← → "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...
↑ ↓ ← → "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...
↑ ↓ ← → "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.
↑ ↓ ← → "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.
↑ ↓ ← → 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.
↑ ↓ ← → "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
↑ ↓ ← → 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
↑ ↓ ← → "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.
↑ ↓ ← → 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
↑ ↓ ← → "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.
↑ ↓ ← → "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>.
↑ ↓ ← → "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>.
↑ ↓ ← → "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
↑ ↓ ← → 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
↑ ↓ ← → 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
↑ ↓ ← → 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
↑ ↓ ← → 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))) ]
↑ ↓ ← → 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?
↑ ↓ ← → 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))) ]
↑ ↓ ← → 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
↑ ↓ ← → 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))) ]
↑ ↓ ← → 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
↑ ↓ ← → 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))) ]
↑ ↓ ← → 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
↑ ↓ ← → 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))) ]
↑ ↓ ← → 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))) ]
↑ ↓ ← → "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...
↑ ↓ ← → 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
↑ ↓ ← → "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
↑ ↓ ← → "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++.
↑ ↓ ← → "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.
↑ ↓ ← → 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
↑ ↓ ← → "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.
↑ ↓ ← → 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?"
↑ ↓ ← → "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...
↑ ↓ ← → "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.
↑ ↓ ← → "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.
↑ ↓ ← → "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
↑ ↓ ← → "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.
↑ ↓ ← → 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))) ]
|
|