# C++: non-negative signed integral?

** Published:**

In this article, I’ll summarize my first experience playing with type traits in C++.

Note that as my first experience, I’ve decided not to dig too deep into cv-qualifications, being able to modify references of my wrapper, and all the sugar you’d get with a real wrapper.

What I have done is write a skeleton of an observer that shows me enough about templates and type traits for it to be a meaningful exercise – and I hope this simple example helps someone else grasp this concept too!

## Awkward interfaces

Have you ever come across an interface that does a lazy job at constraining its inputs? Tell me, why does this API - with the comment, of course - exist? Would it be so hard to make `bar`

an unsigned integer? *Actually, there are valid - but sparse - reasons to do so, but let me be dramatic.*

```
/* argument must be non-zero */
void foo(int64_t bar);
```

After many failed attempts at rationalizing this, I just swallowed the bitter pill and decided to materialize the non-negativity comment! With that, we come to today’s goal: **greedy non-negativity invariance enforcement for signed integral types**.

## Welcome type traits

Type traits is a ~~fantastic rabbit-hole~~ templated interface that lets you express ideas/constraints around types, and even modify the properties of types, all in compile time! These templates are defined in the `<type_traits>`

header.

A particularly useful metafunction in that header is `std::enable_if`

. Think of it as a compile-time switch for templates. This switch lets us tap into an idiomatic C++ rule - “Substitution Failure Is Not An Error” (SFINAE). Explaining SFINAE would take 5 of these blog posts, so the wiki redirection will have to do for now. `std::enable_if`

lets us define a `typedef`

to a given type if its template argument evaluates to a logically true value. In other words, if we specify constraints as part of `std::enable_if`

’s template arguments, we will cause substitution failures (hence omitting a template instantiation) if said constraints are not satisfied. Now, about constraints…

For this situation, I want to represent two constraints:

- The number must be of
**integral**type.- Integral is a big net, but notable residents are
`bool`

,`char`

,`charX_t`

,`short`

,`int`

,`long`

, and all of their signed and cv-qualified varieties… that’s a mouthful. - Really, I just need to avoid floating point types here.
`std::is_integral`

is my new best friend.

- Integral is a big net, but notable residents are
- The number must be of
**signed**type.- Why? Well, this also motivates why the interface accepted a signed integral type! Because unsigned integers give you zero information, maximum chaos. Imagine accidentally producing a negative number only to feed that into an unsigned container? No warnings, just overflow and vibes…
`std::is_signed`

is my other best friend.

Just like that, we have our building blocks to conditionally (`std::enable_if`

) build a wrapper around signed (`std::is_signed`

), integral (`std::is_integral`

) types. In C++, the template argument that represents these constraints is:

```
template<
class T,
typename = std::enable_if<
std::is_integral<T>::value &&
std::is_signed<T>::value>::type
>
```

Here’s what happened above - we have a template type `T`

. `std::is_integral<T>::value`

is `true`

if `T`

is an integral type - and likewise for `std::is_signed`

. Looking at the signature `std::enable_if<bool B, class T = void>`

, what happens is that if expression `B`

evaluates to `true`

, the templated class `enable_if`

will have a public typedef `type`

equal to `T`

. If expression `B`

evaluates to `false`

, there is no such typedef. Finally, `typename = ...`

just indicates the presence of an optional template parameter with no name and a default value. As you can guess, if that default value is nothing - there is a compiler error, which is exactly what happens if we have either an unsigned type, or a non-integral type, or both!

C++14 introduced some type aliases that make for succint expression of constraints like this, namely `std::enable_if_t`

, `std::is_integral_v`

, and `std::is_signed_v`

, where the `a_x`

just represents `a::x`

. Rewriting with these aliases, we get:

```
template<
class T,
typename = std::enable_if_t<
std::is_integral_v<T> &&
std::is_signed_v<T>>
>
```

## What’s in a wrapper?

Finally, the wrapper around our signed, integral type is very short. Its only job is to greedily - at assignment/construction time - check that its not wrapping a negative number. This can be achieved as follows:

```
class non_negative {
public:
// What does a default construction mean here?
non_negative() = delete;
non_negative(T _number) {
// Negative number check!
if (_number < static_cast<T>(0)) {
throw std::runtime_error(
"Please enter non-negative number.");
}
number = _number;
}
private:
T number;
}
```

The last piece of this puzzle is to make this wrapper transparent to the user or the interfaces consuming it. Remember the simple duck test? If it walks like a duck and it talks like a duck… Right! As long as this wrapper can be implicitly converted to the type it represents, we have the duck. This is what the implicit (templated) conversion operator looks like:

```
inline operator T() const { return static_cast<T>(number); }
```

## Ready for takeoff

Combining all the snippets above, this is the wrapper I have:

```
template<
class T,
typename = std::enable_if_t<
std::is_integral_v<T> &&
std::is_signed_v<T>>
>
class non_negative {
public:
// What does a default construction mean here?
non_negative() = delete;
non_negative(T _number) {
if (_number < static_cast<T>(0)) {
// Negative number check!
throw std::runtime_error(
"Please enter non-negative number.");
}
number = _number;
}
// Implicit conversion to underlying integral type
inline operator T() const { return static_cast<T>(number); }
private:
T number;
};
```

In about 22 lines of code, I learned about type traits, SFINAE, and got to think about better interface designs – particularly when the interface has semantically convoluted constraints! **Not a bad use of 22 lines.**

Here’s the link to my wrapper on a Github gist if you’d like to investigate further. Check out my Github profile (@aprotyas) for more of my coding – no, it’s not playing around with type traits all day!