www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - safer casts - take II

reply Yigal Chripun <yigal100 gmail.com> writes:
since the other thread got completely off-topic and lost any connection
to the original discussion, here's a new clean thread, that will
hopefully summarize the previous discussion and present the latest
proposal so that hopefully more people join the debate without the need
to go through all the previous posts (164 posts!).

Here goes:

the proposal is for a safer cast that will _replace_ the current cast.
this is important both for a consistent syntax and to prevent subverting
the system with the old style cast.

there are there forms of cast:
(a) cast(T)something
(b) cast!(T)something
(c) reinterpret_cast!(T)

there is no need for language support for (c) since this can be
implemented via a union and this is exactly what this library template
does. the only reason for this is to have a standardized name. unless
the user really knows what he's doing this should be avoided, since this
is the most dangerous form of cast.

goals of this design:
1. you can search/grep for all casts in your code.
2. constancy is cast explicitly so no more casting an invariant T to a
mutable U in one cast. this prevents bugs.

(a) will do would you'll expect in the common case. while (b) is for
more "dangerous" casts. it can also be defined in all forms
(implicit/explicit) by the user while (b) isn't.

if T is a class then (a) uses RTTI cast (down casting) or a user defined
conversion, otherwise (a) will do plain conversions like the current
cast does.
(b) is used for constancy casts and the specific use case of casting
pointers to void. (more specific cases could be added in the future)

for c++ people this translates to:
(a)  (T is a class) ? dynamic_cast : static_cast
(b)  const_cast
(c)  reinterpret_cast implemented in library

also note that cast(T) for classes will return null on failure  just
like in current D.

questions?
May 13 2008
next sibling parent reply BCS <ao pathlink.com> writes:
Reply to Yigal,
 
 questions?
 
looks good. Excluding the Const stuff (and I've been ignoring all that for months) it seems consistent and reasonably compact. As for the Const stuff, I'll let others pass jugment.
May 13 2008
parent "Janice Caron" <caron800 googlemail.com> writes:
On 13/05/2008, BCS <ao pathlink.com> wrote:
  looks good. Excluding the Const stuff (and I've been ignoring all that for
 months) it seems consistent and reasonably compact. As for the Const stuff,
 I'll let others pass jugment.
Obviously, since I was partly involved in drafting this, I support it. Basically what this means is that, once the change is made, 99% of your code will compile, and maybe 1% wont. (I'm making these numbers up, by the way, just for illustration). So you look at the lines the don't compile, and ponder "Is this a bug, or is it correct?". If you decide it's correct, you change "cast" to "cast!" and recompile, and all should be well. If it /still/ doesn't compile, it's almost certainly a bug in your code. But not neccesarily, because there's still a chance you /might/ be doing two things at once, in which case, if you're really, really sure it's not a bug, you change your code from "cast!(T)" to "cast!(T)cast(U)", where U is some intermediate type (so the safe cast changes the type but not the constancy, and then the danger cast then changes the constancy). Now it should compile. And if it /still/ doesn't compile, it's either a bug or very badly designed code. In the latter case, you can use reinterpret_cast!(T) to force the line to compile. Simply put - this is a tool for finding bugs at compile time. Without this, those bugs could bring your program crashing down at run time. So yes, I love this idea.
May 13 2008
prev sibling next sibling parent reply terranium <spam here.lot> writes:
Yigal Chripun Wrote:

 also note that cast(T) for classes will return null on failure  just
 like in current D.
I don't think that such redesing is useful.
May 13 2008
parent "Janice Caron" <caron800 googlemail.com> writes:
On 13/05/2008, terranium <spam here.lot> wrote:
 I don't think that such redesing is useful.
Could you be a bit more specific? Personally I think finding bugs is /very/ useful, especially since all that is really being asked of the programmer is an exclamation mark.
May 13 2008
prev sibling next sibling parent reply Paul D. Anderson <paul.d.removethis.anderson comcast.andthis.net> writes:
Yigal Chripun Wrote:

 
 there are there forms of cast:
 (a) cast(T)something
 (b) cast!(T)something
 (c) reinterpret_cast!(T)
 
 questions?
My only concern is the re-use of '!'. My first thought on reading your proposal was that 'cast!(T)' was some sort of template. I'm sure with a little practice I could get to where I immediately recognize it as a dangerous(!) cast, but it's just one more thing to learn. From my cursory examination of keyword usage I've concluded that the keywords that are problematic ('const', 'enum') are the ones that mean different (but related) things in different usages. I'm not at all convinced that parsimony in keywords is a good thing, but that seems to be a minority view (and it isn't Walter's view, as far as I can tell). We're a little more accustomed to re-definition of symbols, especially in a new context, but this isn't new -- it's a variable name followed by a '!'. Having said that, I don't have an alternative notation. Perhaps a long keyword llike 'const_cast', 'invariant_cast', etc., similar to the 'reinterpret_cast' usage. I do think it's a good idea, though to have the separate types of casts called out. Paul
May 13 2008
parent reply Yigal Chripun <yigal100 gmail.com> writes:
Paul D. Anderson wrote:
 Yigal Chripun Wrote:
 
 there are there forms of cast: (a) cast(T)something (b)
 cast!(T)something (c) reinterpret_cast!(T)
 
 questions?
My only concern is the re-use of '!'. My first thought on reading your proposal was that 'cast!(T)' was some sort of template. I'm sure with a little practice I could get to where I immediately recognize it as a dangerous(!) cast, but it's just one more thing to learn. From my cursory examination of keyword usage I've concluded that the keywords that are problematic ('const', 'enum') are the ones that mean different (but related) things in different usages. I'm not at all convinced that parsimony in keywords is a good thing, but that seems to be a minority view (and it isn't Walter's view, as far as I can tell). We're a little more accustomed to re-definition of symbols, especially in a new context, but this isn't new -- it's a variable name followed by a '!'. Having said that, I don't have an alternative notation. Perhaps a long keyword llike 'const_cast', 'invariant_cast', etc., similar to the 'reinterpret_cast' usage. I do think it's a good idea, though to have the separate types of casts called out. Paul
to my defense, I'll note the fact that c++ provides its casts (except of course the legacy C syntax) with a template syntax too, even though they don't have to be implemented in terms of templates. You can ask Walter how it's implemented in DMC. and it has some similarity in meaning as well, in a way it does behave like a template since the output is parametrized on the input type T. even though the cast isn't really a template, i think that both meanings are suitable here. look at it like this: cast(T) has similarities with a function in that it's make runtime checks for an RTTI cast for example, or behaves like a function that converts a value to a different type. the cast!(T) on the other hand is more in the realm of the static world of the compiler. it doesn't do all the checks the cast(T) does and in a way is more low-level. the reinterpret cast is a special case. it's very unsafe and very low-level and I wouldn't even provide it at all in the proposal since the experienced OS writer (as an example) which needs this feature can implement it himself via unions. the only benefit of providing it in the library is to have a standardized name for it. it should be discouraged and accompanied with warnings in the docs. since D is a Systems programming language, it's still possible to use it, only you need to be damn sure you know what you're doing when subverting the type system like that.
May 13 2008
parent Yigal Chripun <yigal100 gmail.com> writes:
I've just thought of an analogy to explain the different cast types:
[take this with a grain of salt, it's not meant to be discussed seriously]

casts are like doom cheats. the ones we used in doom 2 when we were just
kids with a 486 computer. (my first real PC. I had a 386 which broke
down after a few months which was then replaced to this model)

cast(T) gives you a one time bonus amount of ammo. in a very hard level
this can be understandable.
cast!(T) give you 150% health. this is a much more serious cheat which
in multi-player mode would be frowned upon unless everyone apply the
same cheat, of course ;)
reinterpret_cast!(T) is god mode and no one will want to play with you
like that. this is serious cheating you'll only use in single-player mode.

I'm sure I can Google the actual codes for those cheats ;) the must be
documented somewhere...

--Yigal
May 13 2008
prev sibling next sibling parent 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.
 
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...
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.
 
Your argument is big need for if(auto y=cast(Y)x) ? 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 reply terranium <spam here.lot> writes:
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.
May 14 2008
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 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 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.
force_cast!(T) and I'd put them in object.d
What is object.d please?
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?
 
void foo(T arg) { 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?
wherever you want, static constructor is a good choise, maybe static initializer will work too.
 for delegates to work
yes, delegates sould be static functions or member calls
May 13 2008
next sibling parent BCS <ao pathlink.com> writes:
Reply to terranium,

 BCS Wrote:
 
 for delegates to work
 
yes, delegates sould be static functions or member calls
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 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
next sibling 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 terranium <spam here.lot> writes:
Janice Caron Wrote:

 Hopefully everyone's happy now.
 
of course, if you're happy, everyone's happy :)
May 14 2008
prev 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 }
too).
May 13 2008
parent BCS <ao pathlink.com> writes:
Reply to terranium,



 value types too).
 
I've never seen it done in D.
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
prev sibling next sibling parent terranium <spam here.lot> writes:
Yigal Chripun Wrote:

 to my defense, I'll note the fact that c++ provides its casts (except of
 course the legacy C syntax) with a template syntax too
c++ casts have uniform syntax of templated functions, D cast has syntax of prefix operator, what syntax will have cast!()?
May 13 2008
prev sibling next sibling parent reply terranium <spam here.lot> writes:
Janice Caron Wrote:

 We already have that.
 
     import std.conv;
 
     dst = to!(T)(src);
 
 so your base is already covered.
just what I wrote about http://www.digitalmars.com/webnews/newsgroups.php?art_group=digitalmars.D&article_id=71725
May 13 2008
parent "Janice Caron" <caron800 googlemail.com> writes:
On 13/05/2008, terranium <spam here.lot> wrote:
 just what I wrote about
http://www.digitalmars.com/webnews/newsgroups.php?art_group=digitalmars.D&article_id=71725
Seriously - to!(T) is the D standard way of doing convertions that will throw an exception on failure. It is the right function for dynamic-cast-with-throw.
May 13 2008
prev sibling next sibling parent reply Leandro Lucarella <llucax gmail.com> writes:
Yigal Chripun, el 13 de mayo a las 15:58 me escribiste:
 since the other thread got completely off-topic and lost any connection
 to the original discussion, here's a new clean thread, that will
It's generally a better idea to keep the original thread topic and start a new one with the new topic, because this way you have the (already polluted) "safer casts" thread splitted in 2 threads, making even more difficult to follow it. -- Leandro Lucarella (luca) | Blog colectivo: http://www.mazziblog.com.ar/blog/ ---------------------------------------------------------------------------- GPG Key: 5F5A8D05 (F8CD F9A7 BF00 5431 4145 104C 949E BFB6 5F5A 8D05) ---------------------------------------------------------------------------- Corrí muchas carreras, tratando de alcanzarte a vos. Pero corría sólo y siempre salí último.
May 13 2008
parent Yigal Chripun <yigal100 gmail.com> writes:
Leandro Lucarella wrote:
 Yigal Chripun, el 13 de mayo a las 15:58 me escribiste:
 since the other thread got completely off-topic and lost any connection
 to the original discussion, here's a new clean thread, that will
It's generally a better idea to keep the original thread topic and start a new one with the new topic, because this way you have the (already polluted) "safer casts" thread splitted in 2 threads, making even more difficult to follow it.
come on, that's just hindsight. I agree of course that one thread for the topic is better than two, but since we are already in a situation with a thread that contains about 170 posts most of which are off topic, a new clearly named thread ("safer cast -take II" clearly suggests what the content is IMO) is better than just continue posting in the older thread. what else do you want me to do?? Go back in time and repost in a different thread?? since we already have one polluted (huge) thread I do not wish to discuss this here further, if you want, you can mail me personally and continue this discussion there (although i don't see a reason to do this) now, back on topic! what's your opinion on the proposal? please share your thoughts for the betterment of the D community. --Yigal
May 13 2008
prev sibling next sibling parent Bill Baxter <dnewsgroup billbaxter.com> writes:
Yigal Chripun wrote:
 since the other thread got completely off-topic and lost any connection
 to the original discussion, here's a new clean thread, that will
 hopefully summarize the previous discussion and present the latest
 proposal so that hopefully more people join the debate without the need
 to go through all the previous posts (164 posts!).
 
 Here goes:
 
 the proposal is for a safer cast that will _replace_ the current cast.
 this is important both for a consistent syntax and to prevent subverting
 the system with the old style cast.
 
 there are there forms of cast:
 (a) cast(T)something
 (b) cast!(T)something
No no. "super cast(T)something" is what you're looking for. :-) [1]
 (c) reinterpret_cast!(T)
Seriously, though, it sounds good to me. My initial impression was that basically we needed just one alternative form of cast. Leave cast(T) for the "yes I'm casting but it's not a big deal" forms, and make some new form for the "Lookout! Crazy guy with a cast who's not afraid to use it, here!" And that's pretty much what you folks seem to have settled on. (Discounting the third form, reinterpret_cast, since it's just a library function -- but a good one to have, I'll agree) [1] (For those who don't recall "invariant" was slated to be called "super const" for a while...) --bb
May 13 2008
prev sibling next sibling parent reply Jason House <jason.james.house gmail.com> writes:
Long ago in one of the const threads, a few people settled on cast(break const)
for what I think you're calling cast!(T). I like that better than using
template syntax to mean less safe.

Yigal Chripun Wrote:

 since the other thread got completely off-topic and lost any connection
 to the original discussion, here's a new clean thread, that will
 hopefully summarize the previous discussion and present the latest
 proposal so that hopefully more people join the debate without the need
 to go through all the previous posts (164 posts!).
 
 Here goes:
 
 the proposal is for a safer cast that will _replace_ the current cast.
 this is important both for a consistent syntax and to prevent subverting
 the system with the old style cast.
 
 there are there forms of cast:
 (a) cast(T)something
 (b) cast!(T)something
 (c) reinterpret_cast!(T)
 
 there is no need for language support for (c) since this can be
 implemented via a union and this is exactly what this library template
 does. the only reason for this is to have a standardized name. unless
 the user really knows what he's doing this should be avoided, since this
 is the most dangerous form of cast.
 
 goals of this design:
 1. you can search/grep for all casts in your code.
 2. constancy is cast explicitly so no more casting an invariant T to a
 mutable U in one cast. this prevents bugs.
 
 (a) will do would you'll expect in the common case. while (b) is for
 more "dangerous" casts. it can also be defined in all forms
 (implicit/explicit) by the user while (b) isn't.
 
 if T is a class then (a) uses RTTI cast (down casting) or a user defined
 conversion, otherwise (a) will do plain conversions like the current
 cast does.
 (b) is used for constancy casts and the specific use case of casting
 pointers to void. (more specific cases could be added in the future)
 
 for c++ people this translates to:
 (a)  (T is a class) ? dynamic_cast : static_cast
 (b)  const_cast
 (c)  reinterpret_cast implemented in library
 
 also note that cast(T) for classes will return null on failure  just
 like in current D.
 
 questions?
May 13 2008
parent reply Yigal Chripun <yigal100 gmail.com> writes:
Jason House wrote:
 Long ago in one of the const threads, a few people settled on
 cast(break const) for what I think you're calling cast!(T). I like
 that better than using template syntax to mean less safe.
 
That's a good point I want to address: initially this is exactly what I proposed, albeit with some minor changes to syntax. [that was: cast(!const) where the ! was meant to be a "not" similar to your break. the problem is that it's not flexible enough. how do you cast the following: const(invariant(C)*)* ==> const(C*)* ? this is a contrived example of course, but with cast!(T) you can just use: auto newVal = cast!(const(C*)*)oldVal; this works since this passes the following simple test: if you remove all invariant/const modifiers in both the source and target types you need to get the exact same type. this is to make sure you only change constancy and not the type. in the above case you'll get: ((C)*)* ==> (C*)* and those two are identical. this only changes constancy and thus legal. had you tried to also change type you'd get an exception. with cast(break const) or cast(!const) this is not possible with just one cast. that kind of shortcut may be considered as well, but since you can always just specify the new constancy of the type this adds very little benefit but adds another syntax rule to the language. I think that Walter probably won't agree to add such a syntax rule and bloat the language without proper justification. with this I 100% sure that I prefer the minimalist approach and do not want to include this in the proposal. feel free to discuss this issue too and suggest use-cases where you think this syntax is needed.
May 13 2008
parent "Janice Caron" <caron800 googlemail.com> writes:
Note that in addition, the ! form would also be the one to use for:

    void[] p;
    T[] q = cast!(T[])p;

So it's not /just/ removal of const that "danger cast" is for. It's
also for casting from void to non-void. For that reason, calling it
"break const" would be highly counterintuitive.
May 13 2008
prev sibling next sibling parent reply "Joel C. Salomon" <joelcsalomon gmail.com> writes:
-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1

Yigal Chripun wrote:
 there are there forms of cast:
 (a) cast(T)something
 (b) cast!(T)something
 (c) reinterpret_cast!(T)
 So, for example, given the following definitions: class A {…} class B : A {…} class C : A {…} class D {…} we can write: const A ac; auto a = cast!(A)a; and: B b0; auto a = cast(A)b0; auto b = cast(B)a; // should succeed auto c = cast(C)a; // c is null and: A a; auto d = reinterpret_cast(D)a; // legal but unpredictable Are these all the intended uses? —Joel S. -----BEGIN PGP SIGNATURE----- Version: GnuPG v1.4.6 (GNU/Linux) iD8DBQFIKwxozLx4GzBL9dYRAlJMAJwIHL+xCS4ZYDk56JOj6yw4LocF/wCffK/e GSYe5sbxojtr6Ha47jyLP7E= =/jDq -----END PGP SIGNATURE-----
May 14 2008
parent "Janice Caron" <caron800 googlemail.com> writes:
On 14/05/2008, Joel C. Salomon <joelcsalomon gmail.com> wrote:
  Are these all the intended uses?
Yep.
May 15 2008
prev sibling parent Bruno Medeiros <brunodomedeiros+spam com.gmail> writes:
Sorry I'm late.

First of all, I agree with the general principle that D's cast system 
should be more safe. But there are some aspects of your proposal I don't 
agree.

First is the cast(...) vs. cast!(...) syntax. I strongly disagree with 
it, I don't think such constructs should have such a similar syntax, 
particularly sharing the same base name, yet one looking like a function 
call, and the other like a template. I know that the rationale for 
cast!(...) would be to indicate that this is a compile time operation, 
unlike cast(...) but I still don't like it.

But even further than that, I agree with Jason, in that the constancy 
cast construct should not require the type to cast, but just the const 
modifiers. That is, there should only be the forms "cast(const)", 
"cast(invariant)", and "cast(!const)". (Altough cast(!const) should 
really be called cast(mutable) , but that's another issue, about keywords)

Some points were raised against it:

Yigal Chripun wrote:
 Jason House wrote:
 Long ago in one of the const threads, a few people settled on
 cast(break const) for what I think you're calling cast!(T). I like
 that better than using template syntax to mean less safe.
That's a good point I want to address: initially this is exactly what I proposed, albeit with some minor changes to syntax. [that was: cast(!const) where the ! was meant to be a "not" similar to your break. the problem is that it's not flexible enough. how do you cast the following: const(invariant(C)*)* ==> const(C*)* ? this is a contrived example of course, but with cast!(T) you can just
use:
     auto newVal = cast!(const(C*)*)oldVal;
 this works since this passes the following simple test:
 if you remove all invariant/const modifiers in both the source and
 target types you need to get the exact same type. this is to make sure
 you only change constancy and not the type. in the above case you'll
 get: ((C)*)*  ==> (C*)* and those two are identical.
 this only changes constancy and thus legal. had you tried to also change
 type you'd get an exception.

 with cast(break const) or cast(!const) this is not possible with just
 one cast. that kind of shortcut may be considered as well, but since you
 can always just specify the new constancy of the type this adds very
 little benefit but adds another syntax rule to the language. I think
 that Walter probably won't agree to add such a syntax rule and bloat the
 language without proper justification. with this I 100% sure that I
 prefer the minimalist approach and do not want to include this in the
 proposal.

 feel free to discuss this issue too and suggest use-cases where you
 think this syntax is needed.
Well, it's true that you couldn't directly do that case "with just one cast", but one could define a template that did it with just one call: const(invariant(C)*)* foo; auto bar = const_cast!(const(C*)*, foo); So I don't think that would be a problem, right? Janice Caron wrote:
 Note that in addition, the ! form would also be the one to use for:

     void[] p;
     T[] q = cast!(T[])p;

 So it's not /just/ removal of const that "danger cast" is for. It's
 also for casting from void to non-void. For that reason, calling it
 "break const" would be highly counterintuitive.
reinterpret_cast would serve that purpose just fine, wouldn't it? -- Bruno Medeiros - Software Developer, MSc. in CS/E graduate http://www.prowiki.org/wiki4d/wiki.cgi?BrunoMedeiros#D
Jun 10 2008