Edit

Share via


SafeInt Class

Extends the integer primitives to help prevent integer overflow and lets you compare different types of integers.

Note

The latest version of the SafeInt library is located at https://github.com/dcleblanc/SafeInt. To use the SafeInt library, clone the repo and #include "SafeInt.hpp"

Syntax

template<typename T, typename E = _SAFEINT_DEFAULT_ERROR_POLICY>
class SafeInt;

Parameters

T
The type of integer or Boolean parameter that SafeInt replaces.

E
An enumerated data type that defines the error handling policy.

U
The type of integer or Boolean parameter for the secondary operand.

rhs
[in] An input parameter that represents the value on the right side of the operator in several stand-alone functions.

i
[in] An input parameter that represents the value on the right side of the operator in several stand-alone functions.

bits
[in] An input parameter that represents the value on the right side of the operator in several stand-alone functions.

Members

Public Constructors

Name Description
SafeInt::SafeInt Default constructor.

Assignment Operators

Name Syntax
= template<typename U>
SafeInt<T,E>& operator= (const U& rhs)
= SafeInt<T,E>& operator= (const T& rhs) throw()
= template<typename U>
SafeInt<T,E>& operator= (const SafeInt<U, E>& rhs)
= SafeInt<T,E>& operator= (const SafeInt<T,E>& rhs) throw()

Casting Operators

Name Syntax
bool operator bool() throw()
char operator char() const
signed char operator signed char() const
unsigned char operator unsigned char() const
__int16 operator __int16() const
unsigned __int16 operator unsigned __int16() const
__int32 operator __int32() const
unsigned __int32 operator unsigned __int32() const
long operator long() const
unsigned long operator unsigned long() const
__int64 operator __int64() const
unsigned __int64 operator unsigned __int64() const
wchar_t operator wchar_t() const

Comparison Operators

Name Syntax
< template<typename U>

bool operator< (U rhs) const throw()
< bool operator< (SafeInt<T,E> rhs) const throw()
>= template<typename U>

bool operator>= (U rhs) const throw()
>= Bool operator>= (SafeInt<T,E> rhs) const throw()
> template<typename U>

bool operator> (U rhs) const throw()
> Bool operator> (SafeInt<T,E> rhs) const throw()
<= template<typename U>

bool operator<= (U rhs) const throw()
<= bool operator<= (SafeInt<T,E> rhs) const throw()
== template<typename U>

bool operator== (U rhs) const throw()
== bool operator== (bool rhs) const throw()
== bool operator== (SafeInt<T,E> rhs) const throw()
!= template<typename U>

bool operator!= (U rhs) const throw()
!= bool operator!= (bool b) const throw()
!= bool operator!= (SafeInt<T,E> rhs) const throw()

Arithmetic Operators

Name Syntax
+ const SafeInt<T,E>& operator+ () const throw()
- SafeInt<T,E> operator- () const
++ SafeInt<T,E>& operator++ ()
-- SafeInt<T,E>& operator-- ()
% template<typename U>

SafeInt<T,E> operator% (U rhs) const
% SafeInt<T,E> operator% (SafeInt<T,E> rhs) const
%= template<typename U>

SafeInt<T,E>& operator%= (U rhs)
%= template<typename U>

SafeInt<T,E>& operator%= (SafeInt<U, E> rhs)
* template<typename U>

SafeInt<T,E> operator* (U rhs) const
* SafeInt<T,E> operator* (SafeInt<T,E> rhs) const
*= SafeInt<T,E>& operator*= (SafeInt<T,E> rhs)
*= template<typename U>

SafeInt<T,E>& operator*= (U rhs)
*= template<typename U>

SafeInt<T,E>& operator*= (SafeInt<U, E> rhs)
/ template<typename U>

SafeInt<T,E> operator/ (U rhs) const
/ SafeInt<T,E> operator/ (SafeInt<T,E> rhs ) const
/= SafeInt<T,E>& operator/= (SafeInt<T,E> i)
/= template<typename U>

SafeInt<T,E>& operator/= (U i)
/= template<typename U>

SafeInt<T,E>& operator/= (SafeInt<U, E> i)
+ SafeInt<T,E> operator+ (SafeInt<T,E> rhs) const
+ template<typename U>

SafeInt<T,E> operator+ (U rhs) const
+= SafeInt<T,E>& operator+= (SafeInt<T,E> rhs)
+= template<typename U>

SafeInt<T,E>& operator+= (U rhs)
+= template<typename U>

SafeInt<T,E>& operator+= (SafeInt<U, E> rhs)
- template<typename U>

SafeInt<T,E> operator- (U rhs) const
- SafeInt<T,E> operator- (SafeInt<T,E> rhs) const
-= SafeInt<T,E>& operator-= (SafeInt<T,E> rhs)
-= template<typename U>

SafeInt<T,E>& operator-= (U rhs)
-= template<typename U>

SafeInt<T,E>& operator-= (SafeInt<U, E> rhs)

Logical Operators

Name Syntax
! bool operator !() const throw()
~ SafeInt<T,E> operator~ () const throw()
<< template<typename U>

SafeInt<T,E> operator<< (U bits) const throw()
<< template<typename U>

SafeInt<T,E> operator<< (SafeInt<U, E> bits) const throw()
<<= template<typename U>

SafeInt<T,E>& operator<<= (U bits) throw()
<<= template<typename U>

SafeInt<T,E>& operator<<= (SafeInt<U, E> bits) throw()
>> template<typename U>

SafeInt<T,E> operator>> (U bits) const throw()
>> template<typename U>

SafeInt<T,E> operator>> (SafeInt<U, E> bits) const throw()
>>= template<typename U>

SafeInt<T,E>& operator>>= (U bits) throw()
>>= template<typename U>

SafeInt<T,E>& operator>>= (SafeInt<U, E> bits) throw()
& SafeInt<T,E> operator& (SafeInt<T,E> rhs) const throw()
& template<typename U>

SafeInt<T,E> operator& (U rhs) const throw()
&= SafeInt<T,E>& operator&= (SafeInt<T,E> rhs) throw()
&= template<typename U>

SafeInt<T,E>& operator&= (U rhs) throw()
&= template<typename U>

SafeInt<T,E>& operator&= (SafeInt<U, E> rhs) throw()
^ SafeInt<T,E> operator^ (SafeInt<T,E> rhs) const throw()
^ template<typename U>

SafeInt<T,E> operator^ (U rhs) const throw()
^= SafeInt<T,E>& operator^= (SafeInt<T,E> rhs) throw()
^= template<typename U>

SafeInt<T,E>& operator^= (U rhs) throw()
^= template<typename U>

SafeInt<T,E>& operator^= (SafeInt<U, E> rhs) throw()
| SafeInt<T,E> operator| (SafeInt<T,E> rhs) const throw()
| template<typename U>

SafeInt<T,E> operator| (U rhs) const throw()
|= SafeInt<T,E>& operator|= (SafeInt<T,E> rhs) throw()
|= template<typename U>

SafeInt<T,E>& operator|= (U rhs) throw()
|= template<typename U>

SafeInt<T,E>& operator|= (SafeInt<U, E> rhs) throw()

Remarks

The SafeInt class protects against integer overflow in mathematical operations. For example, consider adding two 8-bit integers: one has a value of 200 and the second has a value of 100. The correct mathematical operation would be 200 + 100 = 300. However, because of the 8-bit integer limit, the upper bit will be lost and the compiler will return 44 (300 - 28) as the result. Any operation that depends on this mathematical equation will generate unexpected behavior.

The SafeInt class checks whether an arithmetic overflow occurs or whether the code tries to divide by zero. In both cases, the class calls the error handler to warn the program of the potential problem.

This class also lets you compare two different types of integers as long as they are SafeInt objects. Typically, when you do a comparison, you must first convert the numbers to be the same type. Casting one number to another type often requires checks to make sure that there is no loss of data.

The Operators table in this topic lists the mathematical and comparison operators supported by the SafeInt class. Most mathematical operators return a SafeInt object of type T.

Comparison operations between a SafeInt and an integral type can be performed in either direction. For example, both SafeInt<int>(x) < y and y> SafeInt<int>(x) are valid and will return the same result.

Many binary operators don't support using two different SafeInt types. One example of this is the & operator. SafeInt<T, E> & int is supported, but SafeInt<T, E> & SafeInt<U, E> isn't. In the latter example, the compiler does not know what type of parameter to return. One solution to this problem is to cast the second parameter back to the base type. By using the same parameters, this can be done with SafeInt<T, E> & (U)SafeInt<U, E>.

Note

For any bitwise operations, the two different parameters should be the same size. If the sizes differ, the compiler will throw an ASSERT exception. The results of this operation can't be guaranteed to be accurate. To resolve this issue, cast the smaller parameter until it's the same size as the larger parameter.

For the shift operators, shifting more bits than exist for the template type will throw an ASSERT exception. This will have no effect in release mode. Mixing two types of SafeInt parameters is possible for the shift operators because the return type is the same as the original type. The number on the right side of the operator only indicates the number of bits to shift.

When you do a logical comparison with a SafeInt object, the comparison is strictly arithmetic. For example, consider these expressions:

  • SafeInt<uint>((uint)~0) > -1

  • ((uint)~0) > -1

The first statement resolves to true, but the second statement resolves to false. The bitwise negation of 0 is 0xFFFFFFFF. In the second statement, the default comparison operator compares 0xFFFFFFFF to 0xFFFFFFFF and considers them to be equal. The comparison operator for the SafeInt class realizes that the second parameter is negative whereas the first parameter is unsigned. Therefore, although the bit representation is identical, the SafeInt logical operator realizes that the unsigned integer is larger than -1.

Be careful when you use the SafeInt class together with the ?: ternary operator. Consider the following line of code.

Int x = flag ? SafeInt<unsigned int>(y) : -1;

The compiler converts it to this:

Int x = flag ? SafeInt<unsigned int>(y) : SafeInt<unsigned int>(-1);

If flag is false, the compiler throws an exception instead of assigning the value of -1 to x. Therefore, to avoid this behavior, the correct code to use is the following line.

Int x = flag ? (int) SafeInt<unsigned int>(y) : -1;

T and U can be assigned a Boolean type, character type, or integer type. The integer types can be signed or unsigned and any size from 8 bits to 64 bits.

Note

Although the SafeInt class accepts any kind of integer, it performs more efficiently with unsigned types.

E is the error handling mechanism that SafeInt uses. Two error handling mechanisms are provided with the SafeInt library. The default policy is SafeIntErrorPolicy_SafeIntException, which throws a SafeIntException Class exception when an error occurs. The other policy is SafeIntErrorPolicy_InvalidParameter, which stops the program if an error occurs.

There are two options to customize the error policy. The first option is to set the parameter E when you create a SafeInt. Use this option when you want to change the error handling policy for just one SafeInt. The other option is to define _SAFEINT_DEFAULT_ERROR_POLICY to be your customized error-handling class before you include the SafeInt library. Use this option when you want to change the default error handling policy for all instances of the SafeInt class in your code.

Note

A customized class that handles errors from the SafeInt library should not return control to the code that called the error handler. After the error handler is called, the result of the SafeInt operation can't be trusted.

Inheritance Hierarchy

SafeInt

Requirements

Header: SafeInt.hpp

Note

The latest version of this library is located at https://github.com/dcleblanc/SafeInt. Clone the library and include SafeInt.hpp to use the SafeInt library. Prefer this GitHub repo to <safeint.h>. it's a modern version of <safeint.h> that includes a small number of bug fixes, uses modern features of C++ resulting in more efficient code, and is portable to any platform using gcc, clang, or Intel compilers.

Example

#include "SafeInt.hpp" // set path to your clone of the SafeInt GitHub repo (https://github.com/dcleblanc/SafeInt)

int main()
{
    int divisor = 3;
    int dividend = 6;
    int result;

    bool success = SafeDivide(dividend, divisor, result); // result = 2
    success = SafeDivide(dividend, 0, result); // expect fail. result isn't modified.
}

Namespace: none

SafeInt::SafeInt

Constructs a SafeInt object.

SafeInt() throw

SafeInt (const T& i) throw ()

SafeInt (bool b) throw ()

template <typename U>
SafeInt (const SafeInt <U, E>& u)

I template <typename U>
SafeInt (const U& i)

Parameters

i
[in] The value for the new SafeInt object. This must be a parameter of type T or U, depending on the constructor.

b
[in] The Boolean value for the new SafeInt object.

u
[in] A SafeInt of type U. The new SafeInt object will have the same value as u, but will be of type T.

U The type of data stored in the SafeInt. This can be either a Boolean, character, or integer type. If it's an integer type, it can be signed or unsigned and be between 8 and 64 bits.

Remarks

The input parameter for the constructor, i or u, must be a Boolean, character, or integer type. If it's another type of parameter, the SafeInt class calls static_assert to indicate an invalid input parameter.

The constructors that use the template type U automatically convert the input parameter to the type specified by T. The SafeInt class converts the data without any loss of data. It reports to the error handler E if it can't convert the data to type T without data loss.

If you create a SafeInt from a Boolean parameter, you need to initialize the value immediately. You can't construct a SafeInt using the code SafeInt<bool> sb;. This will generate a compile error.