Thread overview | ||||||||||
---|---|---|---|---|---|---|---|---|---|---|
|
July 19, 2013 Why does this template not have the desired result? | ||||
---|---|---|---|---|
| ||||
Why does this template not have the desired result? I honestly though it would return true. import std.stdio; template inBounds(size_t size, T) { enum result = (size >= T.min && size <= T.max); } void main(string[] args) { writefln("%s", inBounds!(10, int).result); // false! eh? } |
July 19, 2013 Re: Why does this template not have the desired result? | ||||
---|---|---|---|---|
| ||||
Posted in reply to Gary Willoughby | On Friday, 19 July 2013 at 20:26:36 UTC, Gary Willoughby wrote:
> Why does this template not have the desired result? I honestly though it would return true.
It is because size_t is an unsigned type. Comparisons between signed and unsigned numbers can be surprising because if either of the items being compared are unsigned, the whole comparison is unsigned.
Negative signed numbers, when interpreted as unsigned, become very large numbers because the bits are flipped when you go negative. cast(ubyte) -1 == 255, and of course, 255 <= 10 is false.
The way I'd do the inBounds is to just use T size instead of size_t size.
template inBounds(T, T size) { snip same stuff }
then
writefln("%s", inBounds!(int, 10).result); // true as expected
|
July 19, 2013 Re: Why does this template not have the desired result? | ||||
---|---|---|---|---|
| ||||
Posted in reply to Adam D. Ruppe | On Fri, Jul 19, 2013 at 10:42:54PM +0200, Adam D. Ruppe wrote: > On Friday, 19 July 2013 at 20:26:36 UTC, Gary Willoughby wrote: > >Why does this template not have the desired result? I honestly though it would return true. > > It is because size_t is an unsigned type. Comparisons between signed and unsigned numbers can be surprising because if either of the items being compared are unsigned, the whole comparison is unsigned. > > Negative signed numbers, when interpreted as unsigned, become very large numbers because the bits are flipped when you go negative. cast(ubyte) -1 == 255, and of course, 255 <= 10 is false. [...] Yikes. I would've expected a compiler warning (at least!) for this, or an outright compile error, unless you use an explicit cast. This kind of silent conversion is just asking for bugs (and newbie annoyance when they can't figure out what went wrong). > The way I'd do the inBounds is to just use T size instead of size_t size. Yeah, comparing disparate types is tricky business, and not recommended if it can be avoided. > template inBounds(T, T size) { snip same stuff } > > then > writefln("%s", inBounds!(int, 10).result); // true as > expected T -- Just because you can, doesn't mean you should. |
July 19, 2013 Re: Why does this template not have the desired result? | ||||
---|---|---|---|---|
| ||||
Posted in reply to Gary Willoughby | On Friday, 19 July 2013 at 20:26:36 UTC, Gary Willoughby wrote:
> Why does this template not have the desired result? I honestly though it would return true.
>
> import std.stdio;
>
> template inBounds(size_t size, T)
> {
> enum result = (size >= T.min && size <= T.max);
> }
>
> void main(string[] args)
> {
> writefln("%s", inBounds!(10, int).result); // false! eh?
> }
I ran into the same issue earlier... if (a > d + 1) would fail even though a = 0 and d + 1 = -1 (supposedly, said the debugger)...
|
July 19, 2013 Re: Why does this template not have the desired result? | ||||
---|---|---|---|---|
| ||||
Posted in reply to Adam D. Ruppe | > The way I'd do the inBounds is to just use T size instead of size_t size.
>
> template inBounds(T, T size) { snip same stuff }
>
> then
> writefln("%s", inBounds!(int, 10).result); // true as expected
The problem with that though is that with size arguments > int.max will wrap when being cast to int (T)? i.e.:
import std.stdio;
template inBounds(T, T size)
{
enum result = (size >= T.min && size <= T.max);
}
void main(string[] args)
{
writefln("%s", inBounds!(int, int.max + 1).result); //true, wrong!
}
|
July 20, 2013 Re: Why does this template not have the desired result? | ||||
---|---|---|---|---|
| ||||
Posted in reply to Gary Willoughby | On Friday, 19 July 2013 at 22:18:54 UTC, Gary Willoughby wrote:
>> The way I'd do the inBounds is to just use T size instead of size_t size.
>>
>> template inBounds(T, T size) { snip same stuff }
>>
>> then
>> writefln("%s", inBounds!(int, 10).result); // true as expected
>
> The problem with that though is that with size arguments > int.max will wrap when being cast to int (T)? i.e.:
>
> import std.stdio;
>
> template inBounds(T, T size)
> {
> enum result = (size >= T.min && size <= T.max);
> }
>
> void main(string[] args)
> {
> writefln("%s", inBounds!(int, int.max + 1).result); //true, wrong!
> }
A warning should be thrown for this type of behavior... it can result in pretty serious and hard to find bugs.
|
July 20, 2013 Re: Why does this template not have the desired result? | ||||
---|---|---|---|---|
| ||||
Posted in reply to JS | JS:
> A warning should be thrown for this type of behavior... it can result in pretty serious and hard to find bugs.
Implementing _well_ such warning in D is hard...
Bye,
bearophile
|
July 20, 2013 Re: Why does this template not have the desired result? | ||||
---|---|---|---|---|
| ||||
Posted in reply to Gary Willoughby | On Friday, 19 July 2013 at 22:18:54 UTC, Gary Willoughby wrote:
> The problem with that though is that with size arguments > int.max will wrap when being cast to int (T)? i.e.:
I see. The only way to guarantee there's no wraparound with a literal is to use a string.... which messes up the arithmetic but works for anything:
template inBounds(T, string size)
{
import std.conv;
enum result = (T.min <= to!T(size));// && size <= T.max);
}
writefln("%s", inBounds!(int, "-5995534353510").result); // false! eh?
That would throw at compile time - to checks for overflows, and since it is an enum in a template, it is run in CTFE.
So kinda awkward but would work fairly reliabily.
|
Copyright © 1999-2021 by the D Language Foundation