2010-09-12 2 views
6

Ich möchte einen Typ erstellen, der ein Integer-Wert ist, aber mit einem eingeschränkten Bereich. Der Versuch, eine Instanz dieses Typs mit einem Wert außerhalb des zulässigen Bereichs zu erstellen, sollte einen Fehler bei der Kompilierung verursachen.Wie kann der zulässige Bereich einer Ganzzahl mit Kompilierzeitfehlern eingeschränkt werden?

Ich habe Beispiele gefunden, die kompilieren Zeit Fehler ausgelöst werden können, wenn eine enumeration value outside those specified is used, aber keine, die einen eingeschränkten Bereich von ganzen Zahlen (ohne Namen) zulassen.

Ist das möglich?

+0

Boost hat eine statische Behauptung für numerische Beziehungen: http://www.boost.org/doc/libs/1_37_0/libs/mpl/doc/refmanual/assert-relation.html –

+0

@In silico: Bitte versuchen Sie und post Links zu der neuen Version von Boost (1.44 zum Schreiben) :) –

+0

Ich denke, dass Clang bereits eine Art von Diagnose, zur Kompilierungszeit, wenn von einem großen numerischen Typ zu einem kleineren zuweisen. Ich nehme an, gcc/visual hätte das auch. Wäre es ausreichend oder möchten Sie gut abgegrenzte Bereiche? –

Antwort

7

Ja, aber es ist klobig:

// 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>(); 
} 

Dinge werden viel einfacher sein, wenn C++0x mit constexpr ist out, static_assert und user defined literals.

+0

Schöne Idee mit dem Array 0 oder 1. Ich habe die reguläre Methode enable_if verwendet. – Puppy

+0

Das ist großartig - Um eine Compiler-Warnung über ungenutzte assert_in_range Variable zu verhindern, habe ich "= {}" zu dieser Zeile hinzugefügt. –

+0

@DeadMG, dies ist der traditionelle Weg, um es zu tun, ich verwendet 'enable_if' für seine Vorlage Coolness, aber kam zu erkennen, es ist Overkill (' static_assert' ist eine willkommene Ergänzung der Sprache) – Motti

-2

Was Sie fordern, ist eine Eigenschaft von Ada, aber nicht C++. Ich glaube nicht, dass Sie einen Bereich einer Ganzzahl während der Kompilierungszeit einschränken können.

4

Möglicherweise können Sie etwas Ähnliches tun, indem Sie Makros und die statische Assertion von C++ 0x kombinieren.

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

Nun, das ist scheußlich:/Bitte verzichten Sie darauf, Makros zu definieren, wenn die Situation es nicht erfordert, eine Template-Funktion wäre viel besser. –

1

Der Wert einer Laufzeit-Ganzzahl kann nur zur Laufzeit überprüft werden, da sie nur zur Laufzeit existiert. Wenn Sie jedoch eine Laufzeitprüfung für alle Schreibmethoden durchführen, können Sie deren Inhalt garantieren. Sie können eine reguläre integrale Ersetzungsklasse mit bestimmten Einschränkungen dafür erstellen.

Für konstante ganze Zahlen können Sie eine Vorlage verwenden, um so etwas zu erzwingen.

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); 
    } 
}; 

Der Versuch, diese Klasse mit einem Vorlagenwert zu erstellen, der nicht innerhalb des Bereichs liegt, führt zu einem Ersetzungsfehler und einem Fehler bei der Kompilierung. Natürlich wird es immer noch Schmuck mit Operatoren usw. benötigen, um int zu ersetzen, und wenn Sie andere Operationen kompilieren wollen, müssen Sie statische Funktionen für sie bereitstellen (es gibt einfachere Möglichkeiten, die Arithmetik zur Kompilierzeit zu garantieren).

0

Nun, wie Sie bemerkt haben, gibt es bereits eine Form der Diagnose für Aufzählungen.

Es ist in der Regel grob: dh die Überprüfung ist "lose", könnte aber auch eine rohe Form der Überprüfung bieten.

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

Sie können (ohne Beanstandung) beliebige Werte zwischen den definierten Minimal- und Maximalwerten zuweisen.

Sie können tatsächlich oft ein bisschen mehr zuweisen (ich denke, gcc arbeitet mit Potenzen von 2).

Verwandte Themen