2009 11 Statyczne asercje w C [Programowanie C C ]


Perełki C++
Statyczne asercje w C++
Aatwe sprawdzanie warunków w czasie kompilacji
Dynamiczne asercje to idiom powszechnie znany i stosowany, statyczne
asercje są zdecydowanie mniej popularne. Jeśli chciałbyś przekonać
się, do czego służy ten ciekawy mechanizm, zapraszam do lektury
niniejszego artykułu.
Kolejną, ważną cechą konstrukcji assert jest
Dowiesz się: Powinieneś wiedzieć: fakt, iż działa ona w czasie wykonania progra-
" Co to jest statyczna asercja w języku C++; " Podstawy programowania w języku C++. mu. W praktyce oznacza to, że warunki opisane
" Jakie biblioteki oferują mechanizm statycz- we wszystkich asercjach będą sprawdzone przez
nej asercji; program dopiero w momencie, kiedy zawiera-
" Jak własnoręcznie zaimplementować me- jący je kod będzie wykonywany. Z tego wzglę-
chanizm statycznej asercji. du makro assert nazywane jest czasami aser-
cją dynamiczną.
No dobrze...  stwierdzi uważny Czytelnik
ku C\C++ jest sprawdzanie, czy dany wskaz-  ale czy zawsze potrzebujemy korzystać z asercji
nik jest niezerowy: dynamicznych? Odpowiedz brzmi: oczywiście,
Poziom że nie! Przecież  myśląc zdroworozsądkowo
trudności assert( p != NULL );  niektóre założenia odnośnie działania progra-
Podobnych warunków może być nieskończe-
Listing 1. Zastosowanie statycznej asercji
nie wiele. Niepisana zasada mówi, że asercje po-
dostępnej w ramach pakietu Boost
echanizm statycznych asercji zna- winny być wykorzystywane tylko i wyłącznie
ny jest już od wielu lat, aczkol- w celu wyłapywania błędów programisty. Na #include
Mwiek  jak wynika z doświad- przykład, jeśli programista zakłada, że wskaz- #include
czeń autora niniejszego tekstu, stosunkowo nik przekazywany do funkcji będzie niezero- template< class UInt >
niewielu programistów stosuje w praktyce wy, lub wartość zwracana z innej funkcji bę- class Test
ten użyteczny idiom. Wydaje się, iż wśród dzie zawsze parzysta, to tego rodzaju niezmien- {
użytkowników języka C++ panuje powszech- niki można weryfikować (i dokumentować) za private:
ne przekonanie, iż statyczne asercje to jakiś pomocą asercji. W przypadku gdy użytkow- BOOST_STATIC_ASSERT(
ezoteryczny mechanizm przeznaczony je- nik wpisał niepoprawne dane lub system ope- ( std::numeric_limits< UInt >::
dynie dla autorów nowoczesnych (tudzież racyjny nie udostępnił programowi żądanego digits >= 16 )
skomplikowanych) bibliotek wykorzystują- zasobu, należy użyć alternatywnych mechani- && std::numeric_limits< UInt >:
cych takie techniki jak metaprogramowanie zmów obsługi błędów, na przykład: rzucić wy- :is_specialized
oparte na szablonach czy programowanie ge- jątek. Opisana wyżej zasada wiąże się poniekąd && std::numeric_limits< UInt >:
neryczne. W niniejszym artykule postaram z rezultatami działania makra assert objawia- :is_integer
się rozwiać ten mit, pokazując, że statyczne jącymi się w przypadku, gdy przekazany do nie- && !std::numeric_limits< UInt
asercje to bardzo prosty, a zarazem użytecz- go warunek nie będzie spełniony. Niespełniona >::is_signed );
ny mechanizm, który z powodzeniem moż- asercja spowoduje brutalne przerwanie progra- public:
na wykorzystać w zwyczajnych, pisanych na mu i wypisanie informacji o błędzie (zazwyczaj /* ... */
co dzień programach. podawana jest nazwa pliku z kodem zródło- };
wym oraz numer linii, w której wystąpił pro-
Listing 2. Przykładowe wykorzystanie
Czym jest statyczna asercja? blem). Oczywistym jest, iż końcowy użytkow-
mechanizmu standardowej statycznej asercji
Mechanizmu asercji nie trzeba chyba przed- nik nie chciałby zobaczyć takiego komunikatu
stawiać nikomu, kto miał do czynienia z ję- w sytuacji gdy np. wpisze niepoprawnie sfor- template < class T >
zykiem C bądz C++. Makro assert to bar- matowany adres e-mail. class Test
dzo proste, a jednocześnie potężne narzę- Makro assert ma pewną ważną właściwość: {
dzie programistyczne, służące do weryfi- jest ono skonstruowane w taki sposób, że dzia- private:
kacji niezmienników w kodzie zródłowym. ła tylko wtedy, gdy program jest zbudowany static_assert( sizeof( int ) <=
Poprzez niezmiennik należy rozumieć do- w trybie odpluskwiania (ang. debug mode). W sizeof( T ),
wolny warunek, który w mniemaniu autora programach zbudowanych w trybie produk- "T is not big
programu powinien być w określonym kon- cyjnym (ang. release mode) instrukcje assert są enough!" );
tekście spełniony. Jednym z najbardziej po- pominięte i nie powodują żadnego dodatkowe- };
pularnych zastosowań makra assert w języ- go narzutu.
48 11/2009
Statyczne asercje w C++
mu znane są już na etapie kompilacji. Powstaje zapisaną na co najmniej 16 bitach (nie licząc bi- zewnętrznych rozwiązań. W takim przypadku
więc pytanie: czy nie dałoby się sprawdzać tych tu reprezentującego znak). pozostaje jedynie zakasać rękawy i zaimplemen-
warunków właśnie wtedy? Warto w tym miejscu wspomnieć, iż makro tować mechanizm statycznej asercji własnoręcz-
Kluczem do znalezienia odpowiedzi na te py- BOOST_STATIC_ASSERT może być użyte zarów- nie. Wbrew pozorom, nie wymaga to wiele wy-
tania są właśnie statyczne asercje. Mechanizm ten no w zakresie przestrzeni nazw, funkcji, szablo- siłku: przykładowa implementacja (którą z po-
działa bardzo podobnie do swojego dynamicz- nu funkcji, klasy oraz szablonu klasy. wodzeniem można nazwać rozwiązaniem pro-
nego odpowiednika, tyle że działa na etapie Alternatywą dla zestawu bibliotek Boost (w dukcyjnym) pokazana jest na Listingu 3.
kompilacji programu. kontekście statycznych asercji) może być biblio- Treść wspomnianego Listingu powinna być
Zaraz, zaraz...  zastanowi się ponownie uważ- teka Loki (patrz: ramka W Sieci). Biblioteka ta zapisana w pliku nagłówkowym (np. StaticAs-
ny Czytelnik. Ale które warunki można weryfiko- związana jest z książką autorstwa Andrei Ale- sert.hpp); w takiej postaci będzie gotowa do
wać za pomocą statycznych asercji? Pytanie to jest xandrescu pt. Nowoczesne Projektowanie w C++ użycia. Nasza implementacja statycznej aser-
niewątpliwie bardzo ważne. Odpowiedz brzmi: (tytuł angielski: Modern C++ Design). Pozycja cji opiera się na specjalizacjach szablonu kla-
statyczne asercje obsługują tylko te warunki, któ- ta, uznawana w kręgu programistów C++ za kla- sy StaticAsserter. Szablon ten przyjmuje je-
re są zbudowane z tzw. całkowitych wyrażeń sta- sykę, opisuje szczegółowo szereg nowoczesnych den parametr typu bool i zawiera jedynie pu-
łych (ang. integral constant expressions). Wyrażenia technik języka C++: między innymi statyczne
te mogą składać się z następujących składników: asercje. Zaprezentowane w książce mechanizmy
Listing 3. Implementacja mechanizmu
zaimplementowane są w towarzyszącej bibliote-
statycznej asercji
" literały całkowite; ce: Loki. Statyczna asercja rodem z Loki dostar-
" wartości enumeracji; czona jest w postaci makra LOKI_STATIC_CHECK, #if !defined __STATIC_ASSERT_HPP_
" stałe obiekty, które zostały zainicjowane w zaś ogólny schemat korzystania z niej jest iden- INCLUDED__
miejscu definicji; tyczny jak w przypadku BOOST_STATIC_ASSERT. #define __STATIC_ASSERT_HPP_INCLUDED__
" parametry szablonów będące liczbami cał- Biblioteka Loki zawiera w sobie wiele interesują- #if !defined _DEBUG
kowitymi bądz wartościami enumeracji; cych, wartych uwagi rozwiązań, aczkolwiek nie #define static_assert( expr, msg )
" wyrażenia sizeof; jest ona tak aktywnie rozwijana jak Boost  dla- #else // !defined _DEBUG
" stałe adresy. tego też, jako domyślną implementację mecha- namespace Private
nizmu statycznej asercji, polecam zdecydowa- {
W kontekście naszych rozważań na temat sta- nie Boost.StaticAssert. template< bool > struct
tycznych asercji istotny jest fakt, iż standard języ- Bardzo interesującą alternatywą dla wspo- StaticAsserter
ka C++ narzuca wymóg mówiący o tym, że wy- mnianych wyżej bibliotek jest... standard {
rażenia złożone z całkowitych wyrażeń stałych C++0x. W planowanej, nowej odsłonie języ- StaticAsserter( ... ) {}
muszą być ewaluowane na etapie kompilacji pro- ka C++ jego projektanci przewidzieli statycz- };
gramu. Dzięki temu statyczne asercje mogą dzia- ne asercje jako wbudowany mechanizm języka template<> struct StaticAsserter<
łać. Tyle teorii. Czas na odrobinę praktyki. w postaci słowa kluczowego static_assert. false >;
Składnia standardowej statycznej asercji wyglą- }
Dostępne implementacje da następująco: #define static_assert( expr, msg ) \
mechanizmu statycznych asercji {\
Wiemy już, czym jest w teorii statyczna asercja, static_assert( stałe_wyrażenie, komunikat_ class ERROR_##msg {}; \
czas wypróbować to narzędzie w działaniu. Za- błędu ); Private::StaticAsserter< ( expr )
nim jednak drogi Czytelniku zaczniesz zasta- != 0 >( ( ERROR_##msg ) ); \
nawiać się, jak można zaimplementować opisa- Przykładowe wykorzystanie tego mechanizmu }
ny wyżej mechanizm, wstrzymaj się przez mo- przedstawione jest na Listingu 2. #endif // !defined _DEBUG
ment  najprawdopodobniej zamiast tego bę- Na nowy standard języka C++ przyjdzie nam #endif // __STATIC_ASSERT_HPP_
dziesz mógł skorzystać z gotowej, wypróbowanej zapewne jeszcze trochę poczekać, aczkolwiek INCLUDED__
implementacji statycznej asercji. W niniejszym zadowalające jest to, że twórcy kompilatorów nie
Listing 4. Test prezentowanej implementacji
podrozdziale przedstawię dostępne alternatywy zasypują gruszek w popiele i już dziś, w najnow-
mechanizmu statycznej asercji
w tym zakresie. szych wersjach swoich produktów, oferują pro-
Implementacja, do której stosowania będę gramistom wybrane właściwości C++0x. W mo- #define _DEBUG
Cię namawiał w pierwszej kolejności, stanowi mencie pisania niniejszego artykułu standardo- #include "StaticAssert.hpp"
część zestawu bibliotek języka C++ znanych ja- wy mechanizm statycznych asercji dostępny jest int main()
ko Boost (patrz: ramka W Sieci). Boost to narzę- w Visual Studio 2010 CTP oraz w GCC (wersja {
dzie wypróbowane i dobrze udokumentowane 4.3 oraz wyższe). Odnośniki do wspomnianych const int a = 63;
 korzystając z niego, masz gwarancję wysokiej narzędzi można znalezć w ramce W Sieci. Zain- const int b = -1;
jakości, przenośności oraz niezawodności kodu. teresowanych Czytelników gorąco namawiam static_assert( ( a % 2 ) == 0,
Mechanizm statycznej asercji jest dostępny w ra- do eksperymentów z tymi kompilatorami. A_is_not_
mach biblioteki Boost.StaticAssert (patrz: ram- dividable_by_2
ka W Sieci) pod postacią makra BOOST_STATIC_ Własna implementacja );
ASSERT. W celu skorzystania z tej biblioteki na- mechanizmu statycznej asercji static_assert( b >= 0,
leży dołączyć nagłówek static_assert.hpp. Na Li- Mogą zdarzyć się przypadki, kiedy programista B_must_be_greater_
stingu 1 przedstawiony jest przykład zastosowa- nie może użyć istniejącej implementacji statycz- or_equal_zero
nia statycznej asercji rodem z Boost. nej asercji. Powody bywają różne: opisane wyżej );
W tym przypadku sprawdzamy, czy para- biblioteki mogą być niedostępne na docelową return 0;
metr UInt w szablonie Test jest typu całkowite- platformę, tudzież polityka firmy realizującej }
go ze znakiem i jest w stanie reprezentować cyfrę dany projekt może nie zezwalać na stosowanie
11/2009 www.sdjournal.org 49
Perełki C++
sty konstruktor, który przyjmuje argumenty do- generowanym przez kompilator GCC pojawi wę, że warunek ten można zweryfikować na
wolnego typu (w tym celu stosujemy wielokro- się informacja: etapie kompilacji mojego programu. W efekcie
pek)  za moment wyjaśnię dlaczego. Specjali- StaticAssertTest.cpp:10: error: `a cannot appear in napisałem kod podobny do tego, który pokaza-
zacja tego szablonu dla wartości parametru rów- a constant-expression ny jest na Listingu 5.
nej false jest tylko zadeklarowana. Szablon jest Oczywiście, poprzestając jedynie na próbach
umieszczony w przestrzeni nazw Private (nie Statyczne asercje: studium zastosowań przekształcania dynamicznych asercji na ich sta-
jest to konieczne, ale ja stosuję taki zabieg jako Wiemy już, co to jest statyczna asercja, jak ją tyczne odpowiedniki, nie uda nam się wykorzy-
wskazówkę mówiącą, że zawarty tutaj kod sta- stosować, a nawet  jak ją zaimplementować. stać pełnego potencjału nowo poznanego na-
nowi szczegół implementacji i nie powinien być Pozostaje ostatnie  chyba najbardziej klu- rzędzia. Aby okiełznać moc statycznych aser-
wykorzystywany przez kod kliencki). czowe pytanie: w jakim celu, tudzież w jakich cji, warto zainteresować się ciekawym mecha-
Rzućmy okiem na definicję makra static_ przypadkach stosować ten mechanizm. W ni- nizmem, służącym do badania typów oraz re-
assert. Na początku definiujemy temporalną niejszym podpunkcie postaram się na to pyta- lacji występujących między nimi. Mowa tutaj
klasę reprezentującą komunikat o błędzie: nie odpowiedzieć. o tzw. cechach typów (ang. type traits). Mecha-
Pierwsza, najbardziej chyba oczywista nizm ten, dostępny w nowoczesnych bibliote-
class ERROR_##msg {}; wskazówka odnośnie stosowania statycznych kach C++ (np. wspomniane wcześniej Boost i
asercji brzmi: za każdym razem, kiedy wsta- Loki), pozwala sprawdzać w czasie kompilacji
W dalszej kolejności tworzona jest instancja wiasz w kodzie dynamiczną asercję, sprawdz, niebanalne cechy typów, np.: czy dany typ jest
szablonu StaticAsserter, przy czym jako pa- czy wyrażenie warunkowe nie jest przypad- typem bazowym dla innego typu, czy posiada
rametr tego szablonu podawane jest wyrażenie kiem całkowitym wyrażeniem stałym. Jeśli konstruktor kopiujący czy może jest wskazni-
( expr ) != 0. expr musi być oczywiście cał- nie, to rozważ, czy nie da się przekształcić go kiem do metody klasy. Czytelników zaintere-
kowitym wyrażeniem stałym, aby kompilator na takie wyrażenie. sowanych możliwościami tego narzędzia odsy-
mógł obliczyć je w czasie kompilacji. Jeśli wy- Pozwolę sobie przytoczyć w tym miejscu przy- łam do dokumentacji biblioteki Boost.TypeTra-
rażenie to jest niezerowe, to kompilator prze- kład z mojej własnej praktyki. Miałem okazję pra- its (patrz: ramka W Sieci). W kontekście niniej-
kształci je na wartość true i użyje podstawo- cować niedawno nad implementacją gry bazują- szego artykułu najbardziej istotne jest to, że ce-
wej definicji szablonu (tej z wielokropkiem w cej na tzw. kafelkach (ang. tiles). Mechanizm ka- chy typów można testować na etapie kompilacji,
konstruktorze). Konstruktor tej klasy przyj- felkowania przy programowaniu gier oznacza, że a co za tym idzie  nakładać na nie ograniczenia
muje... cokolwiek, więc bezproblemowo prze- wybrane elementy sceny gry (np. tło) składane są przy pomocy statycznych asercji!
łknie obiekt zdefiniowanej wcześniej tymcza- przy renderowaniu z ograniczonej liczby elemen- Statyczne asercje będą niewątpliwie nieoce-
sowej klasy. W rezultacie nie wydarzy się nic. tów podstawowych. Element taki, zwany kaflem nioną pomocą dla osób piszących programy nie-
I o to chodzi! Ciekawe rzeczy zaczną się dziać (ang. tile), to zazwyczaj prostokątny fragment ob- zależne od platformy: makro static_assert
w sytuacji, kiedy wyrażenie w warunku bę- razu, który kopiuje się na ekran. W moim przy- nieraz może uratować je przed trudnymi do
dzie równe 0: wtedy kompilator użyje specjali- padku szerokość i wysokość kafla były stałe. Zde- wykrycia błędami wynikającymi z niezgodno-
zacji szablonu klasy StaticAsserter dla para- finiowałem je w mniej więcej następujący sposób: ści reprezentacji typów stosowanych przez róż-
metru równego false. Specjalizacja ta jest nie- ne kompilatory.
zdefiniowana, więc w rezultacie otrzymamy const int k_tileWidth = 32; Reasumując podpunkt o zastosowaniach sta-
komunikat o błędzie, który będzie zawierał nu- const int k_tileHeight = 32; tycznych asercji, można by pokusić się o stwier-
mer linii, w której ten błąd wystąpił (wskazują- dzenie: grunt to zacząć ich używać, a wtedy oka-
cy, gdzie umieściliśmy statyczną asercję) oraz Oprócz tego, przyjąłem założenie, iż wielko- zje do ich stosowania zaczną pojawiać się same.
 nazwę temporalnej klasy (reprezentującą ko- ści te muszą być podzielne przez liczbę 8; na
munikat o błędzie). Voila! Mamy wszystko cze- tym założeniu opierał się algorytm przewijania Podsumowanie
go nam potrzeba! planszy gry. Zacząłem się zastanawiać, jak wy- W powyższym artykule przedstawiłem defini-
Oczywiście, wystarczy zmienić wartości musić spełnienie tego warunku  na wypadek cję mechanizmu statycznych asercji, zaprezen-
stałych a i b , przykładowo na: 64 i 1, a kom- gdybym chciał w przyszłości zmienić rozmiar towałem najpopularniejsze jego implementa-
pilacja przejdzie bezbłędnie. Gdyby zachciało kafelka. Asercja wydała mi się idealnym roz- cje, opisałem implementację własną, zaś na koń-
nam się oszukać kompilator i np. usunąć sło- wiązaniem, szybko jednak zdałem sobie spra- cu  omówiłem krótkie studium użycia tego po-
wo kluczowe const z definicji stałej a, reak- żytecznego narzędzia. Gorąco zachęcam Czytel-
cja obydwu narzędzi będzie natychmiastowa. ników do stosowania zaprezentowanego idiomu
W Sieci
Dla przykładu, w komunikacie o błędzie wy- w celu usprawnienia własnych programów.
" http://www.boost.org/  strona domowa
pakietu bibliotek Boost; RAFAA K0CISZ
Listing 5. Zastosowanie statycznych asercji do
" http://www.boost.org/doc/libs/1_40_0/
Pracuje na stanowisku Dyrektora Technicznego w
wymuszenia odpowiednich rozmiarów kafelka
doc/html/boost_staticassert.html  doku-
firmie Gamelion, wchodzącej w skład Grupy BLStre-
mentacja biblioteki Boost.StaticAssert;
static_assert( ( k_tileWidth % 8 ) am. Rafał specjalizuje się w technologiach związa-
" http://loki-lib.sourceforge.net/  strona
== 0, nych z produkcją oprogramowania na platformy
domowa biblioteki Loki;
Tile_width_is_not_ mobilne, ze szczególnym naciskiem na tworzenie
" ht t ps : //co nn e c t . m i c roso f t . co m/
dividable_by_8 VisualStudio/content/content.aspx?Conte gier. Grupa BLStream powstała, by efektywniej wy-
ntID=9790  strona domowa Visual Stu-
); korzystywać potencjał dwóch szybko rozwijających
dio 2010 CTP;
static_assert( ( k_tileHeight % 8 ) się producentów oprogramowania  BLStream i
" http://gcc.gnu.org/  strona domowa
== 0, Gamelion. Firmy wchodzące w skład grupy specjali-
projektu GCC;
Tile_height_is_not_ zują się w wytwarzaniu oprogramowania dla klien-
" http://www.boost.org/doc/libs/1_40_0/
dividable_by_8 libs/type_traits/doc/html/index.html  do- tów korporacyjnych, w rozwiązaniach mobilnych
kumentacja biblioteki Boost.TypeTraits.
); oraz produkcji i testowaniu gier.
Kontakt z autorem: rafal.kocisz@game-lion.com
50 11/2009


Wyszukiwarka

Podobne podstrony:
2009 11 Klasy cech w programowaniu generycznym [Programowanie C C ]
2009 11 Sprytne wskaźniki [Programowanie C C ]
2009 11 Informatyka śledcza
2009 11 Connected
Meta najnowsza najlepsza 2009 11 26
Eucharystia Orędzie z dnia 27 12 2009, 11 06 2010
2009 11 the Gatekeeper Network Access Control on Wired Networks with Ieee 802 1X
RMZ o upr rat med zmiana 2009 11 64
Tworzenie statycznego gifa w programie GIMP
2009 11 17 arduino basics

więcej podobnych podstron