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 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
prev sibling next sibling 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 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
next sibling parent 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
prev sibling 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

     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