On Wed, 26 Nov 2008 21:45:30 +0300, Andrei Alexandrescu <[EMAIL PROTECTED]> wrote:

Denis Koroskin wrote:
On Wed, 26 Nov 2008 18:24:17 +0300, Andrei Alexandrescu <[EMAIL PROTECTED]> wrote:

Also consider:

auto delta = a1.length - a2.length;

What should the type of delta be? Well, it depends. In my scheme that wouldn't even compile, which I think is a good thing; you must decide whether prior information makes it an unsigned or a signed integral.

Sure, it shouldn't compile. But explicit casting to either type won't help. Let's say you expect that a1.length > a2.length and thus expect a strictly positive result. Putting an explicit cast will not detect (but suppress) an error and give you an erroneous result silently.

But "silently" and "putting a cast" don't go together. It's the cast that makes the erroneous result non-silent.

Besides, you don't need to cast. You can always use a function that does the requisite checks. std.conv will have some of those, should any change in the rules make it necessary.

By this I'm essentially replying Don's message in the bugs newsgroup: nobody puts a gun to your head to cast.

Putting an assert(a1.length > a2.length) might help, but the check will be unavailable unless code is compiled with asserts enabled.

Put an enforce(a1.length > a2.length) then.


Right, it is better. Problem is, you don't want to put checks like "a1.length > a2.length" into your code (I don't, at least). All you want is to be sure that "auto result = a1.length - a2.length" is positive. You *then* decide and solve the "a1.length - a2.length >= 0" equation that leads to the check. Moreover, why evaluate both a1.length and a2.length twice? And you should update all your checks everytime you change your code.

A better solution would be to write code as follows:
auto delta = unsigned(a1.length - a2.length); // returns an unsigned value, throws on overflow (i.e., "2 - 4") auto delta = signed(a1.length - a2.length); // returns result as a signed value. Throws on overflow (i.e., "int.min - 1")
auto delta = a1.length - a2.length; // won't compile

Amazingly this solution was discussed with these exact names! The signed and unsigned functions can be implemented as libraries, but unfortunately (or fortunately I guess) that means the bits32 and bits64 are available to all code.

One fear of mine is the reaction of throwing of hands in the air "how many integral types are enough???". However, if we're to judge by the addition of long long and a slew of typedefs to C99 and C++0x, the answer is "plenty". I'd be interested in gaging how people feel about adding two (bits64, bits32) or even four (bits64, bits32, bits16, and bits8) types as basic types. They'd be bitbags with undecided sign ready to be converted to their counterparts of decided sign.

// this one is also handy:
auto newLength = checked(a1.length - 1); // preserves type of a1.length, be it int or uint, throws on overflow

This could be rather tricky. How can overflow be checked? By inspecting the status bits in the processor only; at the language/typesystem level there's little to do.

It is an implementation detail. Expression can be calculated with higher bit precision and result compared to needed range.


I have previously shown an implementation of unsigned/signed:
 import std.stdio;
 int signed(lazy int dg)
{
    auto result = dg();
    asm {
       jo overflow;
    }
    return result;
     overflow:
    throw new Exception("Integer overflow occured");
}
 int main()
{
   int t = int.max;
   try
   {
       int s = signed(t + 1);
       writefln("Result is %d", s);
   }
   catch(Exception e)
   {
       writefln("Whoops! %s", e.toString());
   }
   return 0;
}

Ah, there we go! Thanks for pasting this code.

But Andrei has correctly pointed out that it has a problem - it may throw without a reason:
int i = int.max + 1; // sets an overflow flag
auto result = expectSigned(1); // raises an exception
 Overflow flag may also be cleared in a complex expression:
auto result = expectUnsigned(1 + (uint.max + 1)); // first add will overflow and second one clears the flag -> no exception as a result A possible solution is to make the compiler aware of this construct and disallow passing none (case 2) or more that one operation (case 1) to the method.

Can't you clear the overflow flag prior to invoking the operation?


No need for this, it adds one more instruction for no gain, flag is automatically set/reset at any of add/sub/mul operations. It can only save you from "auto result = signed(1)" error, that's why I said it should be disallowed in first place.

I'll also mention that making it a delegate reduces appeal quite a bit; expressions under the check tend to be simple which makes the relative overhead huge.


Such simple instructions are usually inlined, aren't they?

Reply via email to