Classe SafeInt
Estende le primitive Integer per impedire l'overflow di Integer e consente di confrontare tipi Integer diversi.
Nota
La versione più recente della libreria SafeInt si trova in https://github.com/dcleblanc/SafeInt. Per usare la libreria SafeInt, clonare il repository e #include "SafeInt.hpp"
Sintassi
template<typename T, typename E = _SAFEINT_DEFAULT_ERROR_POLICY>
class SafeInt;
Parametri
T
Tipo Integer o parametro booleano sostituito da SafeInt
.
E
Tipo di dati enumerato che definisce i criteri di gestione degli errori.
U
Tipo Integer o parametro booleano per il secondo operando.
rhs
[in] Parametro di input che rappresenta il valore sul lato destro dell'operatore in numerose funzioni autonome.
i
[in] Parametro di input che rappresenta il valore sul lato destro dell'operatore in numerose funzioni autonome.
bits
[in] Parametro di input che rappresenta il valore sul lato destro dell'operatore in numerose funzioni autonome.
Membri
Costruttori pubblici
Nome | Descrizione |
---|---|
SafeInt::SafeInt | Costruttore predefinito. |
Operatori di assegnazione
Nome | Sintassi |
---|---|
= | 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() |
Operatori di cast
Nome | Sintassi |
---|---|
bool | operator bool() throw() |
char | operator char() const |
signed char | operator signed char() const |
char senza segno | 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 |
long senza segno | operator unsigned long() const |
__int64 | operator __int64() const |
unsigned __int64 | operator unsigned __int64() const |
wchar_t | operator wchar_t() const |
Operatori di confronto
Nome | Sintassi |
---|---|
< | 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() |
Operatori aritmetici
Nome | Sintassi |
---|---|
+ | 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) |
Operatori logici
Nome | Sintassi |
---|---|
! | 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() |
Osservazioni:
La classe SafeInt
protegge dall'overflow di Integer nelle operazioni matematiche. Si consideri, ad esempio, la somma di due Integer a 8 bit: uno ha un valore pari a 200 e il secondo un valore pari a 100. L'operazione matematica corretta sarebbe 200 + 100 = 300. Tuttavia, a causa del limite di 8 bit per gli Integer, il bit superiore andrà perso e il compilatore restituirà 44 (300 - 28) come risultato. Qualsiasi operazione che dipende da questa equazione matematica genererà un comportamento imprevisto.
La classe SafeInt
controlla se si verifica un overflow aritmetico o se il codice prova a eseguire una divisione per zero. In entrambi i casi la classe chiama il gestore degli errori per informare il programma del potenziale problema.
Questa classe consente anche di confrontare due tipi Integer diversi, a condizione che siano oggetti SafeInt
. In genere, quando si esegue un confronto, è necessario prima convertire i numeri in modo che siano dello stesso tipo. Il cast di un numero in un altro tipo spesso richiede controlli per assicurarsi che non vi sia alcuna perdita di dati.
La tabella relativa agli operatori in questo argomento elenca gli operatori matematici e di confronto supportati dalla classe SafeInt
. La maggior parte degli operatori matematici restituisce un oggetto SafeInt
di tipo T
.
Le operazioni di confronto tra un oggetto SafeInt
e un tipo di dati integrale possono essere eseguite in entrambe le direzioni. Ad esempio, entrambe le operazioni SafeInt<int>(x) < y
e y> SafeInt<int>(x)
sono valide e restituiscono lo stesso risultato.
Molti operatori binari non supportano l'uso di due tipi diversi SafeInt
. Un esempio è costituito dall'operatore &
. SafeInt<T, E> & int
è supportato, ma SafeInt<T, E> & SafeInt<U, E>
non lo è. Nell'ultimo esempio, il compilatore non sa quale tipo di parametro restituire. Per risolvere questo problema è possibile eseguire il cast del secondo parametro nel tipo di base. Usando gli stessi parametri, questa operazione può essere eseguita con SafeInt<T, E> & (U)SafeInt<U, E>
.
Nota
Per le operazioni bit per bit, i due diversi parametri devono avere le stesse dimensioni. Se le dimensioni sono differenti, il compilatore genererà un'eccezione ASSERT. I risultati di questa operazione non possono essere garantiti per essere accurati. Per risolvere questo problema, eseguire il cast del parametro più piccolo fino a quando non corrisponde alla stessa dimensione del parametro più grande.
Per gli operatori shift, lo spostamento di un numero di bit maggiore di quelli esistenti per il tipo di modello genera un'eccezione ASSERT. Ciò non ha alcun effetto nella modalità di rilascio. La combinazione di due tipi di parametri SafeInt è possibile per gli operatori shift perché il tipo restituito è identico al tipo originale. Il numero a destra dell'operatore indica solo il numero di bit da spostare.
Quando si esegue un confronto logico con un oggetto SafeInt, il confronto è strettamente aritmetico. Prendere ad esempio in considerazione queste espressioni:
SafeInt<uint>((uint)~0) > -1
((uint)~0) > -1
La prima istruzione viene risolta in true
, ma la seconda istruzione viene risolta in false
. La negazione bit per bit di 0 è 0xFFFFFFFF. Nella seconda istruzione l'operatore di confronto predefinito confronta 0xFFFFFFFF e 0xFFFFFFFF e li considera uguali. L'operatore di confronto per la classe SafeInt
rileva che il secondo parametro è negativo, mentre il primo parametro è senza segno. Di conseguenza, anche se la rappresentazione in bit è identica, l'operatore logico SafeInt
determina che il valore intero senza segno è maggiore di -1.
Fare attenzione quando si usa la classe SafeInt
insieme all'operatore ternario ?:
. Considerare la riga di codice seguente.
Int x = flag ? SafeInt<unsigned int>(y) : -1;
Il compilatore la converte in quanto segue:
Int x = flag ? SafeInt<unsigned int>(y) : SafeInt<unsigned int>(-1);
Se flag
è false
, il compilatore genera un'eccezione invece di assegnare il valore -1 a x
. Pertanto, per evitare questo comportamento, il codice corretto da usare è la riga seguente.
Int x = flag ? (int) SafeInt<unsigned int>(y) : -1;
È possibile assegnare a T
e U
un tipo booleano, un tipo di carattere o un tipo Integer. I tipi Integer possono essere con o senza segno e avere qualsiasi dimensione tra 8 bit e 64 bit.
Nota
Anche se la classe SafeInt
accetta qualsiasi tipo di Integer, è più efficace con i tipi senza segno.
E
è il meccanismo di gestione degli errori usato da SafeInt
. Con la libreria SafeInt vengono forniti due meccanismi di gestione degli errori. Il criterio predefinito è SafeIntErrorPolicy_SafeIntException
, che genera un'eccezione della classe SafeIntException quando si verifica un errore. L'altro criterio è SafeIntErrorPolicy_InvalidParameter
, che arresta il programma se si verifica un errore.
Ci sono due opzioni per personalizzare i criteri di gestione degli errori. La prima opzione consiste nell'impostare il parametro E
quando si crea un oggetto SafeInt
. Usare questa opzione quando si vuole modificare il criterio di gestione degli errori per un solo oggetto SafeInt
. L'altra opzione consiste nel definire _SAFEINT_DEFAULT_ERROR_POLICY come classe personalizzata di gestione degli errori prima di includere la libreria SafeInt
. Usare questa opzione quando si vuole modificare il criterio predefinito di gestione degli errori per tutte le istanze della classe SafeInt
nel codice.
Nota
Una classe personalizzata che gestisce gli errori dalla libreria SafeInt non deve restituire il controllo al codice che ha chiamato il gestore degli errori. Dopo aver chiamato il gestore degli errori, il risultato dell'operazione SafeInt
non può essere considerato attendibile.
Gerarchia di ereditarietà
SafeInt
Requisiti
Intestazione: SafeInt.hpp
Nota
La versione più recente di questa libreria è disponibile all'indirizzo https://github.com/dcleblanc/SafeInt. Clonare la libreria e includere SafeInt.hpp per usare la libreria SafeInt. Preferisce questo repository GitHub a <safeint.h>. è una versione moderna di <safeint.h> che include un numero ridotto di correzioni di bug, usa funzionalità moderne di C++ con conseguente maggiore efficienza del codice ed è portabile in qualsiasi piattaforma usando compilatori gcc, clang o Intel.
Esempio
#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.
}
Spazio dei nomi: nessuno
SafeInt::SafeInt
Costruisce un oggetto 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)
Parametri
i
[in] Valore per il nuovo oggetto SafeInt
. Deve trattarsi di un parametro di tipo T o U, a seconda del costruttore.
b
[in] Valore booleano per il nuovo oggetto SafeInt
.
u
[in] Oggetto SafeInt
di tipo U. Il nuovo oggetto SafeInt
avrà lo stesso valore di u, ma sarà di tipo T.
U
Tipo di dati archiviati in SafeInt
. Può trattarsi di un tipo booleano, carattere o Integer. Se si tratta di un tipo integer, può essere firmato o senza segno e può essere compreso tra 8 e 64 bit.
Osservazioni:
Il parametro di input per il costruttore, i o u, deve essere un tipo booleano, carattere o Integer. Se si tratta di un altro tipo di parametro, la SafeInt
classe chiama static_assert per indicare un parametro di input non valido.
I costruttori che usano il tipo di modello U
convertono automaticamente il parametro di input nel tipo specificato da T
. La classe SafeInt
converte i dati senza alcuna perdita di dati. Segnala al gestore E
errori se non riesce a convertire i dati in un tipo T
senza perdita di dati.
Se si crea un oggetto SafeInt
da un parametro booleano, è necessario inizializzare il valore immediatamente. Non è possibile costruire un SafeInt
oggetto usando il codice SafeInt<bool> sb;
. In questo caso, verrà generato un errore di compilazione.