#include <limits>
#include <type_traits> // is_base_of, is_same, is_floating_point, conditional
#include <algorithm> // max
-#include <cassert>
+#include <istream>
+#include <ostream>
#include <boost/config.hpp>
template<typename T>
constexpr static R return_value(const T & t){
-
constexpr const interval<r_type> t_interval{
checked::cast<R>(base_value(std::numeric_limits<T>::min())),
checked::cast<R>(base_value(std::numeric_limits<T>::max()))
true != static_cast<bool>(r_interval.excludes(t_interval)),
"can't cast from ranges that don't overlap"
);
-
return std::conditional<
static_cast<bool>(r_interval.includes(t_interval)),
exception_not_possible,
return validate_detail<Stored,Min,Max,E>::return_value(t);
}
-template<class Stored, Stored Min, Stored Max, class P, class E>
-template<typename T, T N, class P1, class E1>
-constexpr Stored safe_base<Stored, Min, Max, P, E>::
-validated_cast(const safe_literal_impl<T, N, P1, E1> &) const {
- constexpr const interval<Stored> this_interval{};
- // if static values don't overlap, the program can never function
- static_assert(
- this_interval.includes(N),
- "safe type cannot be constructed from this value"
- );
- return static_cast<Stored>(N);
-}
-
/////////////////////////////////////////////////////////////////
// constructors
-template<class Stored, Stored Min, Stored Max, class P, class E>
- constexpr /*explicit*/ safe_base<Stored, Min, Max, P, E>::safe_base(
- const Stored & rhs,
- skip_validation
-) :
- m_t(rhs)
-{}
-
+// default constructor
template<class Stored, Stored Min, Stored Max, class P, class E>
constexpr /*explicit*/ safe_base<Stored, Min, Max, P, E>::safe_base(){
dispatch<E, safe_numerics_error::uninitialized_value>(
"safe values must be initialized"
);
}
-
-// construct an instance of a safe type
-// from an instance of a convertible underlying type.
+// construct an instance of a safe type from an instance of a convertible underlying type.
template<class Stored, Stored Min, Stored Max, class P, class E>
-template<class T>
constexpr /*explicit*/ safe_base<Stored, Min, Max, P, E>::safe_base(
- const T & t,
- typename std::enable_if<
- is_safe<T>::value,
- bool
- >::type
+ const Stored & rhs,
+ skip_validation
) :
- m_t(validated_cast(t))
+ m_t(rhs)
{}
+// construct an instance from an instance of a convertible underlying type.
template<class Stored, Stored Min, Stored Max, class P, class E>
-template<class T>
-constexpr /*explicit*/ safe_base<Stored, Min, Max, P, E>::safe_base(
- const T & t,
- typename std::enable_if<
- std::is_integral<T>::value,
- bool
- >::type
-) :
+ template<
+ class T,
+ typename std::enable_if<
+ std::is_convertible<T, Stored>::value,
+ bool
+ >::type
+ >
+constexpr /*explicit*/ safe_base<Stored, Min, Max, P, E>::safe_base(const T &t) :
m_t(validated_cast(t))
{}
+// construct an instance of a safe type from a literal value
template<class Stored, Stored Min, Stored Max, class P, class E>
-template<class T, T value>
+template<typename T, T N, class Px, class Ex>
constexpr /*explicit*/ safe_base<Stored, Min, Max, P, E>::safe_base(
-
- const std::integral_constant<T, value> &
+ const safe_literal_impl<T, N, Px, Ex> & t
) :
- m_t(validated_cast(value))
+ m_t(validated_cast(t))
{}
/////////////////////////////////////////////////////////////////
constexpr safe_base<Stored, Min, Max, P, E>::
operator R () const {
// if static values don't overlap, the program can never function
- #if 1
constexpr const interval<R> r_interval;
constexpr const interval<Stored> this_interval(Min, Max);
static_assert(
! r_interval.excludes(this_interval),
"safe type cannot be constructed with this type"
);
- #endif
-
return validate_detail<
R,
std::numeric_limits<R>::min(),
std::numeric_limits<R>::max(),
E
- >::return_value(*this);
-}
-
-// cast to the underlying builtin type from a safe type
-template<class Stored, Stored Min, Stored Max, class P, class E>
-constexpr safe_base<Stored, Min, Max, P, E>::
-operator Stored () const {
- return m_t;
+ >::return_value(m_t);
}
/////////////////////////////////////////////////////////////////
return t_interval - u_interval;
}
- static constexpr const r_type_interval_t r_type_interval = get_r_type_interval();
+ constexpr static const r_type_interval_t r_type_interval = get_r_type_interval();
constexpr static const interval<result_base_type> return_interval{
r_type_interval.l.exception()
return t_interval * u_interval;
}
- static constexpr const r_type_interval_t r_type_interval = get_r_type_interval();
+ constexpr static const r_type_interval_t r_type_interval = get_r_type_interval();
constexpr static const interval<result_base_type> return_interval{
r_type_interval.l.exception()
// if exception possible
using exception_policy = typename common_exception_policy<T, U>::type;
- constexpr static int bits = std::min(
+ constexpr static const int bits = std::min(
std::numeric_limits<std::uintmax_t>::digits,
std::max(std::initializer_list<int>{
std::numeric_limits<result_base_type>::digits,
);
}
- static constexpr const r_type_interval_t r_type_interval = get_r_type_interval();
+ constexpr static const r_type_interval_t r_type_interval = get_r_type_interval();
constexpr static const interval<result_base_type> return_interval{
r_type_interval.l.exception()
// if exception possible
using exception_policy = typename common_exception_policy<T, U>::type;
- constexpr static int bits = std::min(
+ constexpr static const int bits = std::min(
std::numeric_limits<std::uintmax_t>::digits,
std::max(std::initializer_list<int>{
std::numeric_limits<result_base_type>::digits,
);
}
- static constexpr const r_type_interval_t r_type_interval = get_r_type_interval();
+ constexpr static const r_type_interval_t r_type_interval = get_r_type_interval();
constexpr static const interval<result_base_type> return_interval{
r_type_interval.l.exception()
return ! (lhs == rhs);
}
-/////////////////////////////////////////////////////////////////
+/////////////////////////////////////////////////////////////////////////
+/////////////////////////////////////////////////////////////////////////
+// The following operators only make sense when applied to integet types
+
+/////////////////////////////////////////////////////////////////////////
// shift operators
// left shift
return (t_interval << u_interval);
}
- static constexpr const r_type_interval_t r_type_interval = get_r_type_interval();
+ constexpr static const r_type_interval_t r_type_interval = get_r_type_interval();
constexpr static const interval<result_base_type> return_interval{
r_type_interval.l.exception()
return false;
}
- constexpr static auto rl = return_interval.l;
- constexpr static auto ru = return_interval.u;
+ constexpr static const auto rl = return_interval.l;
+ constexpr static const auto ru = return_interval.u;
public:
using type =
// INT13-CPP
// C++ standards document N4618 & 5.8.2
static_assert(
- std::numeric_limits<T>::is_integer, "shifted value must be an integer"
+ boost::safe_numerics::Integer<T>::value,
+ "shifted value must be an integer"
);
static_assert(
- std::numeric_limits<U>::is_integer, "shift amount must be an integer"
+ boost::safe_numerics::Integer<U>::value,
+ "bit shift count must be an integer"
);
return left_shift_result<T, U>::return_value(t, u);
}
return (t_interval() >> u_interval());
}
- static constexpr const r_type_interval_t r_type_interval = get_r_type_interval();
+ constexpr static const r_type_interval_t r_type_interval = get_r_type_interval();
constexpr static const interval<result_base_type> return_interval{
r_type_interval.l.exception()
constexpr operator>>(const T & t, const U & u){
// INT13-CPP
static_assert(
- std::numeric_limits<T>::is_integer, "shifted value must be an integer"
+ boost::safe_numerics::Integer<T>::value,
+ "shifted value must be an integer"
);
static_assert(
- std::numeric_limits<U>::is_integer, "shift amount must be an integer"
+ boost::safe_numerics::Integer<U>::value,
+ "bit shift count must be an integer"
);
return right_shift_result<T, U>::return_value(t, u);
}
using r_type = typename std::make_unsigned<result_base_type>::type;
using r_type_interval_t = interval<r_type>;
-
- #if 0
- // breaks compilation for earlier versions of clant
- constexpr static const r_type_interval_t r_interval{
- r_type(0),
- utility::round_out(
- std::max(
- static_cast<r_type>(base_value(std::numeric_limits<T>::max())),
- static_cast<r_type>(base_value(std::numeric_limits<U>::max()))
- )
- )
- };
- #endif
-
using exception_policy = typename common_exception_policy<T, U>::type;
public:
bitwise_or_result<T, U>
>::type
constexpr operator|(const T & t, const U & u){
+ static_assert(
+ boost::safe_numerics::Integer<T>::value,
+ "bitwise or arguments must be an integers"
+ );
+ static_assert(
+ boost::safe_numerics::Integer<U>::value,
+ "bitwise or arguments must be an integers"
+ );
return bitwise_or_result<T, U>::return_value(t, u);
}
using r_type = typename std::make_unsigned<result_base_type>::type;
using r_type_interval_t = interval<r_type>;
-
- #if 0
- // breaks compilation for earlier versions of clant
- constexpr static const r_type_interval_t r_interval{
- r_type(0),
- utility::round_out(
- std::min(
- static_cast<r_type>(base_value(std::numeric_limits<T>::max())),
- static_cast<r_type>(base_value(std::numeric_limits<U>::max()))
- )
- )
- };
- #endif
using exception_policy = typename common_exception_policy<T, U>::type;
public:
bitwise_and_result<T, U>
>::type
constexpr operator&(const T & t, const U & u){
+ static_assert(
+ boost::safe_numerics::Integer<T>::value,
+ "bitwise and arguments must be an integers"
+ );
+ static_assert(
+ boost::safe_numerics::Integer<U>::value,
+ "bitwise and arguments must be an integers"
+ );
return bitwise_and_result<T, U>::return_value(t, u);
}
using r_type = typename std::make_unsigned<result_base_type>::type;
using r_type_interval_t = interval<r_type>;
-
- #if 0
- // breaks compilation for earlier versions of clant
- constexpr static const r_type_interval_t r_interval{
- r_type(0),
- utility::round_out(
- std::max(
- static_cast<r_type>(base_value(std::numeric_limits<T>::max())),
- static_cast<r_type>(base_value(std::numeric_limits<U>::max()))
- )
- )
- };
- #endif
-
using exception_policy = typename common_exception_policy<T, U>::type;
public:
bitwise_xor_result<T, U>
>::type
constexpr operator^(const T & t, const U & u){
+ static_assert(
+ boost::safe_numerics::Integer<T>::value,
+ "bitwise xor arguments must be an integers"
+ );
+ static_assert(
+ boost::safe_numerics::Integer<U>::value,
+ "bitwise xor arguments must be an integers"
+ );
return bitwise_xor_result<T, U>::return_value(t, u);
}
m_t = validated_cast(x);
}
else{
+ if(std::is_unsigned<T>::value){
+ // reading a negative number into an unsigned variable cannot result in
+ // a correct result. But, C++ reads the absolute value, multiplies
+ // it by -1 and stores the resulting value. This is crazy - but there
+ // it is! Oh, and it doesn't set the failbit. We fix this behavior here
+ is >> std::ws;
+ int x = is.peek();
+ // if the input string starts with a '-', we know its an error
+ if(x == '-'){
+ // set fail bit
+ is.setstate(std::ios_base::failbit);
+ }
+ }
is >> m_t;
- validated_cast(m_t);
- }
- if(is.fail()){
- boost::safe_numerics::dispatch<
- E,
- boost::safe_numerics::safe_numerics_error::domain_error
- >(
- "error in file input"
- );
+ if(is.fail()){
+ boost::safe_numerics::dispatch<
+ E,
+ boost::safe_numerics::safe_numerics_error::domain_error
+ >(
+ "error in file input"
+ );
+ }
+ else
+ validated_cast(m_t);
}
}