Compartir a través de


SafeInt (Clase)

Amplía las primitivas de enteros para ayudar a evitar el desbordamiento de enteros y permite comparar diferentes tipos de enteros.

Nota:

La versión más reciente de la biblioteca SafeInt se encuentra en https://github.com/dcleblanc/SafeInt. Para usar la biblioteca SafeInt, clone el repositorio y #include "SafeInt.hpp"

Sintaxis

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

Parámetros

T
El tipo de parámetro entero o booleano que reemplaza SafeInt.

E
Un tipo de datos enumerados que define la directiva de control de errores.

U
El tipo de parámetro entero o booleano para el operando secundario.

rhs
[in] Un parámetro de entrada que representa el valor en el lado derecho del operador en varias funciones independientes.

i
[in] Un parámetro de entrada que representa el valor en el lado derecho del operador en varias funciones independientes.

bits
[in] Un parámetro de entrada que representa el valor en el lado derecho del operador en varias funciones independientes.

Miembros

Constructores públicos

Nombre Descripción
SafeInt::SafeInt Constructor predeterminado.

Operadores de asignación

Nombre Sintaxis
= 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()

Operadores de conversión

Nombre Sintaxis
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

Operadores de comparación

Nombre Sintaxis
< 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()

Operadores aritméticos

Nombre Sintaxis
+ 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)

Operadores lógicos

Nombre Sintaxis
! 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()

Comentarios

La clase SafeInt protege frente al desbordamiento de enteros en las operaciones matemáticas. Por ejemplo, suponga que se agregan dos enteros de 8 bits: uno tiene un valor de 200 y el segundo de 100. La operación matemática correcta sería 200 + 100 = 300. Sin embargo, debido al límite de enteros de 8 bits, se perderá el bit superior y el compilador devolverá como resultado 44 (300-28). Cualquier operación que dependa de esta ecuación matemática generará un comportamiento inesperado.

La clase SafeInt comprueba si se produce un desbordamiento aritmético o si el código intenta dividir por cero. En ambos casos, la clase llama al controlador de errores para advertir al programa del posible problema.

Esta clase también le permite comparar dos tipos diferentes de enteros siempre que sean objetos SafeInt. Normalmente, cuando se hace una comparación, primero se deben convertir los números para que sean del mismo tipo. Para convertir un número en otro tipo con frecuencia se necesitan comprobaciones para asegurarse de que no hay pérdida de datos.

En la tabla de operadores de este tema se enumeran los operadores matemáticos y de comparación que admite la clase SafeInt. La mayoría de operadores matemáticos devuelven un objeto SafeInt de tipo T.

Las operaciones de comparación entre SafeInt y un tipo entero se pueden realizar en cualquier dirección. Por ejemplo, tanto SafeInt<int>(x) < y como y> SafeInt<int>(x) son válidos y devolverán el mismo resultado.

Muchos operadores binarios no admiten el uso de dos tipos de SafeInt diferentes. Un operador de este tipo es &. Se admite SafeInt<T, E> & int, pero no SafeInt<T, E> & SafeInt<U, E>. En el último ejemplo, el compilador no sabe qué tipo de parámetro devolver. Una solución a este problema es convertir el segundo parámetro de nuevo al tipo base. Para ello se puede utilizar SafeInt<T, E> & (U)SafeInt<U, E> con los mismos parámetros.

Nota:

En las operaciones bit a bit, los dos parámetros diferentes deben tener el mismo tamaño. Si los tamaños son diferentes, el compilador producirá una excepción ASSERT. No se puede garantizar que los resultados de esta operación sean precisos. Para resolver este problema, convierta el parámetro más pequeño hasta que tenga el mismo tamaño que el parámetro más grande.

Con los operadores de desplazamiento, al desplazar más bits de los que existen para el tipo de plantilla se producirá una excepción ASSERT. Esta acción no tendrá ningún efecto en el modo de versión. Los operadores de desplazamiento permiten mezclar dos tipos de parámetros SafeInt, ya que el tipo de valor devuelto es igual que el tipo original. El número en el lado derecho del operador solo indica el número de bits que se van a desplazar.

Cuando se hace una comparación lógica con un objeto SafeInt, la comparación es estrictamente aritmética. Por ejemplo, vea estas expresiones:

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

  • ((uint)~0) > -1

La primera instrucción se resuelve en true, pero la segunda instrucción se resuelve en false. La negación bit a bit de 0 es 0xFFFFFFFF. En la segunda instrucción, el operador de comparación predeterminado compara 0xFFFFFFFF con 0xFFFFFFFF y considera que son iguales. El operador de comparación de la clase SafeInt se da cuenta de que el segundo parámetro es negativo, mientras que el primer parámetro no tiene signo. Por lo tanto, aunque la representación de bits es idéntica, el operador lógico SafeInt se da cuenta de que el entero sin signo es mayor que -1.

Tenga cuidado al usar la clase SafeInt en combinación con el operador ternario ?:. Vea la siguiente línea de código.

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

El compilador la convierte en esto:

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

Si flag es false, el compilador produce una excepción en lugar de asignar el valor de -1 a x. Por lo tanto, para evitar este comportamiento, el código correcto que se debe usar es la siguiente línea.

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

A T y U se les puede asignar un tipo booleano, un tipo de carácter o un tipo entero. Los tipos enteros pueden tener o no signo y cualquier tamaño entre 8 y 64 bits.

Nota:

Aunque la clase SafeInt acepta cualquier tipo de entero, funciona mejor con tipos sin signo.

E es el mecanismo de control de errores que usa SafeInt. Con la biblioteca de SafeInt se proporcionan dos mecanismos de control de errores. La directiva predeterminada es SafeIntErrorPolicy_SafeIntException, que produce una excepción SafeIntException (Clase) cuando hay un error. La otra directiva es SafeIntErrorPolicy_InvalidParameter, que detiene el programa si se produce un error.

Hay dos opciones para personalizar la directiva de errores. La primera opción es establecer el parámetro E al crear un objeto SafeInt. Use esta opción si quiere cambiar la directiva de control de errores de un único objeto SafeInt. La otra opción es definir _SAFEINT_DEFAULT_ERROR_POLICY como la clase de control de errores personalizada antes de incluir la biblioteca SafeInt. Use esta opción si quiere cambiar la directiva de control de errores predeterminada de todas las instancias de la clase SafeInt en el código.

Nota:

Una clase personalizada que controla los errores de la biblioteca SafeInt no debe devolver el control al código que llamó al controlador de errores. Después de llamar al controlador de errores, no se puede confiar en el resultado de la operación de SafeInt.

Jerarquía de herencia

SafeInt

Requisitos

Encabezado: SafeInt.hpp

Nota:

La versión más reciente de esta biblioteca se encuentra en https://github.com/dcleblanc/SafeInt. Clone la biblioteca e incluya SafeInt.hpp para usar la biblioteca SafeInt. Prefiere este repositorio de GitHub para <safeint.h>. Es una versión moderna de <safeint.h> que incluye un pequeño número de correcciones de errores, usa características modernas de C++, lo que da lugar a código más eficaz, y se puede trasladar a cualquier plataforma mediante compiladores gcc, clang o Intel.

Ejemplo

#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.
}

Espacio de nombres: none

SafeInt::SafeInt

Construye un objeto SafeInt.

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)

Parámetros

i
[in] El valor del nuevo objeto SafeInt. Debe ser un parámetro de tipo T o U, según el constructor.

b
[in] El valor booleano del nuevo objeto SafeInt.

u
[in] Un objeto SafeInt de tipo U. El nuevo objeto SafeInt tendrá el mismo valor que u, pero será de tipo T.

U El tipo de datos almacenados en SafeInt. Puede ser un tipo entero, de carácter o booleano. Si es un tipo entero, puede tener o no signo y estar formado por entre 8 y 64 bits.

Comentarios

El parámetro de entrada para el constructor, i o u, debe ser un tipo entero, de carácter o booleano. Si es otro tipo de parámetro, la clase SafeInt llama a static_assert para indicar un parámetro de entrada no válido.

Los constructores que utilizan el tipo de plantilla U convierten automáticamente el parámetro de entrada en el tipo especificado por T. La clase SafeInt convierte los datos sin pérdida de datos. Informa al controlador de errores E si no puede convertir los datos al tipo T sin pérdida de datos.

Si crea un objeto SafeInt a partir de un parámetro booleano, debe inicializar el valor inmediatamente. No se puede construir un objeto SafeInt con el código SafeInt<bool> sb;. Se generará un error de compilación.