www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - Dynamic and Static Casting

reply d coder <dlang.coder gmail.com> writes:
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
Feb 10 2011
next sibling parent reply "Lars T. Kyllingstad" <public kyllingen.NOSPAMnet> writes:
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
Feb 10 2011
next sibling parent bearophile <bearophileHUGS lycos.com> writes:
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
Feb 10 2011
prev sibling parent spir <denis.spir gmail.com> writes:
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
Feb 10 2011
prev sibling next sibling parent bearophile <bearophileHUGS lycos.com> writes:
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
Feb 10 2011
prev sibling next sibling parent "Lars T. Kyllingstad" <public kyllingen.NOSPAMnet> writes:
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
Feb 10 2011
prev sibling next sibling parent d coder <dlang.coder gmail.com> writes:
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
Feb 10 2011
prev sibling next sibling parent "Steven Schveighoffer" <schveiguy yahoo.com> writes:
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
Feb 10 2011
prev sibling parent "Steven Schveighoffer" <schveiguy yahoo.com> writes:
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
Feb 10 2011