tipos tipo tabla sirve sintaxis rangos que para declaracion datos dato c++ templates

tabla - Limitar el rango de tipos de valores en C++



tipos de variables en c++ y sus rangos (9)

Supongamos que tengo una clase LimitedValue que contiene un valor, y está parametrizada en los tipos int ''min'' y ''max''. Lo usarías como un contenedor para contener valores que solo pueden estar en un cierto rango. Podrías usarlo de la siguiente manera:

LimitedValue< float, 0, 360 > someAngle( 45.0 ); someTrigFunction( someAngle );

para que ''someTrigFunction'' sepa que se garantiza que se le proporcionará una entrada válida (el constructor arrojaría una excepción si el parámetro no es válido).

Sin embargo, la construcción de copias y la asignación están limitadas a tipos exactamente iguales. Me gustaría poder hacer:

LimitedValue< float, 0, 90 > smallAngle( 45.0 ); LimitedValue< float, 0, 360 > anyAngle( smallAngle );

y tener esa operación marcada en tiempo de compilación, por lo que este próximo ejemplo da un error:

LimitedValue< float, -90, 0 > negativeAngle( -45.0 ); LimitedValue< float, 0, 360 > postiveAngle( negativeAngle ); // ERROR!

es posible? ¿Hay alguna forma práctica de hacerlo, o algún ejemplo que se acerque a esto?


Escribí una clase de C ++ que imita la funcionalidad del range de Ada.

Se basa en plantillas, similares a las soluciones proporcionadas aquí.

Si algo así se va a utilizar en un proyecto real, se usará de manera fundamental. Errores sutiles o malentendidos pueden ser desastrosos.

Por lo tanto, a pesar de que es una pequeña biblioteca sin mucho código, en mi opinión la provisión de pruebas unitarias y una filosofía clara de diseño son muy importantes.

No dude en probarlo y dígame si encuentra algún problema.

https://github.com/alkhimey/ConstrainedTypes

http://www.nihamkin.com/2014/09/05/range-constrained-types-in-c++/


Esto es realmente un asunto complejo y lo he abordado por un tiempo ...

Ahora tengo una biblioteca disponible públicamente que te permitirá limitar los puntos flotantes y enteros en tu código para que puedas estar más seguro de que son válidos en todo momento.

No solo puede desactivar los límites en su versión de lanzamiento final y eso significa que los tipos se vuelven prácticamente iguales a los de un typedef .

Defina su tipo como:

typedef controlled_vars::limited_fauto_init<float, 0, 360> angle_t;

Y cuando no defines los indicadores CONTROLLED_VARS_DEBUG y CONTROLLED_VARS_LIMITED , obtienes casi lo mismo que esto:

typedef float angle_t;

Estas clases se generan para que incluyan todos los operadores necesarios para que no sufras demasiado al usarlos. Eso significa que puedes ver tu angle_t casi como un float .

angle_t a; a += 35;

Funcionará como se espera (y throw si a + 35 > 360 ).

http://snapwebsites.org/project/controlled-vars

Sé que esto fue publicado en 2008 ... pero no veo ningún buen enlace a una biblioteca superior que ofrezca esta funcionalidad.


La biblioteca limitada :: entero hace lo que desea (solo para tipos enteros). http://doublewise.net/c++/bounded/

(En aras de una divulgación completa, soy el autor de esta biblioteca)

Difiere de otras bibliotecas que intentan proporcionar "enteros seguros" de una manera significativa: rastrea los límites enteros. Creo que esto se demuestra mejor con el ejemplo:

auto x = bounded::checked_integer<0, 7>(f()); auto y = 7_bi; auto z = x + y; // decltype(z) == bounded::checked_integer<7, 14> static_assert(z >= 7_bi); static_assert(z <= 14_bi);

x es un tipo entero que está entre 0 y 7. y es un tipo entero entre 7 y 7. z es un tipo entero entre 7 y 14. Toda esta información se conoce en tiempo de compilación, por lo que podemos static_assert en él, aunque el valor de z no es una constante de tiempo de compilación.

z = 10_bi; z = x; static_assert(!std::is_assignable<decltype((z)), decltype(0_bi)>::value);

La primera asignación, z = 10_bi , está desmarcada. Esto se debe a que el compilador puede demostrar que 10 cae dentro del rango de z .

La segunda asignación, z = x , verifica que el valor de x esté dentro del rango de z . De lo contrario, arroja una excepción (el comportamiento exacto depende del tipo de entero que use, existen muchas políticas sobre qué hacer).

La tercera línea, static_assert , muestra que es un error de tiempo de compilación asignar desde un tipo que no tiene ninguna superposición. El compilador ya sabe que esto es un error y te detiene.

La biblioteca no convierte implícitamente al tipo subyacente, ya que esto puede causar muchas situaciones en las que intenta evitar algo, pero sucede debido a las conversiones. Permite la conversión explícita.


Me gustaría ofrecer una versión alternativa para la solución de Kasprzol: el enfoque propuesto siempre usa límites de tipo int. Puede obtener más flexibilidad y seguridad tipo con una implementación como esta:

template<typename T, T min, T max> class Bounded { private: T _value; public: Bounded(T value) : _value(min) { if (value <= max && value >= min) { _value = value; } else { // XXX throw your runtime error/exception... } } Bounded(const Bounded<T, min, max>& b) : _value(b._value){ } };

Esto permitirá al verificador de tipos atrapar asignaciones obvias de errores, como:

Bounded<int, 1, 5> b1(1); Bounded<int, 1, 4> b2(b1); // <-- won''t compile: type mismatch

Sin embargo, las relaciones más avanzadas en las que desee comprobar si el rango de una instancia de plantilla se incluye dentro del rango de otra instancia no pueden expresarse en el mecanismo de plantilla de C ++.

Cada especificación limitada se convierte en un nuevo tipo. Por lo tanto, el compilador puede verificar si hay desajustes de tipo. No puede verificar relaciones más avanzadas que puedan existir para esos tipos.


OK, esto es C ++ 11 sin dependencias de Boost.

Todo lo que garantiza el sistema de tipos se verifica en tiempo de compilación, y todo lo demás arroja una excepción.

He añadido unsafe_bounded_cast para conversiones que pueden arrojar, y safe_bounded_cast para conversiones explícitas que son estáticamente correctas (esto es redundante ya que el constructor de copias lo maneja, pero provisto de simetría y expresividad).

Ejemplo de uso

#include "bounded.hpp" int main() { BoundedValue<int, 0, 5> inner(1); BoundedValue<double, 0, 4> outer(2.3); BoundedValue<double, -1, +1> overlap(0.0); inner = outer; // ok: [0,4] contained in [0,5] // overlap = inner; // ^ error: static assertion failed: "conversion disallowed from BoundedValue with higher max" // overlap = safe_bounded_cast<double, -1, +1>(inner); // ^ error: static assertion failed: "conversion disallowed from BoundedValue with higher max" overlap = unsafe_bounded_cast<double, -1, +1>(inner); // ^ compiles but throws: // terminate called after throwing an instance of ''BoundedValueException<int>'' // what(): BoundedValueException: !(-1<=2<=1) - BOUNDED_VALUE_ASSERT at bounded.hpp:56 // Aborted inner = 0; overlap = unsafe_bounded_cast<double, -1, +1>(inner); // ^ ok inner = 7; // terminate called after throwing an instance of ''BoundedValueException<int>'' // what(): BoundedValueException: !(0<=7<=5) - BOUNDED_VALUE_ASSERT at bounded.hpp:75 // Aborted }

Soporte de excepción

Esto es un poco repetitivo, pero da mensajes de excepción bastante legibles como el anterior (el valor real min / max / también se expone, si elige captar el tipo de excepción derivada y puede hacer algo útil con él).

#include <stdexcept> #include <sstream> #define STRINGIZE(x) #x #define STRINGIFY(x) STRINGIZE( x ) // handling for runtime value errors #define BOUNDED_VALUE_ASSERT(MIN, MAX, VAL) / if ((VAL) < (MIN) || (VAL) > (MAX)) { / bounded_value_assert_helper(MIN, MAX, VAL, / "BOUNDED_VALUE_ASSERT at " / __FILE__ ":" STRINGIFY(__LINE__)); / } template <typename T> struct BoundedValueException: public std::range_error { virtual ~BoundedValueException() throw() {} BoundedValueException() = delete; BoundedValueException(BoundedValueException const &other) = default; BoundedValueException(BoundedValueException &&source) = default; BoundedValueException(int min, int max, T val, std::string const& message) : std::range_error(message), minval_(min), maxval_(max), val_(val) { } int const minval_; int const maxval_; T const val_; }; template <typename T> void bounded_value_assert_helper(int min, int max, T val, char const *message = NULL) { std::ostringstream oss; oss << "BoundedValueException: !(" << min << "<=" << val << "<=" << max << ")"; if (message) { oss << " - " << message; } throw BoundedValueException<T>(min, max, val, oss.str()); }

Clase de valor

template <typename T, int Tmin, int Tmax> class BoundedValue { public: typedef T value_type; enum { min_value=Tmin, max_value=Tmax }; typedef BoundedValue<value_type, min_value, max_value> SelfType; // runtime checking constructor: explicit BoundedValue(T runtime_value) : val_(runtime_value) { BOUNDED_VALUE_ASSERT(min_value, max_value, runtime_value); } // compile-time checked constructors: BoundedValue(SelfType const& other) : val_(other) {} BoundedValue(SelfType &&other) : val_(other) {} template <typename otherT, int otherTmin, int otherTmax> BoundedValue(BoundedValue<otherT, otherTmin, otherTmax> const &other) : val_(other) // will just fail if T, otherT not convertible { static_assert(otherTmin >= Tmin, "conversion disallowed from BoundedValue with lower min"); static_assert(otherTmax <= Tmax, "conversion disallowed from BoundedValue with higher max"); } // compile-time checked assignments: BoundedValue& operator= (SelfType const& other) { val_ = other.val_; return *this; } template <typename otherT, int otherTmin, int otherTmax> BoundedValue& operator= (BoundedValue<otherT, otherTmin, otherTmax> const &other) { static_assert(otherTmin >= Tmin, "conversion disallowed from BoundedValue with lower min"); static_assert(otherTmax <= Tmax, "conversion disallowed from BoundedValue with higher max"); val_ = other; // will just fail if T, otherT not convertible return *this; } // run-time checked assignment: BoundedValue& operator= (T const& val) { BOUNDED_VALUE_ASSERT(min_value, max_value, val); val_ = val; return *this; } operator T const& () const { return val_; } private: value_type val_; };

Soporte de Cast

template <typename dstT, int dstMin, int dstMax> struct BoundedCastHelper { typedef BoundedValue<dstT, dstMin, dstMax> return_type; // conversion is checked statically, and always succeeds template <typename srcT, int srcMin, int srcMax> static return_type convert(BoundedValue<srcT, srcMin, srcMax> const& source) { return return_type(source); } // conversion is checked dynamically, and could throw template <typename srcT, int srcMin, int srcMax> static return_type coerce(BoundedValue<srcT, srcMin, srcMax> const& source) { return return_type(static_cast<srcT>(source)); } }; template <typename dstT, int dstMin, int dstMax, typename srcT, int srcMin, int srcMax> auto safe_bounded_cast(BoundedValue<srcT, srcMin, srcMax> const& source) -> BoundedValue<dstT, dstMin, dstMax> { return BoundedCastHelper<dstT, dstMin, dstMax>::convert(source); } template <typename dstT, int dstMin, int dstMax, typename srcT, int srcMin, int srcMax> auto unsafe_bounded_cast(BoundedValue<srcT, srcMin, srcMax> const& source) -> BoundedValue<dstT, dstMin, dstMax> { return BoundedCastHelper<dstT, dstMin, dstMax>::coerce(source); }


Por el momento, eso es imposible de manera portátil debido a las reglas de C ++ sobre cómo se llaman los métodos (y por extensión, los constructores) incluso con argumentos constantes.

En el estándar C ++ 0x, podrías tener un const-expr que permitiera que se produjera ese error.

(Esto supone que desea arrojar un error solo si el valor real es ilegal. Si los rangos no coinciden, puede lograr esto)


Puedes hacerlo usando plantillas; prueba algo como esto:

template< typename T, int min, int max >class LimitedValue { template< int min2, int max2 >LimitedValue( const LimitedValue< T, min2, max2 > &other ) { static_assert( min <= min2, "Parameter minimum must be >= this minimum" ); static_assert( max >= max2, "Parameter maximum must be <= this maximum" ); // logic } // rest of code };


Una cosa para recordar acerca de las plantillas es que cada invocación de un conjunto único de parámetros de plantilla terminará generando una clase "única" para la cual las comparaciones y asignaciones generarán un error de compilación. Puede haber algunos gurús de la metaprogramación que podrían saber cómo evitar esto, pero yo no soy uno de ellos. Mi enfoque sería implementarlos en una clase con verificaciones en tiempo de ejecución y operadores de comparación y asignación sobrecargados.