Thread overview
Dynamic and Static Casting
Feb 10, 2011
d coder
Feb 10, 2011
bearophile
Feb 10, 2011
d coder
Feb 10, 2011
spir
Feb 10, 2011
bearophile
February 10, 2011
Greetings All

I have learnt that D has only one casting operator and that is 'cast'. The same operator assumes different functionality depending on the context in which it he being used.

Now I have a situation where I have to downcast an object and I am sure of the objects type and thereby I am sure that the downcast would only be successful. To make the operation faster, in C++ I could have used static_cast operator, thus giving the RTTI a skip. Would this be possible in D? Can I force a static_cast which downcasting?

Regards
- Cherry
February 10, 2011
On Thu, 10 Feb 2011 16:44:12 +0530, d coder wrote:

> Greetings All
> 
> I have learnt that D has only one casting operator and that is 'cast'. The same operator assumes different functionality depending on the context in which it he being used.
> 
> Now I have a situation where I have to downcast an object and I am sure of the objects type and thereby I am sure that the downcast would only be successful. To make the operation faster, in C++ I could have used static_cast operator, thus giving the RTTI a skip. Would this be possible in D? Can I force a static_cast which downcasting?

Here's one solution.  I am not 100% sure of the validity of this, so until someone else vouches for it, it should be considered evil.  It works by first casting the reference to a pointer, then to a different pointer type (which goes unchecked), and then to a reference again.

    // Evil hack to emulate static_cast
    T staticCast(T, F)(F from)
    {
        return cast(T) cast(T*) cast(F*) from;
    }

Example usage:

    class A { int i; }
    class B : A { int j; }

    void main()
    {
        auto b = new B;
        b.i = 123;

        auto a = staticCast!A(b);
        assert (a.i == 123);
    }

-Lars
February 10, 2011
d coder:

> I have learnt that D has only one casting operator and that is 'cast'. The same operator assumes different functionality depending on the context in which it he being used.

Walter likes this design, I presume he thinks it's simpler.


> Now I have a situation where I have to downcast an object and I am sure of the objects type and thereby I am sure that the downcast would only be successful. To make the operation faster, in C++ I could have used static_cast operator, thus giving the RTTI a skip. Would this be possible in D? Can I force a static_cast which downcasting?

There is no direct support for it because it's considered bad, so this is usually not done in D, it's for special situations only:


class Foo {}
class Bar : Foo {}
Bar test1() {
    Foo f = new Foo;
    Bar b = cast(Bar)f;
    return b;
}
Bar test2() {
    Foo f = new Foo;
    Bar b = cast(Bar)cast(void*)f;
    return b;
}
void main() {}


DMD 2.051, -O -release -inline:

_D4test5test1FZC4test3Bar   comdat
L0:     push    EAX
        mov EAX,offset FLAT:_D4test3Bar7__ClassZ
        mov ECX,offset FLAT:_D4test3Foo7__ClassZ
        push    EAX
        push    ECX
        call    near ptr __d_newclass
        add ESP,4
        push    EAX
        call    near ptr __d_dynamic_cast
        add ESP,8
        pop ECX
        ret

_D4test5test2FZC4test3Bar   comdat
L0:     push    EAX
        mov EAX,offset FLAT:_D4test3Foo7__ClassZ
        push    EAX
        call    near ptr __d_newclass
        add ESP,4
        pop ECX
        ret

Bye,
bearophile
February 10, 2011
On Thu, 10 Feb 2011 11:54:02 +0000, Lars T. Kyllingstad wrote:

> On Thu, 10 Feb 2011 16:44:12 +0530, d coder wrote:
> 
>> Greetings All
>> 
>> I have learnt that D has only one casting operator and that is 'cast'. The same operator assumes different functionality depending on the context in which it he being used.
>> 
>> Now I have a situation where I have to downcast an object and I am sure of the objects type and thereby I am sure that the downcast would only be successful. To make the operation faster, in C++ I could have used static_cast operator, thus giving the RTTI a skip. Would this be possible in D? Can I force a static_cast which downcasting?
> 
> Here's one solution.  [...]

Ok, bearophile's solution is better, because it has fewer casts.  I forgot you can cast to void*.  So here's an improved version, with some template constraints to make sure it's only used for class types:

    T staticCast(T, U)(U obj)  if (is(T == class) && is(U == class))
    {
        return cast(T) cast(void*) obj;
    }

-Lars
February 10, 2011
Lars T. Kyllingstad:

> Ok, bearophile's solution is better, because it has fewer casts.

And your solution was better because it's inside a function :-)


> I forgot you can cast to void*.  So here's an improved version, with some template constraints to make sure it's only used for class types:
> 
>     T staticCast(T, U)(U obj)  if (is(T == class) && is(U == class))
>     {
>         return cast(T) cast(void*) obj;
>     }


And what about:

import std.stdio, std.traits, std.typetuple;

/// C++ static_cast for just down-casting
T staticDownCast(T, F)(F from) if (is(F == class) &&
                                   is(T == class) &&
                                   staticIndexOf!(F, BaseClassesTuple!T) != -1)
    in {
        assert((from is null) || cast(T)from !is null);
    } body {
        return cast(T)cast(void*)from;
    }

class Foo {}
class Bar : Foo {}
class Spam {}

Bar test1() {
    Foo f = new Foo;
    Bar b = cast(Bar)f;
    return b;
}

Bar test2() {
    Foo f = new Foo;
    Bar b = staticDownCast!Bar(f);
    return b;
}

void main() {
    Spam s = new Spam;
    Bar b = staticDownCast!Bar(s); // error
}

/*
_D4test5test1FZC4test3Bar   comdat
L0:     push    EAX
        mov EAX,offset FLAT:_D4test3Bar7__ClassZ
        mov ECX,offset FLAT:_D4test3Foo7__ClassZ
        push    EAX
        push    ECX
        call    near ptr __d_newclass
        add ESP,4
        push    EAX
        call    near ptr __d_dynamic_cast
        add ESP,8
        pop ECX
        ret

_D4test5test2FZC4test3Bar   comdat
L0:     push    EAX
        mov EAX,offset FLAT:_D4test3Foo7__ClassZ
        push    EAX
        call    near ptr __d_newclass
        add ESP,4
        pop ECX
        ret
*/


Is a pair of similar staticDownCast(), staticUpCast() fit for Phobos?

Bye,
bearophile
February 10, 2011
Thanks Lars and Bearophile, I will give it a try.

I understand that static downcasting is dangerous. But there are places where efficiency is paramount and you are sure that the casting is safe. So I wholeheartedly second your proposal to have the stuff in phobos.

Regards
- Cherry
February 10, 2011
On Thu, 10 Feb 2011 08:02:08 -0500, d coder <dlang.coder@gmail.com> wrote:

> Thanks Lars and Bearophile, I will give it a try.
>
> I understand that static downcasting is dangerous. But there are
> places where efficiency is paramount and you are sure that the casting
> is safe. So I wholeheartedly second your proposal to have the stuff in
> phobos.

Be aware that blindly casting interfaces is not a good idea.  An interface pointer is offset into the object and the cast *must* be dynamic.

Casting objects from one class to another should be reliable, however.

-Steve
February 10, 2011
On 02/10/2011 01:38 PM, bearophile wrote:
> Lars T. Kyllingstad:
>
>> Ok, bearophile's solution is better, because it has fewer casts.
>
> And your solution was better because it's inside a function :-)
>
>
>> I forgot you can cast to void*.  So here's an improved version, with some
>> template constraints to make sure it's only used for class types:
>>
>>      T staticCast(T, U)(U obj)  if (is(T == class)&&  is(U == class))
>>      {
>>          return cast(T) cast(void*) obj;
>>      }
>
>
> And what about:
>
> import std.stdio, std.traits, std.typetuple;
>
> /// C++ static_cast for just down-casting
> T staticDownCast(T, F)(F from) if (is(F == class)&&
>                                     is(T == class)&&
>                                     staticIndexOf!(F, BaseClassesTuple!T) != -1)
>      in {
>          assert((from is null) || cast(T)from !is null);
>      } body {
>          return cast(T)cast(void*)from;
>      }
>
> class Foo {}
> class Bar : Foo {}
> class Spam {}
>
> Bar test1() {
>      Foo f = new Foo;
>      Bar b = cast(Bar)f;
>      return b;
> }
>
> Bar test2() {
>      Foo f = new Foo;
>      Bar b = staticDownCast!Bar(f);
>      return b;
> }
>
> void main() {
>      Spam s = new Spam;
>      Bar b = staticDownCast!Bar(s); // error
> }
>
> /*
> _D4test5test1FZC4test3Bar   comdat
> L0:     push    EAX
>          mov EAX,offset FLAT:_D4test3Bar7__ClassZ
>          mov ECX,offset FLAT:_D4test3Foo7__ClassZ
>          push    EAX
>          push    ECX
>          call    near ptr __d_newclass
>          add ESP,4
>          push    EAX
>          call    near ptr __d_dynamic_cast
>          add ESP,8
>          pop ECX
>          ret
>
> _D4test5test2FZC4test3Bar   comdat
> L0:     push    EAX
>          mov EAX,offset FLAT:_D4test3Foo7__ClassZ
>          push    EAX
>          call    near ptr __d_newclass
>          add ESP,4
>          pop ECX
>          ret
> */
>
>
> Is a pair of similar staticDownCast(), staticUpCast() fit for Phobos?

I think so. Definitely need staticDownCast very often for all functionality in type-hierarchy-generic code where some arguments are known to be of a given subtype.
Typically, some func produces a collection of a given supertype (say, Node). It may be stored on a member, or produced on need. Some other func takes such a collection as input; but using this func means we know all or some of the objects are of a given subtype (say AssignmentNode); and indeed, we need the additional members of the subtype.
Other case, each Node holds one or more other nodes. In the general case, they can be of any subtype. But when a func takes an AssignmentNode, then it knows its subnodes must be NameNode and ExpressionNode, hey!, ain't it clever? And indeed it'll need to access members specific to their subtypes.

But I have never needed upcast in D as of now. What are common use cases?

Denis
-- 
_________________
vita es estrany
spir.wikidot.com

February 10, 2011
On Thu, 10 Feb 2011 11:38:40 -0500, spir <denis.spir@gmail.com> wrote:

> On 02/10/2011 01:38 PM, bearophile wrote:

>>
>>
>> Is a pair of similar staticDownCast(), staticUpCast() fit for Phobos?
>
> But I have never needed upcast in D as of now. What are common use cases?

Aren't all upcasts static anyways?

-Steve