====== Biblioteka Boost Static Assert ======
Biblioteka Boost Static Assert udostępnia pojedyncze
makro o nazwie BOOST_STATIC_ASSERT. Makro to jest
analogiczne do popularnego makra assert ze standardowej
biblioteki języka C z tą jednak podstawową różnicą,
że działa w czasie kompilacji kodu, a nie jego wykonania.
W przypadku, gdy wartość wyrażenia podanego jako argument
makra jest fałszem, BOOST_STATIC_ASSERT powoduje błąd
kompilacji. Należy zaznaczyć, że wartość argumentu makra
musi dać się obliczyć w trakcie kompilacji, żeby makro
zadziało. W związku z tym poniższa konstrukcja zakończy się
błędem kompilacji (i to bynajmniej nie wywołanym intencjonalnie
przez BOOST_STATIC_ASSERT):
void funcWithI() {
int i = 5;
BOOST_STATIC_ASSERT(i > 0); // błąd - wartość i nieznana podczas kompilacji
//...
}
W powyższym kodzie powinno zostać użyte makro assert, gdyź ewidentnie
chcemy sprawdzić wartość dostępną jedynie w czasie działania programu.
Makro BOOST_STATIC_ASSERT można umieścić wszędzie tam, gdzie ,można
umiesczać deklaracje, a więc w przestrzeniach nazw, w klasach, funkcjach.
===== BOOST_STATIC_ASSERT w przestrzeniach nazw =====
BOOST_STATIC_ASSERT w zasięgu przestrzeni nazw jest użyteczne, gdy chcemy
sprawdzić, czy platforma, na której dokonywana jest kompilacja naszego
programu spełnia określone wymagania. Poniżej chcemy zagwarantować, że
platforma, dla której kompilujemy kod ma typ unsigned long int długości co najmniej
32 bitów.
namespace boost_static_assert_space {
BOOST_STATIC_ASSERT(sizeof(unsigned long int) >= 4);
}
===== BOOST_STATIC_ASSERT w zasięgu funkcji =====
Użycie makra z biblioteki Boost jest również (a może nawet bardziej)
przydatne w zasięgu generycznych funkcji, do sprawdzania parametrów szablonu.
Poniżej przykład funkcji liczącej silnię, która akceptuje jedynie liczby
całkowite bez znaku (dla innych silnia jest niezdefiniowana):
template T factorial(T arg) {
BOOST_STATIC_ASSERT(std::numeric_limits::is_integer && !std::numeric_limits::is_signed);
T result = T(1);
if (arg > T(0))
while (arg > T(0)) {
result *= arg;
--arg;
}
return result;
}
Uwaga do powyższego przykładu: Oczywiście możemy np. zaimplementować klasę, która
będzie "udawać" liczbę całkowitą bez znaku o nieograniczonej ilości bitów
(przeciążając odpowiednie operatory) i wyspecjalizować klasę std::numeric_limits,
by poprzez is_integer zwracała true, a przez is_signed false dla naszego nowego typu.
To pozwoli używać powyższej funkcji dla naszych "spreparowanych" liczb.
===== BOOST_STATIC_ASSERT w zasięgu klasy =====
Makra BOOST_STATIC_ASSERT można używać również w zasięgu klasy. Najbardziej przydaje
się to znów w przypadku szablonów, gdyż pozwala upewnić się, że argument szablonu
spełnia stawiane przed nim wymagania, których inaczej nie dałoby się sprawdzić podczas
kompilacji. Poniżej znajduje się przykład klasy implementującej teksturę dla potrzeb
grafiki, gdzie użyto bardzo wydajnych algorytmów, które mają jednak to ograniczenie,
że działają tylko gdy rozmiary tekstury są potęgą liczby 2:
template class SquareOldStyleTexture {
BOOST_STATIC_ASSERT((SIZE & (SIZE - 1)) == 0);
public:
inline unsigned int getSize() { return SIZE; }
};
===== Uwagi końcowe =====
Makro BOOST_STATIC_ASSERT pozwala wychwycić błędne (nizamierzone) użycia
szablonów, bądź mechanizmów zanim objawią się awarią działającej aplikacji.
Ma jednak przynajmniej dwie wady:
* nie można przekazać żadnego komunikatu dotyczącego błędu asercji.
* błąd asercji pojawia się w linii, w której użyto makra, a nie w linii w której występuje deklaracja, bądź wywołanie, które spowodowało ten błąd.
Powyższe dwie rzeczy sprawiają, że BOOST_STATIC_ASSERT jest dosyć niewygodny w użyciu, gdyż czasami może być ciężko zlokalizować bezpośrednią przyczynę błędu kompilacji.
Pełny przykładowy program, pozwalający wywołać również błędy kompilacji w wyniku niespełnionej asercji, a także zawierający niniejsze komentarze znajduje się w pliku {{boost_static_assert.cpp}}.