www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - Re: safer casts - take II

reply terranium <spam here.lot> writes:
Janice Caron Wrote:

 Could you be a bit more specific?

http://www.digitalmars.com/webnews/newsgroups.php?art_group=digitalmars.D&article_id=71111 my proposal would be to make cast safer 1) throw InvalidCastException on invalid cast 2) keep const const_cast, dynamic_cast, static_cast, reinterpret_cast templates can be implemented using this safe cast, they can be inefficient and ugly - it's normal, because they normally should not be used. This will break existing code a little 1) if(auto x=cast(X)y) will probably throw in a predicted location, workaround - when porting, replace cast with dynamic_cast template (which should be provided by standard library for porting legacy code) 2) won't compile if mutability is used - easy to fix.
May 13 2008
next sibling parent reply BCS <ao pathlink.com> writes:
Reply to terranium,


 1) if(auto x=cast(X)y) will probably throw in a predicted location,
 workaround - when porting, replace cast with dynamic_cast template
 (which should be provided by standard library for porting legacy code)

The proposed dynamic_cast template, can't be implemented safely and efficiently with the primitives you propose.* The primitives you propose can be safely and efficiently implemented with the ones Yigal proposed. *You either get a redundant downcast check, invoke the exception handling system or you need to use reinterpret_cast.
May 13 2008
parent reply terranium <spam here.lot> writes:
BCS Wrote:

 The proposed dynamic_cast template, can't be implemented safely and
efficiently 
 with the primitives you propose.*
 *You either get a redundant downcast check, invoke the exception handling 
 system or you need to use reinterpret_cast.

this should be deemed as sort of legacy functionality, so it need not to be efficient.
May 13 2008
parent reply BCS <ao pathlink.com> writes:
Reply to terranium,

 BCS Wrote:
 
 The proposed dynamic_cast template, can't be implemented safely and
 efficiently
 with the primitives you propose.*
 *You either get a redundant downcast check, invoke the exception
 handling
 system or you need to use reinterpret_cast.

this should be deemed as sort of legacy functionality, so it need not to be efficient.

As far as I'm concerned it's still an open question if the dynamic_cast or force_cast (by whatever name) will be used more often. So I don't see how you can call it legacy.
May 13 2008
parent reply terranium <spam here.lot> writes:
BCS Wrote:

 As far as I'm concerned it's still an open question if the dynamic_cast or 
 force_cast (by whatever name) will be used more often. So I don't see how 
 you can call it legacy.

Legaciness has nothing to do with frequency of use. I believe, legacy code is used more often, in fact, you're right here. Windows ANSI API is a good example. Total commander... :) Windows itself has workarounds for bugs in MS Office...
May 13 2008
parent reply BCS <ao pathlink.com> writes:
Reply to terranium,

 BCS Wrote:
 
 As far as I'm concerned it's still an open question if the
 dynamic_cast or force_cast (by whatever name) will be used more
 often. So I don't see how you can call it legacy.
 

code is used more often, in fact, you're right here. Windows ANSI API is a good example. Total commander... :) Windows itself has workarounds for bugs in MS Office...

So you are saying that the non-throwing cast should be treated as "the way we did it before we knew better"? If you want to go that way, in order to convince me, you will need to demonstrate a better way. So far no one has.
May 13 2008
parent reply terranium <spam here.lot> writes:
BCS Wrote:

 you will need to demonstrate a better way. So far no one has.

Your argument is big need for if(auto y=cast(Y)x) ? http://www.digitalmars.com/webnews/newsgroups.php?art_group=digitalmars.D&article_id=71729
May 13 2008
next sibling parent reply BCS <ao pathlink.com> writes:
Reply to terranium,

 BCS Wrote:
 
 you will need to demonstrate a better way. So far no one has.
 

http://www.digitalmars.com/webnews/newsgroups.php?art_group=digitalmar s.D&article_id=71729

Yes, I see a bigger need for that than for the throwing kind. I guess if you can convince me that I am in the minority here you might have something, but just saying you never use them won't do it.
May 13 2008
parent terranium <spam here.lot> writes:
BCS Wrote:

 I guess if you can convince me that I am in the minority here

May 14 2008
prev sibling next sibling parent reply Bill Baxter <dnewsgroup billbaxter.com> writes:
terranium wrote:
 BCS Wrote:
 
 you will need to demonstrate a better way. So far no one has.

Your argument is big need for if(auto y=cast(Y)x) ? http://www.digitalmars.com/webnews/newsgroups.php?art_group=digitalmars.D&article_id=71729

I agree with BCS. There's nothing wrong with the null-returning dynamic cast. It's exactly what dynamic_cast does in C++. Dynamic casts are particularly useful when working with interfaces in D, I think. You've got a big pool of objects and you get one and want to ask "Hey, object, do you implement the IHasCheezburger interface?" so you just cast to IHasCheezburger and do something if it does, something else if it doesn't. There is nothing "exceptional" happening there, so it doesn't make sense to throw an exception. I don't really understand what you were getting at with your hashtable example in your other post. But it sounds like you're saying we should re-implement a subset of the down-casting functionality that is already present in D objects instead of using the built-in dynamic cast capability. Why should we do that if D already manages it for us? --bb
May 13 2008
parent Bruno Medeiros <brunodomedeiros+spam com.gmail> writes:
Bill Baxter wrote:
 ask "Hey, object, do you implement the IHasCheezburger interface?" so 

ROFL, best interface name evar :P (it's funny *and* follows my preferred name conventions) -- Bruno Medeiros - Software Developer, MSc. in CS/E graduate http://www.prowiki.org/wiki4d/wiki.cgi?BrunoMedeiros#D
Jun 10 2008
prev sibling parent "Janice Caron" <caron800 googlemail.com> writes:
On 14/05/2008, terranium <spam here.lot> wrote:
 BCS Wrote:

  > I guess if you can convince me that I am in the minority here

 In fact, I am the only who argued for throwing cast, it seems like everyone
else is against my proposal, so the result is obvious.

I don't understand. In the next Phobos release, you will have your wish. to!(T) will work as a throwing dynamic cast. It looks to me like you've actually got what you wanted. What's the problem? It would be very, very impractical to make cast(T) itself throw, because it would break a lot of a code. Obviously, my/Yigal's probosal will itself break lots of code, but in our case, the fix of adding an exclamation mark to the offending line will make it compile again, almost all of the time. But if cast(T) were to suddenly change its behavior to start throwing, there would be no compile errors for using it - just lots of runtime exceptions generated by existing code. And even once the cause is discovered, it's not clear how existing code would have to be rewritten in order to carry on working. The way I see it, the fact that you've got to!(T) coming means that you can write your code in the way that you want it, but also so can everyone else. What's wrong with that?
May 15 2008
prev sibling next sibling parent reply Yigal Chripun <yigal100 gmail.com> writes:
terranium wrote:
 Janice Caron Wrote:
 
 Could you be a bit more specific?

http://www.digitalmars.com/webnews/newsgroups.php?art_group=digitalmars.D&article_id=71111 my proposal would be to make cast safer 1) throw InvalidCastException on invalid cast 2) keep const const_cast, dynamic_cast, static_cast, reinterpret_cast templates can be implemented using this safe cast, they can be inefficient and ugly - it's normal, because they normally should not be used. This will break existing code a little 1) if(auto x=cast(X)y) will probably throw in a predicted location, workaround - when porting, replace cast with dynamic_cast template (which should be provided by standard library for porting legacy code) 2) won't compile if mutability is used - easy to fix.

You want the RTTI cast to throw on error. I'm personally a bit torn here since on the one hand I do see your point and it is more "clean" for some examples. on the other hand it adds a performance penalty the others do not want. Casts shouldn't be normally used and if you do use them then you should pay the performance price, right? this current proposal suits everyone in that you can simply define a template my_cast that will call the cast(T) operator and on null will throw an exception. maybe a helper template like this could be added to the proposal as well since we already added the reinterpret_cast in a library form too. so D will have two basic language level constructs and two additional library helper templates: built into the language: cast(T) cast!(T) import std.cast for this: [the name is just an example, of course] reinterpret_cast!(T) strong_cast!(T) // like cast(T) only throws on error I don't add this to the "official" proposal yet, this is still just some thoughts not finalized into anything formal. feel free to discuss this and propose names for the strong_cast!(T) template.
May 13 2008
next sibling parent reply BCS <ao pathlink.com> writes:
Reply to Yigal,

 I don't add this to the "official" proposal yet, this is still just
 some
 thoughts not finalized into anything formal.
 feel free to discuss this and propose names for the strong_cast!(T)
 template.

force_cast!(T) and I'd put them in object.d
May 13 2008
parent reply Dee Girl <deegirl noreply.com> writes:
BCS Wrote:

 Reply to Yigal,
 
 I don't add this to the "official" proposal yet, this is still just
 some
 thoughts not finalized into anything formal.
 feel free to discuss this and propose names for the strong_cast!(T)
 template.

force_cast!(T) and I'd put them in object.d

What is object.d please? Only now I understood something. It was in a early post. There is enforce already in std.contracts. Then enforce(cast(T) x) works. It is longer than force_cast!(T) x. But I prefer it because it respects composition principle. If I know what cast does and what enforce does then I know what enforce(cast(T) x) does. Thank you, Dee Girl
May 13 2008
parent BCS <ao pathlink.com> writes:
Reply to Dee,

 BCS Wrote:
 
 Reply to Yigal,
 
 I don't add this to the "official" proposal yet, this is still just
 some
 thoughts not finalized into anything formal.
 feel free to discuss this and propose names for the strong_cast!(T)
 template.

and I'd put them in object.d


all the stuff that you can't live without like the definition of Object, TypeInfo 'n friends, the Error and Exception class and whatnot.
May 13 2008
prev sibling parent terranium <spam here.lot> writes:
Yigal Chripun Wrote:

 You want the RTTI cast to throw on error.
 it adds a performance penalty

I don't think so. How does it add a performance penalty?
 this current proposal suits everyone in that you can simply define a
 template my_cast that will call the cast(T) operator and on null will
 throw an exception.

There is no need for proposals to implement such a template. My reason is to force people to use safe cast, if you just write safe cast with template function, ugly syntax, ugly name, performance penalty (additional check for null) - no one will use it, even I.
May 13 2008
prev sibling next sibling parent reply Dee Girl <deegirl noreply.com> writes:
terranium Wrote:

 Janice Caron Wrote:
 
 Could you be a bit more specific?

http://www.digitalmars.com/webnews/newsgroups.php?art_group=digitalmars.D&article_id=71111 my proposal would be to make cast safer 1) throw InvalidCastException on invalid cast 2) keep const const_cast, dynamic_cast, static_cast, reinterpret_cast templates can be implemented using this safe cast, they can be inefficient and ugly - it's normal, because they normally should not be used. This will break existing code a little 1) if(auto x=cast(X)y) will probably throw in a predicted location, workaround - when porting, replace cast with dynamic_cast template (which should be provided by standard library for porting legacy code) 2) won't compile if mutability is used - easy to fix.

Maybe the way it should work is this. The language provides primitives. Library constructs from primitives. But cast that throws is not good primitive. Because you need expensive operation to implement cast that returns null on failure. A better primitive is cast that returns null. You can implement cast that throws in a library. One more opinion I have. The pattern in Java with if (a isa Type) { Type t = (Type) a; ... } is very clunky. It is not inefficient. Because Java compilers all have special case for the sequence (peephole optimization). But it is verbose programming style with duplication. If you use cast that throws in D then you are forced in that pattern. Another reason cast that throws is not good. Thank you, Dee Girl
May 13 2008
parent reply terranium <spam here.lot> writes:
Dee Girl Wrote:

 But cast that throws is not good primitive. Because you need expensive
operation to implement cast that returns null on failure.

There is only minor need for this silent cast, I believe, so 1) since this is rarely needed, performance penalty is acceptable, 2) if you don't want performance penalty, avoid need for silent cast.
 If you use cast that throws in D then you are forced in that pattern.

The unwanted one (and forced to) should be unsafe functionality, rather than safe. The type switch from your example can be implemented in a more efficient way with a type hashtable.
May 13 2008
next sibling parent reply BCS <ao pathlink.com> writes:
Reply to terranium,

 There is only minor need for this silent cast, I believe, so 1) since
 this is rarely needed, performance penalty is acceptable,

by that augment, "there is only minor need for /down/ casts, I believe, since" the throwing kind is even rarer than the non throwing kind. In my experience that is the case. (note: paraphrased to draw attention to the parallel nature of the counter argument)
 The type switch from your example can be implemented
 in a more efficient way with a type hashtable.

I'm not seeing it, example please?
May 13 2008
parent reply terranium <spam here.lot> writes:
BCS Wrote:

 I'm not seeing it, example please?

void foo(T arg) { auto handle=myhashtable[arg.classinfo]; //handle can be data or delegate for increased functionality }
May 13 2008
next sibling parent reply BCS <ao pathlink.com> writes:
Reply to terranium,

 BCS Wrote:
 
 I'm not seeing it, example please?
 

{ auto handle=myhashtable[arg.classinfo]; //handle can be data or delegate for increased functionality }

Where would the table be set up? for delegates to work you would need to set up the table every time the function is called (to get the context pointer right). The next best thing would be something like this: switch(myhashtable[arg.classinfo]) { case ClassEnum: ... } but then we might as well let switch take a classinfo struct and avoid the enum. But that looks just plain ugly.
May 13 2008
parent reply terranium <spam here.lot> writes:
BCS Wrote:

 Where would the table be set up?

 for delegates to work

May 13 2008
next sibling parent BCS <ao pathlink.com> writes:
Reply to terranium,

 BCS Wrote:
 
 for delegates to work
 


delegates can't be static functions (function pointers can) and member calls wouldn't give me access to local variables. even if all that is addressed, the best implementation of your proposal is a lot more hackish than if(auto a = cast(A)b) ...
May 13 2008
prev sibling parent terranium <spam here.lot> writes:
Janice Caron Wrote:

 Hopefully everyone's happy now.
 

May 14 2008
prev sibling next sibling parent reply terranium <spam here.lot> writes:
terranium Wrote:

 BCS Wrote:
 
 I'm not seeing it, example please?

void foo(T arg) { auto handle=myhashtable[arg.classinfo]; //handle can be data or delegate for increased functionality }

I thought it's a known use case (in C# it's used widely and handles value types too).
May 13 2008
parent BCS <ao pathlink.com> writes:
Reply to terranium,


 I thought it's a known use case (in C# it's used widely and handles
 value types too).
 

I've never seen it done in D.
May 13 2008
prev sibling parent reply "Janice Caron" <caron800 googlemail.com> writes:
The case for dynamic cast is closed. I just heard from the powers that
be that std.conv.to will support an exception-throwing dynamic cast in
the next Phobos. So it's

    dst = cast(T)src;  // non-throwing dynamic cast
    dst = to!(T)(src);  // throwing dynamic cast

Hopefully everyone's happy now.

Does anyone have a problem with the proposal /apart/ from dynamic cast failure?
May 13 2008
parent BCS <ao pathlink.com> writes:
Reply to Janice,


 Does anyone have a problem with the proposal /apart/ from dynamic cast
 failure?

no
May 13 2008
prev sibling parent reply Dee Girl <deegirl noreply.com> writes:
terranium Wrote:

 Dee Girl Wrote:
 
 But cast that throws is not good primitive. Because you need expensive
operation to implement cast that returns null on failure.

There is only minor need for this silent cast, I believe, so 1) since this is rarely needed, performance penalty is acceptable, 2) if you don't want performance penalty, avoid need for silent cast.

In my code I use often capability test. So cast that returns null is good for me. If I want to make sure I check on my side. In those cases I assign it to a variable that lives longer. So there are few casts that should throw in my code. If you want to make general statement about frequency of cast at best you collect data from programs.
 If you use cast that throws in D then you are forced in that pattern.

The unwanted one (and forced to) should be unsafe functionality, rather than safe. The type switch from your example can be implemented in a more efficient way with a type hashtable.

This is incomplete answer. In my compiler implementation for switch if-else test is fast for 4 elements or less. Then linear search is fast up to 32 elements. Only then hash is best. But many variables influence this. Dee Girl
May 13 2008
parent terranium <spam here.lot> writes:
Dee Girl Wrote:

 In my code I use often capability test.

I see no problem, if you want it, use it.
 Then linear search is fast up to 32 elements. Only then hash is best.

don't forget, with if-else chain you do type check on every element, typecheck is implemented as looping through vtable. With type hashtable you process typeinfo only once, then you search for its hash.
May 14 2008
prev sibling parent "Janice Caron" <caron800 googlemail.com> writes:
On 13/05/2008, terranium <spam here.lot> wrote:
  1) throw InvalidCastException on invalid cast

We already have that. import std.conv; dst = to!(T)(src); I quote: <quote> The to family of functions converts a value from type Source to type Target. The source type is deduced and the target type must be specified, for example the expression to!(int)(42.0) converts the number 42 from double to int. The conversion is "safe", i.e., it checks for overflow; to!(int)(4.2e10) would throw the ConvOverflowError exception. Overflow checks are only inserted when necessary, e.g., to!(double)(42) does not do any checking because any int fits in a double. </quote> If std.conv.to doesn't do dynamic casting, it can easily be made to, so your base is already covered.
May 13 2008