3

I need to mask out some leading bits of an value. If the value is unsigned, I can assert (guarantee) that the some arbitrary number of leading bits are not set, that is the value is guaranteed to be limited.

If it is signed, I need to mask out the leading bits (turning the value into some nonportable heap of bits, yes, I am aware of that :-)). I would like to save the masking operation if the value is unsigned.

So I basically have

template<typename T, some more template parameters>
class {
    unsigned transform(T value) {
        ...
        if (isSigned(T)) {
            value &= mask;
        }
        ...
    }
}

Is there an easy way to write an isSigned() which can be evaluated at compile time (to enable the optimizer to remove the unsigned dead code)?

Of course I could add another template parameter...

Gunther Piez
  • 29,760
  • 6
  • 71
  • 103

3 Answers3

5

Yes. You have to use partial specialisation:

template <bool> struct impl { static void foo(); };
template <> struct impl<true> { static void foo(); };

template <typename T> struct Foo
{
    void do_magic(T const &)
    {
        impl<std::is_signed<T>::value>();
        // ...
    }
};

You can use the ready-made is_signed trait class from <type_traits> rather than rolling your own.

Kerrek SB
  • 464,522
  • 92
  • 875
  • 1,084
  • Actually, the compiler is fully capable of optimising the dead code. But it might issue a warning … – Konrad Rudolph Mar 15 '12 at 23:45
  • 1
    @KonradRudolph: That's a detail. The OP may want to write code that only makes sense in one of the two cases, and the `if` statement requires you to write correct code even if it is never used. Templates don't have this restriction. – Kerrek SB Mar 15 '12 at 23:47
  • Thank you, I will go for this. The main thing what was missing was the `std::is_signed` (or `numeric_limits::is_signed` or even `T(-1) < T(0)` :-)) – Gunther Piez Mar 16 '12 at 00:00
  • You can also create an overload that has an extra `std::true_type` and another with an extra `std::false_type` parameter, and pass `typename std::is_signed::type()` as that argument. It will then call the signed or unsigned version appropriately. Signed-ness is not a good example use for that, `std::is_integral` is a better use case when it is critical not to attempt to treat the argument as a number. – doug65536 Jan 30 '14 at 15:49
3
if (T(-1) < T(0))

But I would put that in a template parameter and use it for specialization, not conditional code. Conditional code based on template parameters often leads to spurious compiler warnings, such as "unreachable code" or "constant expression in condition".

Something like:

template <typename T, bool is_signed>
inline void apply_mask_helper(T& value) { value &= mask; }

template <typename T>
inline void apply_mask_helper<T, false>(T&) { }

template <typename T>
inline void apply_mask(T& value) { apply_mask_helper<T, T(-1) < T(0)>(value); }
Ben Voigt
  • 277,958
  • 43
  • 419
  • 720
  • I thought the OP already had a compile-time check for signedness and just wanted a static version of the conditional... – Kerrek SB Mar 15 '12 at 23:45
  • I will go for that and look at the generated code, usually gcc handles that quite nice :-) – Gunther Piez Mar 15 '12 at 23:46
  • @Kerrek A static conditional? AFAIK this could only work with the ternary operator, because `if` is only ever evaluated at runtime? – Gunther Piez Mar 15 '12 at 23:48
  • @drhirsch: A static conditional is what I wrote in my answer. It's a conditional that is resolved at compile time. C++ doesn't have a built-in "`static if`", so we have this template crutch. – Kerrek SB Mar 15 '12 at 23:50
  • @KerrekSB I see. I fact now I recognize that I have already used specializations as `static if` in other parts of the code, so I will go with it again. – Gunther Piez Mar 15 '12 at 23:56
2

Use numeric_limits from the limits header:

if(numeric_limits<T>::is_signed) { … }

Like Kerrek said, I’d go with partial specialisation. Otherwise the compiler might complain that the condition’s value is known at compile time.

Konrad Rudolph
  • 530,221
  • 131
  • 937
  • 1,214