www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - rtti cast

reply Simon Buerger <krox gmx.net> writes:
hi
I would like to do some RTTI, but not with type-IDs, but in some more 
pretty way. I'll simply post some code to show you what I mean :) . My 
Question is now: Does anyone know, how to do sth alike with the 
current D implementation (maybe with templates, but I dont see a nice 
way) ? Or do you think it could be worth an extension for D 2.0 ?
Krox

Code:

class Base {}
class A : Base
{
	void function_A() { ... }
}
class B : Base {}
{
	void function_B() { ... }
}

void main()
{
	Base x = ...
	
	// checks if x is of type A, and if so, declares y as A alias of x 
with other type
	if (x == A y)	
		y.function_A();
	else if(x == B y)
		y.function_B();
}
May 01 2008
next sibling parent reply BCS <BCS pathlink.com> writes:
Simon Buerger wrote:
 hi
 I would like to do some RTTI, but not with type-IDs, but in some more 
 pretty way. I'll simply post some code to show you what I mean :) . My 
 Question is now: Does anyone know, how to do sth alike with the current 
 D implementation (maybe with templates, but I dont see a nice way) ? Or 
 do you think it could be worth an extension for D 2.0 ?
 Krox
 
 Code:
 
 class Base {}
 class A : Base
 {
     void function_A() { ... }
 }
 class B : Base {}
 {
     void function_B() { ... }
 }
 
 void main()
 {
     Base x = ...
     
     // checks if x is of type A, and if so, declares y as A alias of x 
 with other type
     if (x == A y)   
         y.function_A();
     else if(x == B y)
         y.function_B();
 }
this work? if (auto y = cast(A)x) y.function_A(); else if(auto y = cast(B)x) y.function_B();
May 01 2008
parent reply Simon Buerger <krox gmx.net> writes:
BCS wrote:
 this work?
 
 if (auto y = cast(A)x)
     y.function_A();
 else if(auto y = cast(B)x)
     y.function_B();
 
Well, honestly I'm really impressed. Thats perfectly what I wanted. Seems to be standard, but I didn't knew about it *lolz*. Okay, seems to be solved then. Thanks.
May 01 2008
parent reply terranium <spam here.lot> writes:
Simon Buerger Wrote:

 BCS wrote:
 this work?
 
 if (auto y = cast(A)x)
     y.function_A();
 else if(auto y = cast(B)x)
     y.function_B();
 
Well, honestly I'm really impressed. Thats perfectly what I wanted. Seems to be standard, but I didn't knew about it *lolz*. Okay, seems to be solved then. Thanks.
OMG invalid cast doesn't throw exception????
May 02 2008
parent reply Pragma <eric.t.anderton gmail.com> writes:
terranium Wrote:

 Simon Buerger Wrote:
 
 BCS wrote:
 this work?
 
 if (auto y = cast(A)x)
     y.function_A();
 else if(auto y = cast(B)x)
     y.function_B();
 
Well, honestly I'm really impressed. Thats perfectly what I wanted. Seems to be standard, but I didn't knew about it *lolz*. Okay, seems to be solved then. Thanks.
OMG invalid cast doesn't throw exception????
Nope. Invalid casts simply return null. If you absolutely need an exception, then you can just wrap the cast in a templated function: class DynamicCastException : Exception{ public this(char[] reason){ super(reason); } } T dyn_cast(T)(Object x){ T result = cast(T)x; if(result is null) throw new DynamicCastException("Cannot cast to type " ~ T.stringof); return result; } class A{} class B:A{} class C{} void main(){ B x = new B(); C y = dyn_cast!(C)(x); }
May 02 2008
parent reply terranium <spam here.lot> writes:
Pragma Wrote:

 If you absolutely need an exception
And who don't? Yet another D misdesign to be worked around? And where will be more legacy design typos in the end? In C++ or in D?
May 02 2008
next sibling parent reply BCS <BCS pathlink.com> writes:
terranium wrote:
 Pragma Wrote:
 
 
If you absolutely need an exception
And who don't? Yet another D misdesign to be worked around? And where will be more legacy design typos in the end? In C++ or in D?
In most cases forgetting to check the cast return will result in an seg-v from a dereferenced null because you almost always use it as the very next thing. Also it will never result in a miss use for the same reason, the pointer doesn't point to anything. Having the "bad cast" -> null is very handy when you don't known if the cast will be good. It mean you can test it without invoking the exception system and all the overhead that entails.
May 02 2008
parent reply terranium <spam here.lot> writes:
BCS Wrote:

 In most cases forgetting to check the cast return will result in an 
 seg-v from a dereferenced null because you almost always use it as the 
In most cases... almost always... When an invalid cast occurs, the error is not in null pointer it's in invalid cast, and replacing InvalidCastException with a hope for NullPointerException is a wrong way.
 Having the "bad cast" -> null is very handy when you don't known if the 
 cast will be good. It mean you can test it without invoking the 
 exception system and all the overhead that entails.
maybe in this case we need some syntax sugar, not a silent cast?
May 07 2008
parent BCS <BCS pathlink.com> writes:
terranium wrote:
 BCS Wrote:
 
 
In most cases forgetting to check the cast return will result in an 
seg-v from a dereferenced null because you almost always use it as the 
In most cases... almost always... When an invalid cast occurs,
it is not an error. Thinking about it, I can't recall ever casting to a derived type where a failed cast was an indication of a bug. In the few times I have done it, it has always been in the context of "if a is'a B then" (and not just in the code but in the proper function of the program as well).
Having the "bad cast" -> null is very handy when you don't known if the 
cast will be good. It mean you can test it without invoking the 
exception system and all the overhead that entails.
maybe in this case we need some syntax sugar, not a silent cast?
a little sugar for the other way might be even handier // not tested template Force_Cast(T) { T Force_Cast(U)(U u) { static assert(is(U : T), "cant cast from " ~ U.stringof ~ " to " ~ T.stringof); if(auto t = cast(T)u) return t; else throw new InvalidCastException("failed cast from " ~ U.stringof ~ " to " ~ T.stringof); } }
May 07 2008
prev sibling next sibling parent reply "Jarrett Billingsley" <kb3ctd2 yahoo.com> writes:
"terranium" <spam here.lot> wrote in message 
news:fvf93j$194u$1 digitalmars.com...
 Pragma Wrote:

 If you absolutely need an exception
And who don't?
I don't. I perform downcasts extremely rarely. When I do, it's usually in code like: if(auto y = cast(Y)x) ... else // not a Y, try something else That is, the code is only executed if the cast succeeds. Furthermore, it's a lot easier and more efficient to have the cast return null and throw an exception _only if needed_ than to throw an exception and then have to catch it: try { auto y = cast(Y)x; ... } catch(CastException e) { // not a Y, try something else.. } that throws an exception and one that doesn't. Either can really be implemented in the other, but the null-returning kind is more basic and efficient.
May 02 2008
parent Leandro Lucarella <llucax gmail.com> writes:
Jarrett Billingsley, el  2 de mayo a las 16:40 me escribiste:

 that throws an exception and one that doesn't.  Either can really be 
 implemented in the other, but the null-returning kind is more basic and 
 efficient. 
C++ have the 2 too. If you cast a pointer, it returns NULL if the cast fails but if you cast a reference, the cast throws a bad_cast exception if it fails. Unfortunatelly this can't be done in D (at least in an elegant way) because it's reference semantics for objects. But I thinks std.contracts.enforce[1] is a perfect solution for this: auto y = enforce(cast(D)x); // y is not null from here on Or just use assert if you want the check to be gone in release builds: auto y = cast(D)x; assert(y !is null); [1] http://www.digitalmars.com/d/2.0/phobos/std_contracts.html -- Leandro Lucarella (luca) | Blog colectivo: http://www.mazziblog.com.ar/blog/ ---------------------------------------------------------------------------- GPG Key: 5F5A8D05 (F8CD F9A7 BF00 5431 4145 104C 949E BFB6 5F5A 8D05) ---------------------------------------------------------------------------- The number of wars fought between countries That both have at least one McDonalds is zero
May 05 2008
prev sibling next sibling parent Robert Fraser <fraserofthenight gmail.com> writes:
terranium wrote:
 Pragma Wrote:
 
 If you absolutely need an exception
And who don't? Yet another D misdesign to be worked around? And where will be more legacy design typos in the end? In C++ or in D?
In Java, I often find myself writing code like: void process(Object o) { if(o instanceof String) { String s = (String) o; // ... } else if(o instanceof List) { List<String> l = (List<String>) o; // ... } } It's much easier (and more efficient) to use the D way: void process(Object o) { if(auto s = cast(string) o) { //... } else if(auto l = cast(Seq!(string) o) { // ... } }
May 02 2008
prev sibling parent downs <default_357-line yahoo.de> writes:
terranium wrote:
 Pragma Wrote:
 
 If you absolutely need an exception
And who don't? Yet another D misdesign to be worked around? And where will be more legacy design typos in the end? In C++ or in D?
Actually, since you don't know if the cast is really valid, it failing is a regular outcome. As such, an exception (which is reserved, appropriately, for *exceptional* events), is totally the wrong approach to use. Note that with the current behavior, you can incur the runtime speed loss of throwing an exception if you _want_ to, by using the above templated wrapper, but if throwing an exception were the only possibility, you couldn't possibly get that speed back. So it really is better this way. :) --downs
May 03 2008
prev sibling next sibling parent reply terranium <spam here.lot> writes:
Jarrett Billingsley Wrote:

 I perform downcasts extremely rarely.
That's *exactly* why you need safe cast.
 
 if(auto y = cast(Y)x)
     ...
 else
     // not a Y, try something else
yeah, try something else, hide the bug :)
 Furthermore, it's a lot easier and more efficient to have the cast return 
 null and throw an exception _only if needed_ than to throw an exception and 
 then have to catch it:
most programmers won't bother to thow an exception in this case, they just cast and go.

 that throws an exception and one that doesn't.  Either can really be 
 implemented in the other, but the null-returning kind is more basic and 
 efficient. 
...efficient bugmaker.
May 07 2008
next sibling parent reply BCS <BCS pathlink.com> writes:
terranium wrote:
 Jarrett Billingsley Wrote:
 
 
I perform downcasts extremely rarely.
That's *exactly* why you need safe cast.
How does that follow? I don't see the connection.
 
if(auto y = cast(Y)x)
    ...
else
    // not a Y, try something else
yeah, try something else, hide the bug :)
That's not a bug. That is the normal idiom for checking to see if x is a Y and acting on it.
 
Furthermore, it's a lot easier and more efficient to have the cast return 
null and throw an exception _only if needed_ than to throw an exception and 
then have to catch it:
most programmers won't bother to thow an exception in this case, they just cast and go.
And this will result in a Seg-V. The program will stop in a predictable manner. IMHO (and it seems others opinions) that is what matters.
 

that throws an exception and one that doesn't.  Either can really be 
implemented in the other, but the null-returning kind is more basic and 
efficient. 
...efficient bugmaker.
the same can be said going the other way. IMHO any code that take a
May 07 2008
parent BCS <BCS pathlink.com> writes:
 the same can be said going the other way. 
dang non-linear editing (scratch the following):
 IMHO any code that take a
May 07 2008
prev sibling parent reply "Jarrett Billingsley" <kb3ctd2 yahoo.com> writes:
"terranium" <spam here.lot> wrote in message 
news:fvs67v$1gge$1 digitalmars.com...

 That's *exactly* why you need safe cast.
 ...
 yeah, try something else, hide the bug :)
 ...
 ...efficient bugmaker.
I _really_ don't understand where you're coming from here. I'd be interested to see some example of code where an invalid downcast were a bug and where a failed cast _should_ throw an exception. I've only ever used downcasts in D as a replacement for Java's "instanceof", where it's just a simple typecheck.
May 07 2008
parent reply terranium <spam here.lot> writes:
Jarrett Billingsley Wrote:

 yeah, try something else, hide the bug :)
 ...
 ...efficient bugmaker.
I _really_ don't understand where you're coming from here. I'd be interested to see some example of code where an invalid downcast were a bug and where a failed cast _should_ throw an exception. I've only ever used downcasts in D as a replacement for Java's "instanceof", where it's just a simple typecheck.
void GetNodes() { auto obj = Parser.GetNextObject(); MyList.Add(cast(INode)obj); }
May 08 2008
parent BCS <ao pathlink.com> writes:
Reply to terranium,

 Jarrett Billingsley Wrote:
 
 yeah, try something else, hide the bug :)
 ...
 ...efficient bugmaker.
I _really_ don't understand where you're coming from here. I'd be interested to see some example of code where an invalid downcast were a bug and where a failed cast _should_ throw an exception. I've only ever used downcasts in D as a replacement for Java's "instanceof", where it's just a simple typecheck.
void GetNodes() { auto obj = Parser.GetNextObject(); MyList.Add(cast(INode)obj); }
Dang, you just reminded me that I do have some code that downcasts and expects stuff to work (it check the cast though). It's in a generated parser I'm working on.
May 08 2008
prev sibling next sibling parent terranium <spam here.lot> writes:
Robert Fraser Wrote:

 In Java, I often find myself writing code like:
 
In any language I often find myself thinking about program's desing.
May 07 2008
prev sibling next sibling parent reply terranium <spam here.lot> writes:
downs Wrote:

 As such, an exception (which is reserved, appropriately, for *exceptional*
events), is totally the wrong approach to use.
 
Yes, function received wrong input, this is normal, if we want, we throw exception, if we want, we ignore it.
 Note that with the current behavior, you can incur the runtime speed loss of
throwing an exception if you _want_ to, by using the above templated wrapper,
but if throwing an exception were the only possibility, you couldn't possibly
get that speed back.
 
You'll get that speed back without hours spent in debugger trying to figure out, "where this null came from"?
May 07 2008
parent BCS <BCS pathlink.com> writes:
terranium wrote:
 downs Wrote:
 
 
As such, an exception (which is reserved, appropriately, for *exceptional*
events), is totally the wrong approach to use.
Yes, function received wrong input, this is normal, if we want, we throw exception, if we want, we ignore it.
The point is that offtent a failed cast is not an indecation of an error. For instance code like this class A {} class B1 : A {} class B2 : A {} void foo(A a) { if(auto b = cast(B1)a) {} else if(auto b = cast(B2)a) {} else {} }
May 07 2008
prev sibling next sibling parent reply terranium <spam here.lot> writes:
BCS Wrote:

 Thinking about it, I can't recall ever casting to a derived type where a 
 failed cast was an indication of a bug.
You can't, others can. When a function receives unexpected input it's a bug.
 a little sugar for the other way might be even handier
making default behavior unsafe is a wrong way.
May 08 2008
parent BCS <ao pathlink.com> writes:
Reply to terranium,

 BCS Wrote:
 
 Thinking about it, I can't recall ever casting to a derived type
 where a failed cast was an indication of a bug.
 
You can't, others can.
Ok, some some people use then one way, I use them another. That doesn't prove that they your proposal is better (or worse) than the current way.
 When a function receives unexpected input it's
 a bug.
 
That's irrelevant. What I'm saying is that in every case I know of where I have used such a cast, the cast failing was /not/ unexpected. It was mealy a legitimate and /expected/ "no" answer to the question "is a's type B?"
May 08 2008
prev sibling next sibling parent reply terranium <spam here.lot> writes:
BCS Wrote:

I perform downcasts extremely rarely.
That's *exactly* why you need safe cast.
How does that follow? I don't see the connection.
When you do some unusual thing, the probability of mistake increases.
 And this will result in a Seg-V. The program will stop in a predictable 
 manner. IMHO (and it seems others opinions) that is what matters.
The program will not stop in a predictable manner since a third party library can silently handle nulls, e.g. XML serialization. And if it stops in an AV this happens in an unpredictable location - miles away from the place where the bug was triggered. What matters is a presence of bugs, to solve this you must eliminate them, to eliminate them you should know, where the bug was triggered (and of course you need to know that there is a bug), to help locating bug an exception should be thrown as soon as possible after the bug was triggered, ideally exception should be thrown in the same location, where the bug was triggered.
May 08 2008
parent "Simen Kjaeraas" <simen.kjaras gmail.com> writes:
terranium <spam here.lot> wrote:
 BCS Wrote:
 And this will result in a Seg-V. The program will stop in a predictable
 manner. IMHO (and it seems others opinions) that is what matters.
The program will not stop in a predictable manner since a third party library can silently handle nulls, e.g. XML serialization.
So, don't send any unsanitized data to a third-party library? A third-party library might also catch and ignore exceptions, if the language automatically threw one when seeing an invalid cast. -- Simen
May 08 2008
prev sibling next sibling parent reply terranium <spam here.lot> writes:
Jarrett Billingsley Wrote:

 I've only ever used 
 downcasts in D as a replacement for Java's "instanceof", where it's just a 
 simple typecheck. 
cast is not a typecheck, it's a type conversion.
May 08 2008
parent reply "Janice Caron" <caron800 googlemail.com> writes:
2008/5/8 terranium <spam here.lot>:
  cast is not a typecheck, it's a type conversion.
In C++, there is a difference between a regular cast and a dynamic_cast. The expression dynamic_cast<T>(x) performs a run-time type check and type conversion. See http://publib.boulder.ibm.com/infocenter/macxhelp/v6v81/topic/com.ibm.vacpp6m.doc/language/ref/clrc05keyword_dynamic_cast.htm dynamic_cast<T>() returns a null pointer of type T if the cast fails. Users of dynamic_cast<T>() are therefore required always to check the return value for null. This is not an indication of a bug - it is designed behavior. Thus, in C++ (1) traditional cast is not a typecheck, it's a type conversion (2) dynamic cast is a a runtime type check Unfortunately, D's cast(T)x behaves exactly like C++'s dynamic_cast<T>(x) when it comes to casting up and down a class heirarchy. D only has one kind of cast, and it does both jobs. So Jarrett is using D's casts correctly, /but/ D's casts are themselves flawed. The "one cast that does everything" ideology is just a tool for making bugs.
May 08 2008
parent terranium <spam here.lot> writes:
Janice Caron Wrote:

 (1) traditional cast is not a typecheck, it's a type conversion
 (2) dynamic cast is a a runtime type check
 
Typecheck just determines whether an object can be cast to the target type, so does a type conversion and applies a typecheck during the conversion and the result depends on whether the typecheck succeeded. It was noted that for failed typecheck for pointer type null is returned and for failed typecheck for reference type bad_cast is thrown. And D reference types are very similar to those of C++ (at least in syntax :) ) except that C++ reference can't be changed after initialization.
May 08 2008
prev sibling parent reply terranium <spam here.lot> writes:
BCS Wrote:

 The point is that offtent a failed cast is not an indecation of an 
 error. For instance code like this
 
 class A {}
 class B1 : A {}
 class B2 : A {}
 
 void foo(A a)
 {
    if(auto b = cast(B1)a) {}
    else if(auto b = cast(B2)a) {}
    else {}
 }
I see polimorphism should help here.
May 08 2008
parent BCS <ao pathlink.com> writes:
Reply to terranium,

 BCS Wrote:
 
 The point is that offtent a failed cast is not an indecation of an
 error. For instance code like this
 
 class A {}
 class B1 : A {}
 class B2 : A {}
 void foo(A a)
 {
 if(auto b = cast(B1)a) {}
 else if(auto b = cast(B2)a) {}
 else {}
 }
I see polimorphism should help here.
dosn't always work module third.party.lib; class A {} module my.lib.one class B1 : A { int newFn(){...}} module my.lib.teo class B2 : A { char somthingNew(char[] c){...}} void foo(A a) { if(auto b = cast(B1)a) b.newFn(); else if(auto b = cast(B2)a) b.somthingNew("hello"); else throw new Error("a isn't a B1 or B2"); }
May 08 2008