SafeInt — Klasa
Rozszerza liczby pierwotne liczb całkowitych, aby zapobiec przepełnieniu liczb całkowitych i umożliwia porównywanie różnych typów liczb całkowitych.
Uwaga
Najnowsza wersja biblioteki SafeInt znajduje się w https://github.com/dcleblanc/SafeIntlokalizacji . Aby użyć biblioteki SafeInt, sklonuj repozytorium i #include "SafeInt.hpp"
Składnia
template<typename T, typename E = _SAFEINT_DEFAULT_ERROR_POLICY>
class SafeInt;
Parametry
T
Typ liczby całkowitej lub parametru logicznego, który SafeInt
zastępuje.
E
Wyliczony typ danych, który definiuje zasady obsługi błędów.
U
Typ liczby całkowitej lub parametru logicznego pomocniczego operandu.
Rhs
[in] Parametr wejściowy reprezentujący wartość po prawej stronie operatora w kilku funkcjach autonomicznych.
i
[in] Parametr wejściowy reprezentujący wartość po prawej stronie operatora w kilku funkcjach autonomicznych.
Bitów
[in] Parametr wejściowy reprezentujący wartość po prawej stronie operatora w kilku funkcjach autonomicznych.
Elementy członkowskie
Konstruktory publiczne
Nazwa/nazwisko | opis |
---|---|
SafeInt::SafeInt | Konstruktor domyślny. |
Operatory przypisania
Nazwisko | Składnia |
---|---|
= | 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() |
Operatory rzutowania
Nazwisko | Składnia |
---|---|
bool | operator bool() throw() |
char | operator char() const |
znak ze znakiem | operator signed char() const |
unsigned char | operator unsigned char() const |
__int16 | operator __int16() const |
niepodpisane __int16 | operator unsigned __int16() const |
__int32 | operator __int32() const |
__int32 bez znaku | operator unsigned __int32() const |
długi | operator long() const |
unsigned long | operator unsigned long() const |
__int64 | operator __int64() const |
niepodpisane __int64 | operator unsigned __int64() const |
wchar_t | operator wchar_t() const |
Operatory porównania
Nazwisko | Składnia |
---|---|
< | 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() |
Operatory arytmetyczne
Nazwisko | Składnia |
---|---|
+ | 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) |
Operatory logiczne
Nazwisko | Składnia |
---|---|
! | 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() |
Uwagi
Klasa SafeInt
chroni przed przepełnieniem liczb całkowitych w operacjach matematycznych. Rozważ na przykład dodanie dwóch 8-bitowych liczb całkowitych: jedna ma wartość 200, a druga ma wartość 100. Prawidłowa operacja matematyczna to 200 + 100 = 300. Jednak ze względu na 8-bitowy limit liczby całkowitej, górny bit zostanie utracony, a kompilator zwróci 44 (300– 28) w wyniku. Każda operacja, która zależy od tego równania matematycznego, spowoduje wygenerowanie nieoczekiwanego zachowania.
Klasa SafeInt
sprawdza, czy występuje przepełnienie arytmetyczne, czy też kod próbuje podzielić przez zero. W obu przypadkach klasa wywołuje program obsługi błędów, aby ostrzec program o potencjalnym problemie.
Ta klasa umożliwia również porównanie dwóch różnych typów liczb całkowitych, o ile są to SafeInt
obiekty. Zazwyczaj podczas porównywania należy najpierw przekonwertować liczby na ten sam typ. Rzutowanie jednej liczby do innego typu często wymaga sprawdzenia, czy nie ma utraty danych.
Tabela Operatory w tym temacie zawiera listę operatorów matematycznych i porównawczych obsługiwanych przez klasę SafeInt
. Większość operatorów matematycznych zwraca SafeInt
obiekt typu T
.
Operacje porównania między typem całkowitym a SafeInt
mogą być wykonywane w obu kierunkach. Na przykład oba SafeInt<int>(x) < y
elementy i y> SafeInt<int>(x)
są prawidłowe i będą zwracać ten sam wynik.
Wiele operatorów binarnych nie obsługuje używania dwóch różnych SafeInt
typów. Jednym z przykładów jest &
operator . SafeInt<T, E> & int
jest obsługiwany, ale SafeInt<T, E> & SafeInt<U, E>
nie. W tym ostatnim przykładzie kompilator nie wie, jaki typ parametru ma być zwracany. Jednym z rozwiązań tego problemu jest rzutowanie drugiego parametru z powrotem do typu podstawowego. Za pomocą tych samych parametrów można to zrobić za SafeInt<T, E> & (U)SafeInt<U, E>
pomocą polecenia .
Uwaga
W przypadku wszystkich operacji bitowych dwa różne parametry powinny mieć ten sam rozmiar. Jeśli rozmiary będą się różnić, kompilator zgłosi wyjątek ASSERT . Wyniki tej operacji nie mogą być dokładne. Aby rozwiązać ten problem, rzutuj mniejszy parametr do momentu, gdy będzie on taki sam jak większy parametr.
W przypadku operatorów przesunięcia przesunięcie większej liczby bitów niż istnieje dla typu szablonu spowoduje zgłoszenie wyjątku ASSERT. Nie będzie to miało żadnego wpływu w trybie wydania. Mieszanie dwóch typów parametrów SafeInt jest możliwe dla operatorów przesunięcia, ponieważ typ zwracany jest taki sam jak oryginalny typ. Liczba po prawej stronie operatora wskazuje tylko liczbę bitów do przesunięcia.
W przypadku porównania logicznego z obiektem SafeInt porównanie jest ściśle arytmetyczne. Rozważmy na przykład następujące wyrażenia:
SafeInt<uint>((uint)~0) > -1
((uint)~0) > -1
Pierwsza instrukcja jest rozpoznawana jako true
, ale druga instrukcja jest rozpoznawana jako false
. Negacja bitowa 0 jest 0xFFFFFFFF. W drugiej instrukcji domyślny operator porównania porównuje 0xFFFFFFFF z 0xFFFFFFFF i uważa je za równe. Operator porównania dla SafeInt
klasy zdaje sobie sprawę, że drugi parametr jest ujemny, podczas gdy pierwszy parametr jest niepodpisany. W związku z tym, mimo że reprezentacja bitowa jest identyczna, operator logiczny zdaje sobie sprawę, SafeInt
że liczba całkowita bez znaku jest większa niż -1.
Należy zachować ostrożność podczas używania SafeInt
klasy razem z operatoremternary ?:
. Rozważ następujący wiersz kodu.
Int x = flag ? SafeInt<unsigned int>(y) : -1;
Kompilator konwertuje go na następujący:
Int x = flag ? SafeInt<unsigned int>(y) : SafeInt<unsigned int>(-1);
Jeśli flag
parametr to false
, kompilator zgłasza wyjątek zamiast przypisywać wartość -1 do x
. W związku z tym, aby uniknąć tego zachowania, prawidłowy kod do użycia jest następujący wiersz.
Int x = flag ? (int) SafeInt<unsigned int>(y) : -1;
T
można U
przypisać typ logiczny, typ znaku lub typ liczby całkowitej. Typy liczb całkowitych mogą być podpisane lub niepodpisane, a dowolny rozmiar z 8 bitów do 64 bitów.
Uwaga
SafeInt
Mimo że klasa akceptuje dowolny rodzaj liczby całkowitej, działa wydajniej z niepodpisanymi typami.
E
to mechanizm obsługi błędów, który SafeInt
używa. Dwie mechanizmy obsługi błędów są dostarczane z biblioteką SafeInt. Domyślne zasady to SafeIntErrorPolicy_SafeIntException
, która zgłasza wyjątek klasy SafeIntException w przypadku wystąpienia błędu. Inne zasady to SafeIntErrorPolicy_InvalidParameter
, która zatrzymuje program w przypadku wystąpienia błędu.
Istnieją dwie opcje dostosowywania zasad błędów. Pierwszą opcją jest ustawienie parametru E
podczas tworzenia elementu SafeInt
. Użyj tej opcji, jeśli chcesz zmienić zasady obsługi błędów tylko dla jednego SafeInt
. Drugą opcją jest zdefiniowanie _SAFEINT_DEFAULT_ERROR_POLICY jako dostosowanej klasy obsługi błędów przed dołączeniem biblioteki SafeInt
. Użyj tej opcji, jeśli chcesz zmienić domyślne zasady obsługi błędów dla wszystkich wystąpień SafeInt
klasy w kodzie.
Uwaga
Niestandardowa klasa, która obsługuje błędy z biblioteki SafeInt, nie powinna zwracać kontroli do kodu, który nazwał procedurę obsługi błędów. Po wywołaniu procedury obsługi błędów wynik SafeInt
operacji nie może być zaufany.
Hierarchia dziedziczenia
SafeInt
Wymagania
Nagłówek: SafeInt.hpp
Uwaga
Najnowsza wersja tej biblioteki znajduje się w https://github.com/dcleblanc/SafeIntlokalizacji . Sklonuj bibliotekę i dołącz plik SafeInt.hpp do korzystania z biblioteki SafeInt. Preferuj to repozytorium GitHub, aby <safeint.h>. jest to nowoczesna wersja <pliku safeint.h> , która zawiera niewielką liczbę poprawek usterek, używa nowoczesnych funkcji języka C++ w celu uzyskania bardziej wydajnego kodu i jest przenośna do dowolnej platformy przy użyciu kompilatorów gcc, clang lub Intel.
Przykład
#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.
}
Przestrzeń nazw: brak
SafeInt::SafeInt
SafeInt
Tworzy obiekt.
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)
Parametry
i
[in] Wartość nowego SafeInt
obiektu. Musi to być parametr typu T lub U, w zależności od konstruktora.
b
[in] Wartość logiczna dla nowego SafeInt
obiektu.
u
[in] Typ SafeInt
U. Nowy SafeInt
obiekt będzie miał taką samą wartość jak u, ale będzie miał typ T.
U
Typ danych przechowywanych w obiekcie SafeInt
. Może to być typ logiczny, znak lub liczba całkowita. Jeśli jest to typ liczb całkowitych, może być podpisany lub niepodpisany i mieć od 8 do 64 bitów.
Uwagi
Parametr wejściowy konstruktora i lub u musi być typem logicznym, znakiem lub liczbą całkowitą. Jeśli jest to inny typ parametru, SafeInt
klasa wywołuje static_assert , aby wskazać nieprawidłowy parametr wejściowy.
Konstruktory używające typu U
szablonu automatycznie konwertują parametr wejściowy na typ określony przez T
. Klasa SafeInt
konwertuje dane bez utraty danych. Zgłasza on program obsługi E
błędów, jeśli nie może przekonwertować danych na typ T
bez utraty danych.
Jeśli tworzysz element SafeInt
na podstawie parametru logicznego, musisz natychmiast zainicjować wartość. Nie można skonstruować elementu SafeInt
przy użyciu kodu SafeInt<bool> sb;
. Spowoduje to wygenerowanie błędu kompilacji.