2010-09-12 10 views
6

Chciałbym utworzyć typ, który jest liczbą całkowitą, ale z ograniczonym zakresem. Próba utworzenia wystąpienia tego typu o wartości spoza dozwolonego zakresu powinna spowodować błąd czasu kompilacji.W jaki sposób dozwolony zakres liczby całkowitej może być ograniczony przez błędy czasu kompilacji?

Znalazłem przykłady, które pozwalają na kompilowanie błędów czasu, które mają być wyzwalane, gdy enumeration value outside those specified is used, ale żadne nie pozwalają na ograniczony zakres liczb całkowitych (bez nazw).

Czy to możliwe?

+0

Funkcja Boost ma twierdzenie statyczne dla relacji numerycznych: http://www.boost.org/doc/libs/1_37_0/libs/mpl/doc/refmanual/assert-relation.html –

+0

@In silico: proszę spróbować i opublikować linki do nowej wersji boost (1.44 od pisania) :) –

+0

Myślę, że Clang ma już pewien rodzaj diagnostyki, w czasie kompilacji, kiedy przypisywanie z dużego typu numerycznego do mniejszego. Przypuszczam, że również gcc/visual. Czy to wystarczy, czy chcesz dobrze rozgraniczone zakresy? –

Odpowiedz

7

Tak, ale to niezgrabne:

// Defining as template but the main class can have the range hard-coded 
template <int Min, int Max> 
class limited_int { 
private: 
    limited_int(int i) : value_(i) {} 
    int value_; 
public: 
    template <int Val> // This needs to be a template for compile time errors 
    static limited_int make_limited() { 
     static_assert(Val >= Min && Val <= Max, "Bad! Bad value."); 
     // If you don't have static_assert upgrade your compiler or use: 
     //typedef char assert_in_range[Val >= Min && Val <= Max]; 
     return Val; 
    } 

    int value() const { return value_; } 
}; 

typedef limited_int<0, 9> digit; 
int main(int argc, const char**) 
{ 

    // Error can't create directly (ctor is private) 
    //digit d0 = 5; 

    // OK 
    digit d1 = digit::make_limited<5>(); 

    // Compilation error, out of range (can't create zero sized array) 
    //digit d2 = digit::make_limited<10>(); 

    // Error, can't determine at compile time if argc is in range 
    //digit d3 = digit::make_limited<argc>(); 
} 

Co będzie znacznie łatwiejsze, gdy C++0x jest obecnie z constexpr, static_assert i user defined literals.

+0

Przyjemny pomysł z tablicą o rozmiarze 0 lub 1. Użyłem standardowej techniki enable_if. – Puppy

+0

To jest wspaniałe - Aby zapobiec ostrzeżeniu kompilatora nad nieużywaną zmienną assert_in_range, dodałem do niej "= {}". –

+0

@DeadMG, to jest tradycyjny sposób na to, użyłem 'enable_if' dla jego szablonu coolness ale zdałem sobie sprawę, że to przesada (' static_assert' jest mile widzianym dodatkiem do języka) – Motti

-2

To, o co prosisz, to funkcja Ada, ale nie C++. Nie wierzę, że możesz ograniczyć zakres liczby całkowitej podczas kompilacji.

4

Może być w stanie zrobić coś podobnego poprzez połączenie makr i statycznego assert C++ 0x.

#define SET_CHECK(a,b) { static_assert(b>3 && b<7); a=b; } 
+3

Teraz jest okropnie:/Proszę powstrzymać się od definiowania makr, gdy sytuacja na pewno tego nie wymaga, funkcja szablonu będzie znacznie lepsza. –

1

Wartość całkowita środowiska wykonawczego może być sprawdzana tylko w środowisku wykonawczym, ponieważ istnieje tylko w środowisku wykonawczym, ale jeśli sprawdzisz wszystkie metody zapisu w czasie wykonywania, możesz zagwarantować jej zawartość. Możesz zbudować klasyczną, zastępczą klasę zastępczą z określonymi ograniczeniami.

Dla stałych całkowitych można użyć szablonu do wymuszenia takiej rzeczy.

template<bool cond, typename truetype> struct enable_if { 
}; 
template<typename truetype> struct enable_if<true, truetype> { 
    typedef truetype type; 
}; 
class RestrictedInt { 
    int value; 
    RestrictedInt(int N) 
     : value(N) { 
    } 
public: 
    template<int N> static typename enable_if< (N > lowerbound) && (N < upperbound), RestrictedInt>::type Create() { 
     return RestrictedInt(N); 
    } 
}; 

Próba utworzenia tej klasy z wartością szablonu, która nie mieści się w zakresie, spowoduje niepowodzenie zastąpienia i błąd kompilacji. Oczywiście nadal będzie wymagać ozdoby z operatorami i innymi, aby zastąpić int, a jeśli chcesz, aby czas kompilacji gwarantował inne operacje, będziesz musiał zapewnić im funkcje statyczne (są prostsze sposoby na zapewnienie arytmetycznej kompilacji).

0

Cóż, jak zauważyłeś, istnieje już pewna forma diagnostyki dla wyliczeń.

Generalnie jest to surowe: oznacza to, że sprawdzenie jest "luźne", ale może również zapewnić prostą formę kontroli.

enum Range { Min = 0, Max = 31 }; 

Zazwyczaj można przypisać (bez reklamacji) dowolne wartości między zdefiniowanymi minimalnymi i maksymalnymi wartościami.

Często można przypisać nieco więcej (myślę, że gcc działa z uprawnieniami 2).

Powiązane problemy