module checkedint; debug { import std.conv; import std.traits; class OverflowException : Exception { this( string msg, string file = __FILE__, uint line = __LINE__ ) { super( msg, file, line ); } } struct Checked( T ) if ( isIntegral!T ) { T payload; alias payload this; pure this( U )( U value, string file = __FILE__, int line = __LINE__ ) if ( isIntegral!U ) { if ( value < T.min || value > T.max ) { debug throw new OverflowException( "Overflow!", file, line ); } payload = cast( T )value; } pure Checked opUnary( string op, string file = __FILE__, int line = __LINE__ )( ) if ( !( op == "++" || op == "--" ) ) { Checked tmp = mixin( op ~ " payload" ); debug asm { jo overflow; } return tmp; overflow: debug throw new OverflowException( "Overflow!", file, line ); } pure ref Checked opUnary( string op, string file = __FILE__, int line = __LINE__ )( ) if ( op == "++" || op == "--" ) { payload = mixin( op ~ " payload" ); debug asm { jo overflow; } return this; overflow: debug throw new OverflowException( "Overflow!", file, line ); } const pure nothrow Checked opBinary( string op, string file = __FILE__, int line = __LINE__, U )( U other ) if ( isIntegral!U || is( U u == Checked!V, V ) ) { Checked tmp = mixin( "payload " ~ op ~ " other" ); debug asm { jo overflow; } return tmp; overflow: debug throw new OverflowException( "Overflow!", file, line ); } pure nothrow ref Checked opOpAssign( string op, string file = __FILE__, int line = __LINE__, U )( U other ) if ( isIntegral!U || is( U u == Checked!V, V ) ) { mixin( "payload " ~ op ~ "= other;" ); debug asm { jo overflow; } return this; overflow: debug throw new OverflowException( "Overflow!", file, line ); } pure ref Checked opAssign( string file = __FILE__, int line = __LINE__, U )( U value ) if ( isIntegral!U || is( U u == Checked!V, V ) ) { if ( value < T.min || value > T.max ) { debug throw new OverflowException( "Overflow!", file, line ); } payload = cast( T )value; return this; } unittest { import core.exception; import std.exception; Checked a; a = 3; assertNotThrown!OverflowException( a + a, "Overflow on 3 + 3 for " ~ Checked.stringof ); a = 3; assertNotThrown!OverflowException( a - a, "Overflow on 3 - 3 for " ~ Checked.stringof ); a = 3; assertNotThrown!OverflowException( a * a, "Overflow on 3 * 3 for " ~ Checked.stringof ); a = 3; assertNotThrown!OverflowException( a / a, "Overflow on 3 / 3 for " ~ Checked.stringof ); static if( !is( T == uint ) && !is( T == ulong ) ) { a = Checked.max; assertThrown!OverflowException( a + 1, "No overflow on " ~ Checked.stringof ~ ".max(" ~ to!string( Checked.max ) ~ ") + 1 for " ~ Checked.stringof ); } static if( !is( T == int ) && !is( T == long ) && !is( T == uint ) && !is( T == ulong ) ) { a = Checked.min; assertThrown!OverflowException( a - 1, "No overflow on " ~ Checked.stringof ~ ".min(" ~ to!string( Checked.min ) ~ ") - 1 for " ~ Checked.stringof ); } static if( !is( T == uint ) && !is( T == long ) && !is( T == ulong ) ) { a = Checked.max; assertThrown!OverflowException( a << 1, "No overflow on " ~ Checked.stringof ~ ".max (" ~ to!string( Checked.max ) ~ ") << 1" ); } static if ( !isUnsigned!T ) { static if( !is( T == int ) && !is( T == long ) ) { a = Checked.min; assertThrown!OverflowException( a << 1, "No overflow on " ~ Checked.stringof ~ ".min (" ~ to!string( Checked.min ) ~ ") << 1" ); } a = Checked.min; assertThrown!OverflowException( --a, "No overflow on --" ~ Checked.stringof ~ ".min(" ~ to!string( Checked.min ) ~ ") for " ~ Checked.stringof ); a = Checked.min; assertThrown!OverflowException( a--, "No overflow on " ~ Checked.stringof ~ ".min(" ~ to!string( Checked.min ) ~ ")-- for " ~ Checked.stringof ); a = Checked.min; assertThrown!OverflowException( -a, "No overflow on -" ~ Checked.stringof ~ ".min(" ~ to!string( Checked.min ) ~ ") for " ~ Checked.stringof ); a = Checked.max; assertThrown!OverflowException( ++a, "No overflow on ++" ~ Checked.stringof ~ ".max(" ~ to!string( Checked.max ) ~ ") for " ~ Checked.stringof ); a = Checked.max; assertThrown!OverflowException( a++, "No overflow on " ~ Checked.stringof ~ ".max(" ~ to!string( Checked.max ) ~ ")++ for " ~ Checked.stringof ); a = Checked.max; assertThrown!OverflowException( a += 1, "No overflow on " ~ Checked.stringof ~ ".max(" ~ to!string( Checked.max ) ~ ") += 1 for " ~ Checked.stringof ); static if( !is( T == int ) && !is( T == long ) ) { a = Checked.max; assertThrown!OverflowException( a *= 2, "No overflow on " ~ Checked.stringof ~ ".max(" ~ to!string( Checked.max ) ~ ") *= 2 for " ~ Checked.stringof ); } a = Checked.min; assertThrown!OverflowException( a -= 1, "No overflow on " ~ Checked.stringof ~ ".min(" ~ to!string( Checked.min ) ~ ") -= -1 for " ~ Checked.stringof ); a = Checked.min; assertThrown!OverflowException( a <<= 1, "No overflow on " ~ Checked.stringof ~ ".max (" ~ to!string( Checked.max ) ~ ") <<= 1" ); a = Checked.max; assertThrown!OverflowException( a <<= 1, "No overflow on " ~ Checked.stringof ~ ".max (" ~ to!string( Checked.max ) ~ ") <<= 1" ); } } } } else { template Checked( T ) if ( isIntegral!T ) { alias T Checked; } } unittest { import std.typetuple; foreach ( e; TypeTuple!( byte, short, int, long ) ) { Checked!e a; Checked!(Unsigned!e) b; } } void main( ) {}