Część z nas, korzystając z C++, pewnie pisała podobny kod:
#include <cassert>
int main()
{
assert(sizeof(long long) == 8);
return 0;
}
Ale przecież to można by sprawdzić już w czasie kompilacji - prawda?
W C++0x (w sumie teraz już C++1x, nawet na wiki zmienili ;)) będzie słowo kluczowe static_assert, które pozwoli robić takie rzeczy.
Ale jak sobie z tym poradzić zanim nowy C++ trafi pod strzechy?
Istnieje dyrektywa preprocesora #error, można to spróbować połączyć z #if:
int main()
{
#if sizeof(long long) == 8
#else
#error Static assertion failed.
#endif
return 0;
}
Prezentowane powyżej "rozwiązanie" ma wiele wad:
1. Nie można go zapisać za pomocą prostego #define, potrzeba minimum 3 linijek kodu.
2. Tymi asercjami zajmuje się preprocesor, czyli są one sprawdzane na samym początku procesu kompilacji.
3. Z punktu 2 wynikają wielkie ograniczenia instrukcji #if - np. nie może być wykorzystywany operator sizeof (bo preprocesor nie wie nic o typach).
Czyli jest to słabe rozwiązanie.
Ale można wykorzystać magię C++, czyli szablony i ich konkretyzacje!
template<bool>
struct StaticAssert;
template<>
struct StaticAssert<true> { };
O co tu chodzi? Mamy definicję (bez deklaracji!) szablonowej struktury StaticAssert, parametryzowanej parametrem pozatypowym o typie bool. Mamy też jedną konkretyzację (pustą!) tego szablonu struktury dla parametru o wartości true.
Ale równie ważne jest to, czego nie ma - czyli drugiej konkretyzacji, dla false. Oznacza to, że jeżeli kompilator spróbuje utworzyć obiekt struktury StaticAssert<false>, to zgłoszony zostanie błąd kompilacji - brak przecież deklaracji dla takiej konkretyzacji tego szablonu.
Czyli otrzymujemy pożądaną asercję czasu kompilacji. Teraz tylko ładniej opakować:
#ifdef _DEBUG
#define STATIC_ASSERT(expr) (StaticAssert<(expr)>())
#else
#define STATIC_ASSERT(expr)
#endif
Makro STATIC_ASSERT ułatwia korzystanie z tej konstrukcji, a także gwarantuje, iż pojawi się ona w kodzie tylko w trybie "debug" (przynajmniej w Visual C++, użytkownicy g++ muszą pewnie trochę zmienić ten #if). W StaticAssert<(expr)>() nawiasy otaczające expr są istotne, bez nich operator ">" używany w warunkach byłby błędnie interpretowany.
Voila!
int main()
{
STATIC_ASSERT(sizeof(long long) == 8);
return 0;
}
Niestety to rozwiązanie też nie jest pozbawione wad - musi być wykorzystywane tylko w miejscach, w których można umieszczać instrukcje programu. Nie można napisać np.
template<class T>
struct Check
{
STATIC_CHECK(sizeof(T) > sizeof(char));
};
Ale i tak jest nieźle ;)
Więcej "C++ heavy wizardry" w książce Andreia Alexandrescu "Modern C++ Design".
wtorek, 5 stycznia 2010
Subskrybuj:
Komentarze do posta (Atom)
Brak komentarzy:
Prześlij komentarz