On 02.07.2016 14:26, Andrei Alexandrescu wrote:

How would you reshape this? It's important that the call to hook is
physically at the end of the function and issued just in that place, and
that the code does not do any redundant work.

U hook(U, T)(T value) { return U.init; }

U opCast(U, T)(T payload)
{
     import std.traits;
     do
     {
         static if (!isUnsigned!T && isUnsigned!U &&
                    T.sizeof <= U.sizeof)
         {
             if (payload < 0) break; // extra check needed
         }
         auto result = cast(U) payload;
         if (result != payload) break;
         static if (isUnsigned!T && !isUnsigned!U)
         {
             static assert(U.sizeof <= T.sizeof);
             if (result < 0) break;
         }
         return result;
     } while (0);
     return hook!U(payload);
}

void main()
{
     opCast!short(42);
}


Thanks,

Andrei

How do you decide what 'redundant work' is? Is this combination of branches and type casts really particularly cheap to execute? Could masking out the "sign bit" from the unsigned value before comparison be faster? Also, isn't the result != payload check redundant if sizeof(U) == sizeof(T)?

Anyway, this is the kind of optimization that compilers are supposed to be good at, it should pick whatever procedure is optimal, not necessarily the one specified. Personally, I'd just write something like:

U opCast(U,T)(T payload)if(...){
    auto result = cast(U)payload;
    if(result == payload && (result<0) == (payload<0))
        return result;
    return hook!U(payload);
}

Reply via email to