Thread overview | ||||||||||
---|---|---|---|---|---|---|---|---|---|---|
|
February 16, 2014 Why is int implicitly convertible to ulong? | ||||
---|---|---|---|---|
| ||||
isImplicitlyConvertible!(int,ulong) is true. Maybe this is just me, but I get the impression that this is quite nuts. Why is an implicit conversion from a signed to an unsigned type possible? The other way round would be at least somewhat understandable if there's a static check that the values actually fit. |
February 16, 2014 Re: Why is int implicitly convertible to ulong? | ||||
---|---|---|---|---|
| ||||
Posted in reply to Hannes Steffenhagen | On 02/16/2014 01:35 PM, Hannes Steffenhagen wrote: > isImplicitlyConvertible!(int,ulong) is true. Maybe this is just me, but > I get the impression that this is quite nuts. Why is an implicit > conversion from a signed to an unsigned type possible? The other way > round would be at least somewhat understandable if there's a static > check that the values actually fit. I don't know all of the reasons but it is at least about convenience. It is possible to write expressions like 'u + diff' without explicit casts: ulong u = 10; int diff = -3; auto a = u + diff; static assert(is (typeof(a) == ulong)); Related, the type of 'a' above is implied as ulong due to arithmetic conversions, which are sometimes very confusing as well. See "Usual Arithmetic Conversions" here: http://dlang.org/type.html Ali |
February 17, 2014 Re: Why is int implicitly convertible to ulong? | ||||
---|---|---|---|---|
| ||||
Posted in reply to Hannes Steffenhagen | On Sunday, 16 February 2014 at 21:35:02 UTC, Hannes Steffenhagen wrote:
> isImplicitlyConvertible!(int,ulong) is true. Maybe this is just me, but I get the impression that this is quite nuts. Why is an implicit conversion from a signed to an unsigned type possible? The other way round would be at least somewhat understandable if there's a static check that the values actually fit.
IIRC, the way they put it is that "information is not lost," so you could always cast back to an int and get the original value. The same is not true for casting to a smaller type, e.g. int to byte, the original value may be lost.
|
February 17, 2014 Re: Why is int implicitly convertible to ulong? | ||||
---|---|---|---|---|
| ||||
Posted in reply to Hannes Steffenhagen | On Sunday, February 16, 2014 21:35:01 Hannes Steffenhagen wrote:
> isImplicitlyConvertible!(int,ulong) is true. Maybe this is just me, but I get the impression that this is quite nuts. Why is an implicit conversion from a signed to an unsigned type possible? The other way round would be at least somewhat understandable if there's a static check that the values actually fit.
signed to unsigned conversions (and vice versa) are implicit as are conversions from smaller integral types to larger integral types. Converting from smaller integral types to larger really doesn't cause any problems, but the signed to unsigned (or vice versa) can cause issues - one of the biggest of those being the comparison of signed and unsigned values, and IIRC, there was some discussion on making that a warning or error. However, while there are occasional problems from the conversion being implicit, if it weren't implicit, you'd be forced to cast a lot more when the signed and unsigned types interact, which would lead to messier code and could actually increase the number of bugs, because if you got in the habit of casting everywhere to get the signed to unsigned conversions to work, you'd risk accidentally doing stuff like casting a ulong to int and losing data, since the compiler would assume that you knew what you were doing with the cast.
So, it's a tradeoff, and neither making the signed to unsigned (or vice versa) conversions explicit nor implicit would be without problems. Walter went with it being implicit, which matches what C does. However, unlike C, conversions that actually lose data (e.g. long -> int) do require casts so that it's easier to catch those problems. But no data is actually lost with a sign conversions, as casting it back to what it was will result in the same value (unlike with converting to a smaller integral value).
Of slightly bigger concern IMHO is that bool and the character types are all treated as integral types, which is at times useful but also risks some entertaining bugs. But again, it's a matter of tradeoffs. If they required casts when interacting with integral types, then a lot more casting would be required, risking a different set of bugs. There really isn't a right answer as to whether the conversions should be implicit or explicit. It just comes down to the tradeoffs that you prefer.
- Jonathan M Davis
|
February 17, 2014 Re: Why is int implicitly convertible to ulong? | ||||
---|---|---|---|---|
| ||||
Posted in reply to Jonathan M Davis | On Monday, 17 February 2014 at 04:40:52 UTC, Jonathan M Davis wrote:
> On Sunday, February 16, 2014 21:35:01 Hannes Steffenhagen wrote:
>> isImplicitlyConvertible!(int,ulong) is true. Maybe this is just
>> me, but I get the impression that this is quite nuts. Why is an
>> implicit conversion from a signed to an unsigned type possible?
>> The other way round would be at least somewhat understandable if
>> there's a static check that the values actually fit.
>
> signed to unsigned conversions (and vice versa) are implicit as are
> conversions from smaller integral types to larger integral types. Converting
> from smaller integral types to larger really doesn't cause any problems, but
> the signed to unsigned (or vice versa) can cause issues - one of the biggest
> of those being the comparison of signed and unsigned values, and IIRC, there
> was some discussion on making that a warning or error. However, while there
> are occasional problems from the conversion being implicit, if it weren't
> implicit, you'd be forced to cast a lot more when the signed and unsigned
> types interact, which would lead to messier code and could actually increase
> the number of bugs, because if you got in the habit of casting everywhere to
> get the signed to unsigned conversions to work, you'd risk accidentally doing
> stuff like casting a ulong to int and losing data, since the compiler would
> assume that you knew what you were doing with the cast.
>
> So, it's a tradeoff, and neither making the signed to unsigned (or vice versa)
> conversions explicit nor implicit would be without problems. Walter went with
> it being implicit, which matches what C does. However, unlike C, conversions
> that actually lose data (e.g. long -> int) do require casts so that it's
> easier to catch those problems. But no data is actually lost with a sign
> conversions, as casting it back to what it was will result in the same value
> (unlike with converting to a smaller integral value).
>
> Of slightly bigger concern IMHO is that bool and the character types are all
> treated as integral types, which is at times useful but also risks some
> entertaining bugs. But again, it's a matter of tradeoffs. If they required
> casts when interacting with integral types, then a lot more casting would be
> required, risking a different set of bugs. There really isn't a right answer
> as to whether the conversions should be implicit or explicit. It just comes
> down to the tradeoffs that you prefer.
>
> - Jonathan M Davis
I've been bitten by signed / unsigned comparisons before, and I'm sure others have been as well. On the other hand, I can't recall any bugs that were due to an explicit cast. I can see how explicit casts might cause unexpected bugs (if the original type changes but is still a valid cast), but in my personal experience, explicit casts are safer than implicit casts.
Walter decided to adopt C-style switches for D, to simplify translating code. However, implicit fall-through is notorious for causing bugs in C. So as a tradeoff, D still allows fall-through but only by explicitly writing "goto case;".
We could speculate all day, but ultimately it comes down to experience of what works and what doesn't. If something is generally safe in practice, then perhaps we're better with leaving it alone. But if something is a known nuisance for causing bugs, then find a better solution.
|
February 21, 2014 Re: Why is int implicitly convertible to ulong? | ||||
---|---|---|---|---|
| ||||
Posted in reply to Xinok | The specific problem here was when working with std.json. std.json distinguishes between UINTEGER and INTEGER, so I had code like static if(is(T : ulong)) { // must be UINTEGER } else static if(is(T : long)) { // can be either INTEGER or UINTEGER } I've since found out about isSigned and isUnsigned, still it was mighty confusing for me that the first case was selected for signed types. |
February 21, 2014 Re: Why is int implicitly convertible to ulong? | ||||
---|---|---|---|---|
| ||||
Posted in reply to Hannes Steffenhagen | On Friday, February 21, 2014 12:41:07 Hannes Steffenhagen wrote:
> The specific problem here was when working with std.json.
>
> std.json distinguishes between UINTEGER and INTEGER, so I had code like
>
> static if(is(T : ulong)) {
> // must be UINTEGER
> } else static if(is(T : long)) {
> // can be either INTEGER or UINTEGER
> }
>
>
> I've since found out about isSigned and isUnsigned, still it was mighty confusing for me that the first case was selected for signed types.
Well, it is using : - which means implicit conversion, which is always something that you should be careful with in generic code. Technically, something like
struct S
{
ulong ul;
alias ul this;
}
would match it as well, and depending on what the code inside the static if is like, it might work, but there's also a good chance that it wouldn't. Using is(T == ulong) if you want to require that it be exactly ulong, or isUnsigned!T if you want any unsigned integral type. Using implicit conversion in generic code _can_ be useful, but you need to be _very_ careful with it.
- Jonathan M Davis
|
February 21, 2014 Re: Why is int implicitly convertible to ulong? | ||||
---|---|---|---|---|
| ||||
Posted in reply to Hannes Steffenhagen | On 02/21/2014 04:41 AM, Hannes Steffenhagen wrote:
> The specific problem here was when working with std.json.
>
> std.json distinguishes between UINTEGER and INTEGER, so I had code like
>
> static if(is(T : ulong)) {
> // must be UINTEGER
> } else static if(is(T : long)) {
> // can be either INTEGER or UINTEGER
> }
>
>
> I've since found out about isSigned and isUnsigned, still it was mighty
> confusing for me that the first case was selected for signed types.
I have something like the following in an experimental std.json code that converts to JSONValue.
Since std.json uses long for the value, I attempt to detect data loss when the value is a large ulong:
JSONValue to(Target : JSONValue, T)(T value)
{
/* ... */
} else static if (is (T : long)) {
static if (is (T == ulong)) {
enforce(value <= long.max,
format("Data loss: %s %s cannot fit a long.",
T.stringof, value));
}
json.type = JSON_TYPE.INTEGER;
json.integer = value;
} else static if (is (T : real)) {
Ali
|
Copyright © 1999-2021 by the D Language Foundation