Classe SafeInt
Estende as primitivas de inteiro para ajudar a evitar o estouro de inteiro e permite comparar diferentes tipos de inteiros.
Observação
A última versão da biblioteca SafeInt está localizada em https://github.com/dcleblanc/SafeInt. Para usar a biblioteca SafeInt, clone o repositório e #include "SafeInt.hpp"
Sintaxe
template<typename T, typename E = _SAFEINT_DEFAULT_ERROR_POLICY>
class SafeInt;
Parâmetros
T
O tipo de inteiro ou parâmetro booliano que SafeInt
substitui.
E
Um tipo de dados enumerados que define a política de tratamento de erro.
U
O tipo de inteiro ou parâmetro booliano para o operando secundário.
rhs
[in] Um parâmetro de entrada que representa o valor no lado direito do operador em várias funções independentes.
i
[in] Um parâmetro de entrada que representa o valor no lado direito do operador em várias funções independentes.
bits
[in] Um parâmetro de entrada que representa o valor no lado direito do operador em várias funções independentes.
Membros
Construtores públicos
Nome | Descrição |
---|---|
SafeInt::SafeInt | Construtor padrão. |
Operadores de Atribuição
Nome | Sintaxe |
---|---|
= | 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 conversão
Nome | Sintaxe |
---|---|
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 |
longo | 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 comparação
Nome | Sintaxe |
---|---|
< | 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
Nome | Sintaxe |
---|---|
+ | 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:
Nome | Sintaxe |
---|---|
! | 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() |
Comentários
A classe SafeInt
protege contra o estouro de inteiros em operações matemáticas. Por exemplo, considere adicionar dois inteiros de 8 bits: um tem o valor 200, o outro, 100. A operação matemática correta seria 200 + 100 = 300. No entanto, devido ao limite de inteiros de 8 bits, o bit superior será perdido, e o compilador retornará como resultado 44 (300 - 28). Qualquer operação que depender dessa equação matemática gerará um comportamento inesperado.
A classe SafeInt
verifica se um estouro aritmético ocorreu ou se o código tenta dividir por zero. Em ambos os casos, a classe chama o manipulador de erro para avisar o programa sobre o possível problema.
Essa classe também permite comparar dois tipos diferentes de inteiros, desde que sejam objetos SafeInt
. Normalmente, quando você executa uma comparação, primeiro deve converter os números no mesmo tipo. Converter um número em outro tipo geralmente requer verificações para garantir que não haja perda de dados.
A tabela Operadores neste tópico mostra os operadores matemáticos e de comparação compatíveis com a classe SafeInt
. A maioria dos operadores matemáticos retorna um objeto SafeInt
do tipo T
.
Operações de comparação entre um SafeInt
e um tipo integral podem ser executadas em qualquer direção. Por exemplo, SafeInt<int>(x) < y
e y> SafeInt<int>(x)
são válidos e retornarão o mesmo resultado.
Muitos operadores binários não são compatíveis com o uso de dois tipos SafeInt
diferentes. Um exemplo disso é o operador &
. Há suporte para SafeInt<T, E> & int
, mas não para SafeInt<T, E> & SafeInt<U, E>
. No último exemplo, o compilador não sabe o tipo de parâmetro a ser retornado. Uma solução para esse problema é converter o segundo parâmetro de volta no tipo base. Usando os mesmos parâmetros, isso pode ser feito com SafeInt<T, E> & (U)SafeInt<U, E>
.
Observação
Para qualquer operação bit a bit, os dois parâmetros diferentes devem ter o mesmo tamanho. Se os tamanhos forem diferentes, o compilador lançará uma exceção ASSERT. Não há garantia de que os resultados dessa operação sejam precisos. Para resolver esse problema, converta o parâmetro menor até que ele tenha o mesmo tamanho do parâmetro maior.
Para operadores de deslocamento, a mudança para mais bits do que existe para o tipo de modelo lançará uma exceção ASSERT. Isso não terá efeito no modo de liberação. A combinação de dois tipos de parâmetros SafeInt é possível para os operadores shift porque o tipo de retorno é o mesmo do tipo original. O número no lado direito do operador indica apenas o número de bits a serem alterados.
Quando você realiza uma comparação lógica com um objeto SafeInt, essa comparação é estritamente aritmética. Por exemplo, considere estas expressões:
SafeInt<uint>((uint)~0) > -1
((uint)~0) > -1
A primeira instrução é resolvida como true
, mas a segunda instrução é resolvida como false
. A negação bit a bit de 0 é 0xFFFFFFFF. Na segunda instrução, o operador de comparação padrão compara 0xFFFFFFFF a 0xFFFFFFFF e os considera iguais. O operador de comparação da classe SafeInt
percebe que o segundo parâmetro é negativo, enquanto o primeiro não tem sinal. Portanto, apesar de a representação de bits ser idêntica, o operador lógico SafeInt
percebe que o inteiro sem sinal é maior que -1.
Tenha cuidado ao usar a classe SafeInt
junto com o operador ternário ?:
. Considere a seguinte linha de código.
Int x = flag ? SafeInt<unsigned int>(y) : -1;
O compilador converte isso no seguinte:
Int x = flag ? SafeInt<unsigned int>(y) : SafeInt<unsigned int>(-1);
Se flag
for false
, o compilador lançará uma exceção em vez de atribuir o valor de -1 a x
. Portanto, para evitar esse comportamento, o código correto a ser usado é a seguinte linha.
Int x = flag ? (int) SafeInt<unsigned int>(y) : -1;
T
e U
podem receber um tipo booliano, um tipo caractere ou um tipo inteiro. Os tipos inteiros podem ser assinados ou não assinados e ter qualquer tamanho de 8 a 64 bits.
Observação
Embora a classe SafeInt
aceite qualquer tipo inteiro, seu desempenho de execução é melhor com tipos sem sinal.
E
é o mecanismo de tratamento de erro usado por SafeInt
. Dois mecanismos de tratamento de erro são fornecidos com a biblioteca SafeInt. A política padrão é SafeIntErrorPolicy_SafeIntException
, que gera uma exceção SafeIntException Class quando ocorre um erro. A outra política é SafeIntErrorPolicy_InvalidParameter
, que interromperá o programa se ocorrer um erro.
Existem duas opções para personalizar a política de erro. A primeira opção é definir o parâmetro E
quando você cria um SafeInt
. Use essa opção quando quiser alterar a política de tratamento de erro para apenas um SafeInt
. A outra opção é definir _SAFEINT_DEFAULT_ERROR_POLICY como sua classe personalizada de tratamento de erro antes de incluir a biblioteca SafeInt
. Use essa opção quando quiser mudar a política de tratamento de erros padrão para todas as instâncias da classe SafeInt
no seu código.
Observação
Uma classe personalizada que manipula erros da biblioteca SafeInt não deve retornar o controle para o código que chamou o manipulador de erros. Após a chamada do manipulador de erro, o resultado da operação SafeInt
não poderá ser confiável.
Hierarquia de herança
SafeInt
Requisitos
Cabeçalho: SafeInt.hpp
Observação
A última versão dessa biblioteca está localizada em https://github.com/dcleblanc/SafeInt. Clone a biblioteca e inclua SafeInt.hpp para usar a biblioteca SafeInt. Prefira este repositório GitHub a <safeint.h>. é uma versão moderna do <safeint.h> que inclui um pequeno número de correções de bugs, usa recursos modernos do C++ resultando em código mais eficiente e é portátil para qualquer plataforma usando compiladores gcc, clang ou Intel.
Exemplo
#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: nenhum
SafeInt::SafeInt
Constrói um 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] O valor do novo objeto SafeInt
. Este deve ser um parâmetro do tipo T ou U, dependendo do construtor.
b
[in] O valor booliano do novo objeto SafeInt
.
u
[in] Um SafeInt
do tipo U. O novo objeto SafeInt
terá o mesmo valor que u, mas será do tipo T.
U
O tipo de dados armazenados em SafeInt
. Isso pode ser um tipo booliano, caractere ou inteiro. Se for um tipo inteiro, ele poderá ser assinado ou não assinado e ter entre 8 e 64 bits.
Comentários
O parâmetro de entrada para o construtor, i ou u, deve ser um tipo booliano, caractere ou inteiro. Se for outro tipo de parâmetro, a classe SafeInt
chamará static_assert para indicar um parâmetro de entrada inválido.
Os construtores que usam o tipo modelo U
convertem automaticamente o parâmetro de entrada no tipo especificado por T
. A classe SafeInt
converte os dados sem perda de dados. Ela informa o manipulador de erro E
quando não consegue converter os dados no tipo T
sem perda de dados.
Se você criar um SafeInt
de um parâmetro booliano, precisará inicializar o valor imediatamente. Não é possível construir um SafeInt
usando o código SafeInt<bool> sb;
. Isso gerará um erro de compilação.