digitalmars.D.learn - Why does this template not have the desired result?
- Gary Willoughby (11/11) Jul 19 2013 Why does this template not have the desired result? I honestly
- Adam D. Ruppe (15/17) Jul 19 2013 It is because size_t is an unsigned type. Comparisons between
- H. S. Teoh (12/30) Jul 19 2013 [...]
- Gary Willoughby (12/18) Jul 19 2013 The problem with that though is that with size arguments >
- JS (3/23) Jul 20 2013 A warning should be thrown for this type of behavior... it can
- bearophile (4/6) Jul 20 2013 Implementing _well_ such warning in D is hard...
- Adam D. Ruppe (14/16) Jul 20 2013 I see. The only way to guarantee there's no wraparound with a
- JS (4/15) Jul 19 2013 I ran into the same issue earlier... if (a > d + 1) would fail
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
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
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:[...] 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).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.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 expectedT -- Just because you can, doesn't mean you should.
Jul 19 2013
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 expectedThe 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
On Friday, 19 July 2013 at 22:18:54 UTC, Gary Willoughby wrote:A warning should be thrown for this type of behavior... it can result in pretty serious and hard to find bugs.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 expectedThe 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 20 2013
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
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
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