www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - Why does this template not have the desired result?

reply "Gary Willoughby" <dev nomad.so> writes:
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?
}
Jul 19 2013
next sibling parent reply "Adam D. Ruppe" <destructionator gmail.com> writes:
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
Jul 19 2013
next sibling parent "H. S. Teoh" <hsteoh quickfur.ath.cx> writes:
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.
Jul 19 2013
prev sibling parent reply "Gary Willoughby" <dev nomad.so> writes:
 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! }
Jul 19 2013
next sibling parent reply "JS" <js.mdnq gmail.com> writes:
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.
Jul 20 2013
parent "bearophile" <bearophileHUGS lycos.com> writes:
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
Jul 20 2013
prev sibling parent "Adam D. Ruppe" <destructionator gmail.com> writes:
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.
Jul 20 2013
prev sibling parent "JS" <js.mdnq gmail.com> writes:
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)...
Jul 19 2013