On Sat, Jun 05, 2021 at 01:46:45AM +0000, someone via Digitalmars-d-learn wrote: [...] > As I am writing code today I am encountering a lot of these situations. > > cast(ushort)(this.pintBottom1 - 1) > > My first walkaround for this was intuitive: > > this.pintBottom1 - cast(ushort) 1 /// I didn't know if eg: 1S was > possible which should have been the proper way to handle it > > ... since pintBottom was already defined as ushort, but it didn't work > also, so I ended with cast(ushort)(this.pintBottom1 - 1). I don't > understand how to write typed code in D then. What's the point of > declaring, for instance ushort's if then nothing will treat them as > ushort's and I have to manually cast() everything to ushort() all the > time ?
Here's my solution to D's short integer cast-madness: abstract it away in an infectious wrapper type. Save the following code into a file called nopromote.d, import it, and whenever you have an expression involving shorts, bytes, etc., that you don't want to write a cast for, just insert .np (for "no-promote") somewhere in the expression (see unittests below for concrete examples). -------- /** * Truncating wrapper around built-in narrow ints to work around stupid * casts. */ module nopromote; enum isNarrowInt(T) = is(T : int) || is(T : uint); /** * A wrapper around a built-in narrow int that truncates the result of * arithmetic operations to the narrow type, overriding built-in int promotion * rules. */ struct Np(T) if (isNarrowInt!T) { T impl; alias impl this; /** * Truncating binary operator. */ Np opBinary(string op, U)(U u) if (is(typeof((T x, U y) => mixin("x " ~ op ~ " y")))) { return Np(cast(T) mixin("this.impl " ~ op ~ " u")); } /** * Truncating unary operator. */ Np opUnary(string op)() if (is(typeof((T x) => mixin(op ~ "cast(int) x")))) { return Np(cast(T) mixin(op ~ " cast(int) this.impl")); } /** * Infectiousness: any expression containing Np should automatically use Np * operator semantics. */ Np opBinaryRight(string op, U)(U u) if (is(typeof((T x, U y) => mixin("x " ~ op ~ " y")))) { return Np(cast(T) mixin("u " ~ op ~ " this.impl")); } } /** * Returns: A lightweight wrapped type that overrides built-in arithmetic * operators to always truncate to the given type without promoting to int or * uint. */ auto np(T)(T t) if (isNarrowInt!T) { return Np!T(t); } // Test binary ops @safe unittest { ubyte x = 1; ubyte y = 2; auto z = x.np + y; static assert(is(typeof(z) : ubyte)); assert(z == 3); byte zz = x.np + y; assert(zz == 3); x = 255; z = x.np + y; assert(z == 1); } @safe unittest { byte x = 123; byte y = 5; auto z = x.np + y; static assert(is(typeof(z) : byte)); assert(z == byte.min); byte zz = x.np + y; assert(zz == byte.min); } @safe unittest { import std.random; short x = cast(short) uniform(0, 10); short y = 10; auto z = x.np + y; static assert(is(typeof(z) : short)); assert(z == x + 10); short s = x.np + y; assert(s == x + 10); } // Test unary ops @safe unittest { byte b = 10; auto c = -b.np; static assert(is(typeof(c) : byte)); assert(c == -10); ubyte ub = 16; auto uc = -ub.np; static assert(is(typeof(uc) : ubyte)); assert(uc == 0xF0); } version(unittest) { // These tests are put here as actual module functions, to force optimizer // not to discard calls to these functions, so that we can see the actual // generated code. byte byteNegate(byte b) { return -b.np; } ubyte ubyteNegate(ubyte b) { return -b.np; } byte byteTest1(int choice, byte a, byte b) { if (choice == 1) return a.np + b; if (choice == 2) return a.np / b; assert(0); } short shortAdd(short a, short b) { return a.np + b; } // Test opBinaryRight byte byteRightTest(byte a, byte c) { auto result = a + c.np; static assert(is(typeof(result) : byte)); return result; } unittest { assert(byteRightTest(127, 1) == byte.min); } short multiTest1(short x, short y) { return short(2) + 2*(x - y.np); } unittest { // Test wraparound semantics. assert(multiTest1(32767, 16384) == short.min); } short multiTest2(short a, short b) { short x = a; short y = b; return (2*x + 1) * (y.np/2 - 1); } unittest { assert(multiTest2(1, 4) == 3); } } // vim:set ai sw=4 ts=4 et: -------- T -- The two rules of success: 1. Don't tell everything you know. -- YHL