regarding what seems (to me) unnecessary casts on integer expressions
H. S. Teoh
hsteoh at quickfur.ath.cx
Mon Jun 7 14:12:14 UTC 2021
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
More information about the Digitalmars-d-learn
mailing list