Traits

Jonathan M Davis jmdavisProg at gmx.com
Sat Oct 12 03:21:14 PDT 2013


On Saturday, October 12, 2013 11:50:27 luminousone wrote:
> On Saturday, 12 October 2013 at 08:40:42 UTC, Jonathan M Davis
> 
> wrote:
> > On Saturday, October 12, 2013 10:24:13 luminousone wrote:
> >> 1.
> >> 
> >> opCast, and alias can break the cast approach. You pointed this
> >> out yourself.
> > 
> > And as I pointed out, that's perfectly acceptable in most
> > cases, because what
> > you care about is the conversion. alias this is specifically
> > designed with the
> > idea that you can subtype a struct or class with it. Checking
> > with typeid to
> > get the exact type would be circumventing that design. So,
> > casting is almost
> > always the correct option.
> > 
> >> 2.
> >> 
> >> The requested function is testing types not instance state.
> >> 
> >> bool instanceOf(A,B)( B value ) {
> >> 
> >>          assert( value !is null );
> >>          return inheritsFrom(A,typeof(value));
> >> 
> >> }
> > 
> > Fine. That doesn't invalidate casting to check an instance.
> > They're two
> > different things.
> > 
> >> 3.
> >> 
> >> Cast breaks on null, so this must be checked on every call, and
> >> leads to a 3 state outcome, true, false, and null, null then
> >> returns false, which is potentially incorrect and could cause
> >> odd
> >> bugs hard to trace.
> > 
> > null is _supposed_ to be false. The whole point is to check
> > whether the object
> > in question can be used as the type that you're casting to. If
> > it's null, it
> > isn't the type that you're casting to - it's null. So, false is
> > exactly what
> > it should be doing. It's specifically designed so that you can
> > do
> > 
> > if(auto c = cast(MyClass)obj)
> > {
> > 
> >     ...
> > 
> > }
> > 
> >> Again it is bad practice regardless of what any document says.
> > 
> > And I completely disagree. Casting is doing precisely what it's
> > designed to
> > do, and I really don't see a problem with it.
> > 
> > - Jonathan M Davis
> 
> 1.
> 
> Your hand waving corner cases.

No. I'm saying that where there are corner cases, they are intentional and 
expected. Casting takes conversions into account that are supposed to be 
there, and using any kind reflection would bypass that.

And if you know that you're dealing with a reference to a base class, and 
you're testing whether that reference points to a particular derived class (as 
opposed to simply checking whether the conversion works), you know that the 
corner cases don't even exist unless the base class overrode opCast or alias 
this to that derived class (which would be a really broken design, since base 
classes shouldn't know about derived classes).

> 2.
> 
> Your throwing away perfectly good opportunities for the compiler
> to use CTFE by depending on allocated instance state(or at least
> its address) to get your answers.

It's _impossible_ to use CTFE for testing whether a particular instance is of 
a particular type. You can test its _reference_. You can test whether a 
particular type is derived from another. But you _can't_ test a particular 
instance, because it doesn't even exist at compile time.

> 3.
> 
> Hardly, casting null works perfectly fine in c and c++, D strives
> towards more correct code, its one of the things I love about the
> language.
> 
> It is precisely because D checks null on cast, that creates the
> potential ternary return. A corner case I grant you, but still.
> 
> Further D being a statically typed language, it could very well
> be argued, that a nulled reference, still has a type.

Sure, the reference has a type, but the object doesn't because there isn't 
one. It's typeof(null). And the whole point of casting to check the type is to 
check the obect's type, not the reference. If it's null, the reference does 
_not_ point to an object, so there's no object to test, and it is _not_ of the 
type that you're testing for.

> meaning...
> 
> bool instanceOf(A,B)( B value ) {
>           return inheritsFrom(A,B);
> }
> 
> would be more correct then using typeOf(value) version above.

Again, this is completely wrong for the case where you're checking an object's 
type. That is a _runtime_ operation for determining what the type of the 
actual object being referred to is. All you're doing here is checking whether 
B is a derived class of A, which is for testing the types of the _references_, 
not the actual type of the object. B could inherit from A, but the object 
could in fact be of type C which is derived from B - or any other type which 
is derived from B. e.g.

class A {}
class B : A {}
class C : B {}

A a1 = new A;
A a2 = new B;
A a3 = new C;

assert(cast(B)a1 is null);
assert(cast(B)a2 !is null);
assert(cast(B)a3 !is null);

These are all testing the objects being referred to and _not_ anything about 
the types themselves.

You use compile time reflection when you're testing _types_ (e.g. whether B is 
derived from A). You use casts to determine whether a reference of a base type 
is actually referring to an object of a particular derived class. The two use 
cases are completely different. If you're using compile time reflection to 
determine whether a particular instance is of a particular type, then you're 
doing it wrong, because all you're testing are the references, not the object. 
If you're using casts to determine whether a particular type is derived from 
another type, then that's wrong because of the extra conversions that could 
take place (not to mention, it's then a runtime check instead of a compile-
time one). You have to use the right test for the right situation.

- Jonathan M Davis


More information about the Digitalmars-d-learn mailing list