c00000fd
c00000fd

Reputation: 22265

Compile-time check that a variable is signed/unsigned

I'm trying to come up with some compile-time ways to check if a certain variable is signed or unsigned. Actually, I was using the following macro for quite some time to check for a signed variable:

#ifdef _DEBUG
#define CHECK_SIGNED(v) if((v) == -(v)){}
#else
#define CHECK_SIGNED(v)
#endif

and then the following will pass it:

INT rr = 0;
CHECK_SIGNED(rr);

while the following:

UINT rr = 0;
CHECK_SIGNED(rr);

will generate a compile-time error:

error C4146: unary minus operator applied to unsigned type, result still unsigned

So now I am trying to come up with a similar check for unsigned variable. Any suggestions?

PS. Although I'm using VS 2017 it'd be nice to make it backwards compatible with older C++ standards.

Upvotes: 2

Views: 1017

Answers (2)

LoS
LoS

Reputation: 1835

The standard solution to verify whether a certain type T be signed or unsigned are the std::is_signed and std::is_unsigned type traits, respectively. However, if the available Standard is pre-C++11, they can be re-implemented.

The std::integral_constant class can be emulated with a minimal interface, where only the two type definitions and the member attribute are declared. About the constexpr keyword, it can be replaced with a simple const.

Example:

template <typename T, T v>
struct integral_constant
{
  typedef T                 value_type;
  typedef integral_constant type;

  static const T value = v;
};

A way to implement the is_signed and is_unsigned type traits involves the partial-specialization of the type trait for all arithmetic signed and unsigned types, respectively. In particular, the original class would be derived from integral_constant<bool, false>, which represents std::false_type, while all the partial-specializations would be derived from integral_constant<bool, true>, which represents std::true_type. During the compiler process, the most specialized vetsion if the class is searched, and therefore if the type T is an arithmetic signed or unsigned type, depending on which of the two the type traits is used, the corresponding specialization is selected. Otherwise, the original class is preferred.

It is important to note that, to entirely emulate the standard type traits, even cv-qualified versions of all the signed or unsigned types should be supported. The simplest way involves the removal of cv-qualification from the type T, if present, through a re-implementation of the std::remove_cv standard type trait.

This approach is robust, but requires to create a lot of repeated code. Furthermore, it is not possible to exploit variadic templates to compress all partial-specializations into one class. In particular, the implementation of a helper type trait, which performs a linear probing on a list of predefined types until the end is reached or a matching type is found, is not even an option.

A different approach would be testing the behavior of the type rather than detecting it. In this case, it is known that, given a certain arithmetic type T, the expression T(-1) < T(0) is true for all signed types, while is false for all unsigned ones.

Example:

template <typename T>
struct is_signed
 : integral_constant<bool, T(-1) < T(0)> {};

template <typename T>
struct is_unsigned
 : integral_constant<bool, !(T(-1) < T(0))> {};

There is only a limit: if the previous design allows to treat cases in which the type T is not arithmetic, the current one works correctly with arithmetic types only. Specializing the type trait for other types is undefined behavior or, in the case the less-than operator be not defined, the program is even ill-formed. However, it seems not to be a problem in your case.

Upvotes: -1

P0W
P0W

Reputation: 47784

Could use something like this :

static_assert(std::is_signed<decltype(rr)>::value, "Not signed number");

and the sister version std::is_unsigned

Also, those are not very difficult to implement on your own for supporting old compilers.

Upvotes: 6

Related Questions