2010-09-12 13 views
6

Me gustaría crear un tipo que sea un valor entero, pero con un rango restringido. Intentar crear una instancia de este tipo con un valor fuera del rango permitido debería causar un error de tiempo de compilación.¿Cómo se puede restringir el rango permitido de un entero con errores de tiempo de compilación?

He encontrado ejemplos que permiten desencadenar errores de tiempo de compilación cuando enumeration value outside those specified is used, pero ninguno que permita un rango restringido de enteros (sin nombres).

¿Esto es posible?

+0

Boost tiene una afirmación estática para las relaciones numéricas: http://www.boost.org/doc/libs/1_37_0/libs/mpl/doc/refmanual/assert-relation.html –

+0

@In silico: intente y publique enlaces a la nueva versión de boost (1.44 al momento de la escritura) :) –

+0

Creo que Clang ya tiene algún tipo de diagnóstico, en tiempo de compilación, cuando se asigna desde un tipo numérico grande a uno más pequeño. Supongo que gcc/visual también lo tendría. ¿Sería suficiente o quieres rangos bien delimitados? –

Respuesta

7

Sí, pero es torpe:

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

las cosas serán mucho más fácil cuando es C++0x con constexpr, static_assert y user defined literals.

+0

Buena idea con la matriz 0 o 1 de tamaño. Usé la técnica regular enable_if. – Puppy

+0

Esto es genial: para evitar una advertencia del compilador sobre la variable assert_in_range sin usar, agregué "= {}" a esa línea. –

+0

@DeadMG, esta es la forma tradicional de hacerlo, solía usar 'enable_if' para su frescura de plantilla, pero me di cuenta de que era excesivo (' static_assert' es una adición bienvenida al lenguaje) – Motti

-2

Lo que estás pidiendo es una característica de Ada, pero no de C++. No creo que pueda restringir un rango de un entero durante el tiempo de compilación.

4

Podría ser capaz de hacer algo similar al combinar macros y la afirmación estática de C++ 0x.

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

Ahora bien, eso es horrible:/Por favor, absténgase de definir macros cuando la situación ciertamente no lo requiera, una función de plantilla sería mucho mejor. –

1

El valor de un entero de tiempo de ejecución solo se puede verificar en tiempo de ejecución, ya que solo existe en tiempo de ejecución, pero si realiza una verificación en tiempo de ejecución de todos los métodos de escritura, puede garantizar su contenido. Puede construir una clase de reemplazo integral regular con las restricciones dadas para eso.

Para enteros constantes, puede utilizar una plantilla para aplicar tal cosa.

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

El intento de crear esta clase con un valor de plantilla que no está dentro del rango causará un fallo de sustitución y un error de tiempo de compilación. Por supuesto, todavía requerirá adorno con operadores y otros para reemplazar int, y si desea garantizar en tiempo de compilación otras operaciones, tendrá que proporcionar funciones estáticas para ellas (hay formas más fáciles de garantizar la aritmética en tiempo de compilación).

0

Bueno, como habrás notado, ya existe una forma de diagnóstico para las enumeraciones.

En general es tosco: es decir, la comprobación es "suelta", pero también podría proporcionar una forma cruda de verificación.

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

Generalmente, puede asignar (sin quejarse) ningún valor entre los valores mínimos y máximos definidos.

De hecho, puede asignar un poco más (creo que gcc funciona con potencias de 2).

Cuestiones relacionadas