digitalmars.D - Casts, especially array casts
- bearophile (19/19) Feb 24 2010 This comes from a recent small discussion on the D.learn newsgroup, and ...
- bearophile (3/4) Feb 24 2010 This was not precise enough: such conversion operations count if they ar...
- Jonathan M Davis (26/71) Feb 24 2010 C++ is the only language that I'm aware of which has multiple types of
- bearophile (11/18) Feb 24 2010 In this example the conversion can be a trunc of the float, or it can be...
- Rainer Deyke (14/21) Feb 24 2010 Really? I think the casts are one of the areas where C++ has a huge
- bearophile (6/9) Feb 24 2010 C++ has 4+1 casts, they are a good amount of complexity, and it's not ea...
- Rainer Deyke (14/16) Feb 24 2010 C++ casts perform three different logical functions:
- bearophile (4/5) Feb 24 2010 After your answer and more thinking I have lost most interest in my prop...
- =?UTF-8?B?QWxpIMOHZWhyZWxp?= (16/20) Feb 24 2010 Safely though: It won't allow any wild bit representations. For really
This comes from a recent small discussion on the D.learn newsgroup, and from an answer by Daniel Keep: http://www.digitalmars.com/webnews/newsgroups.php?art_group=digitalmars.D.learn&article_id=18915 There are many possible kinds of casts in D: - To interpret a sequence of bits in a different way, like a uint as a float, a double as a fixed array of two ints, a fixed array of 8 bytes into a fixed array of 2 ints, etc, with no need of an intermediate union definition (And sometimes I want to convert a class reference to a different class, currently you can do it with: cast(Foo)cast(Void*)bar). - To perform a safe dynamic cast between two objects, that can return null. This operation is a bit slow. - To perform a conversion, for example from double to an int, with truncation. In future I can even want a way to tell the compiler how to perform such N to M conversions, so I can convert an int to its string equivalent, etc. - To perform a cast between two arrays, where I want to convert (reinterpret) each item, using a true conversion. - Maybe there are other kinds of casting (like turning an immutable into a mutable, an impure into a pure, arg!, etc). Conflating so much different things in the same cast() syntax is less unsafe and is less flexible. In C++ there are 4+1 kinds of casts, they add some complexity. Maybe in D2 just two different casts can be enough: - A cast that works at compile time only, with no run time penality (the first and last type in my list). - A cast that has to perform some operations at run time (the other kinds of casts in my list). I think this is an important enough thing for D2, and it's not a backwards compatible change, it's not an additive change if you want to do it well (otherwise if you want to follow the route used by C++ of just discouraging the usage of the old C cast it can be an additive change, but it increases the language complexity). If you want to do this in a good way, you can for example use: - static_cast() or scast() for the purely compile-time ones. - dcast() or dynamic_cast(), or just cast() for the run-time ones. Such casts are a bit safer, so they can use a shorter name. Another good thing is to make the cast semantics more tidy, aligning the effects of the casts done on an array literal with the effect of the same cast done on a run-time array (I think they are a bit different now). Bye, bearophile
Feb 24 2010
- A cast that has to perform some operations at run time (the other kinds of casts in my list).This was not precise enough: such conversion operations count if they are done at compile-time too. So you can use a static or dynamic cast at compile-time too, according to what you need. Bye, bearophile
Feb 24 2010
bearophile wrote:This comes from a recent small discussion on the D.learn newsgroup, and from an answer by Daniel Keep:http://www.digitalmars.com/webnews/newsgroups.php?art_group=digitalmars.D.learn&article_id=18915There are many possible kinds of casts in D: - To interpret a sequence of bits in a different way, like a uint as a float, a double as a fixed array of two ints, a fixed array of 8 bytes into a fixed array of 2 ints, etc, with no need of an intermediate union definition (And sometimes I want to convert a class reference to a different class, currently you can do it with: cast(Foo)cast(Void*)bar). - To perform a safe dynamic cast between two objects, that can return null. This operation is a bit slow. - To perform a conversion, for example from double to an int, with truncation. In future I can even want a way to tell the compiler how to perform such N to M conversions, so I can convert an int to its string equivalent, etc. - To perform a cast between two arrays, where I want to convert (reinterpret) each item, using a true conversion. - Maybe there are other kinds of casting (like turning an immutable into a mutable, an impure into a pure, arg!, etc). Conflating so much different things in the same cast() syntax is less unsafe and is less flexible. In C++ there are 4+1 kinds of casts, they add some complexity. Maybe in D2 just two different casts can be enough: - A cast that works at compile time only, with no run time penality (the first and last type in my list). - A cast that has to perform some operations at run time (the other kinds of casts in my list). I think this is an important enough thing for D2, and it's not a backwards compatible change, it's not an additive change if you want to do it well (otherwise if you want to follow the route used by C++ of just discouraging the usage of the old C cast it can be an additive change, but it increases the language complexity). If you want to do this in a good way, you can for example use: - static_cast() or scast() for the purely compile-time ones. - dcast() or dynamic_cast(), or just cast() for the run-time ones. Such casts are a bit safer, so they can use a shorter name. Another good thing is to make the cast semantics more tidy, aligning the effects of the casts done on an array literal with the effect of the same cast done on a run-time array (I think they are a bit different now). Bye, bearophileC++ is the only language that I'm aware of which has multiple types of casts, and I don't think that it really adds anything of benefit. C-style casts are, of course, unsafe, but that doesn't mean that you need a bucket- safe casting with only one type of cast. Two is definitely better than four, but I question that we really need more than one. Can't the compiler figure out which cast is supposed to be used in a given situation and deal with it internally? Having multiple casts just confuses things (certainly, I don't think that I know anyone who fully understands the C++ casts). Really, casts should be quite rare in code anyway. And as much as I'd like to avoid performance penalties, I'd prefer a performance penalty in the rare case when I have to cast rather than having to worry about whether I or anyone else on a project that I'm working on is using the various cast types correctly. In most C++ code that I've seen, people don't bother to use anything other than a c-style cast unless they specifically need one of the C++ casts for a safety reason - like using dynamic_cast to check whether you can safely cast to a particular type. In almost all cases, they just use a c-style cast. I think that it would be a huge mistake for D to have more than one type of cast. And honestly, I would have thought that the compiler would be able to figure out the correct cast type internally. And if it can't, should you really be doing that cast in the first place? I say keep the current single cast type and leave it at that. - Jonathan M Davis
Feb 24 2010
Jonathan M Davis:Can't the compiler figure out which cast is supposed to be used in a given situation and deal with it internally?In this example the conversion can be a trunc of the float, or it can be a reinterpret of the bits contained in the float as an int, the compiler can't tell them apart (currently D performs the trunc/round/ceil, according to the FP flags): float x = 10.5; int y = cast(int)x; The same happens in the conversions of arrays, as I have explained in the post.I'd prefer a performance penalty in the rare case when I have to cast rather than having to worry about whether I or anyone else on a project that I'm working on is using the various cast types correctly.It's not just a matter of performance, it's also a matter of keeping the semantics more tidy, because currently different operations are conflated in the same syntax. I think D programmers can appreciate to have a way to tell apart the two kinds of casts I have explained, also because 90% of the times you can use the normal (run-time) cast, that can be called just cast() or dcast(). In most cases that's what you want, you can use it as the default one, and use a scast or static_cast when you want to reinterpret the bits of a value, struct or array, pointer (or class reference). At the moment cast() performs a static cast on run time arrays, a dynamic cast on array literals, a dynamic cast on values, a dynamic cast on class references, and I think a static cast between signed and unsigned integers. And to convert an integer into a string you need to!(). In my opinion this situation is a bit too much messy. Bye, bearophile
Feb 24 2010
On 2/24/2010 14:30, Jonathan M Davis wrote:C++ is the only language that I'm aware of which has multiple types of casts, and I don't think that it really adds anything of benefit.Really? I think the casts are one of the areas where C++ has a huge advantage over C. Maybe the new casts are not as important as templates and RAII, but they're up there in the same category. The best part of the C++ cast system is that it uses template function syntax, which allows user-defined casts to use the same syntax. Casts are yet another issue where moving from C++ to D feels like regressing to C.Can't the compiler figure out which cast is supposed to be used in a given situation and deal with it internally?That's not the point.Having multiple casts just confuses things (certainly, I don't think that I know anyone who fully understands the C++ casts).If you don't understand C++ casts, then you don't understand D casts. They do the same thing; the C++ version is just more explicit about what it does (and therefore safer and easier to read). -- Rainer Deyke - rainerd eldwood.com
Feb 24 2010
Rainer Deyke:Casts are yet another issue where moving from C++ to D feels like regressing to C.Yet, currently D casts aren't like C ones, D casts performs dynamic casts of object references, etc.If you don't understand C++ casts, then you don't understand D casts.C++ has 4+1 casts, they are a good amount of complexity, and it's not easy to learn their difference and purposes. So probably Jonathan is right when he says many C++ programmers don't use C++ casts well and probably tend to stick to one or two only. I think casts of C++ are too much complex, as most things in C++ :-) That's why I have suggested only two casts for D, that differ a lot in purposes, and where most times you can use just one of them (the one with the simpler and shorter name) and be happy. Bye, bearophile
Feb 24 2010
On 2/24/2010 17:09, bearophile wrote:C++ has 4+1 casts, they are a good amount of complexity, and it's not easy to learn their difference and purposes.C++ casts perform three different logical functions: - Removing cv-qualifiers. (const_cast) - Reinterpreting raw bytes. (reinterpret_cast) - Converting values. (static_cast/dynamic_cast) These are clearly distinct function. Accidentally performing the wrong type of cast is clearly an error, and should be flagged by the compiler. Ideally, casts should be (distinct) library functions, not language features. The only thing vaguely confusing about the C++ system is the distinction between static_cast and dynamic_cast. I wouldn't mind seeing those two merged into a conversion_cast. -- Rainer Deyke - rainerd eldwood.com
Feb 24 2010
Rainer Deyke:C++ casts perform three different logical functions:After your answer and more thinking I have lost most interest in my proposal... -.- Bye, bearophile
Feb 24 2010
Rainer Deyke wrote:C++ casts perform three different logical functions: - Removing cv-qualifiers. (const_cast)Agreed.- Reinterpreting raw bytes. (reinterpret_cast)Safely though: It won't allow any wild bit representations. For really wild stuff, you need to use the C-style cast.- Converting values. (static_cast/dynamic_cast)I would like to separate static_cast and dynamic_cast: - static_cast does two things: a) can convert values: this does call the user-defined conversion operators if necessary b) static_cast also communicates "trust me, this super class *really* is this subclass." In this latter case, no value is converted; rather, the type of the pointer value is changed, so that the member access offsets will be adjusted accordingly later on, when this pointer is used - dynamic_cast performs only (b) above, but safely: "if this super class is actually this subclass, set the pointer to point to it". Otherwise the pointer is set to null. Again, no value conversion here. Ali
Feb 24 2010