#ifndef BOOST_NUMERIC_SAFE_BASE_OPERATIONS_HPP #define BOOST_NUMERIC_SAFE_BASE_OPERATIONS_HPP // Copyright (c) 2012 Robert Ramey // // Distributed under the Boost Software License, Version 1.0. (See // accompanying file LICENSE_1_0.txt or copy at // http://www.boost.org/LICENSE_1_0.txt) #include #include // is_base_of, is_same, is_floating_point, conditional #include // max #include #include #include // lazy_enable_if #include #include #include "checked_integer.hpp" #include "checked_result.hpp" #include "safe_base.hpp" #include "interval.hpp" #include "utility.hpp" namespace boost { namespace safe_numerics { ///////////////////////////////////////////////////////////////// // validation template struct validate_detail { using r_type = checked_result; struct exception_possible { constexpr static R return_value( const T & t ){ // INT08-C const r_type rx = heterogeneous_checked_operation< R, typename base_type::type, dispatch_and_return >::cast(t); const R r = rx.exception() ? static_cast(t) : rx.m_r; return r; } }; struct exception_not_possible { constexpr static R return_value( const T & t ){ return static_cast(base_value(t)); } }; constexpr static R return_value(const T & t){ constexpr const interval t_interval{ checked::cast(base_value(std::numeric_limits::min())), checked::cast(base_value(std::numeric_limits::max())) }; constexpr const interval r_interval{r_type(Min), r_type(Max)}; /* static_assert( true != r_interval.excludes(t_interval), "ranges don't overlap: can't cast" ); */ return std::conditional< static_cast(r_interval.includes(t_interval)), exception_not_possible, exception_possible >::type::return_value(t); } }; template template constexpr Stored safe_base:: validated_cast(const T & t) const { return validate_detail::return_value(t); } template template constexpr Stored safe_base:: validated_cast(const safe_literal_impl &) const { constexpr const interval 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(N); } ///////////////////////////////////////////////////////////////// // constructors template constexpr /*explicit*/ safe_base::safe_base( const Stored & rhs, skip_validation ) : m_t(rhs) {} template constexpr /*explicit*/ safe_base::safe_base(){ dispatch( "safe values must be initialized" ); } // construct an instance of a safe type // from an instance of a convertible underlying type. template template constexpr /*explicit*/ safe_base::safe_base( const T & t, typename std::enable_if< is_safe::value, bool >::type ) : m_t(validated_cast(t)) {} template template constexpr /*explicit*/ safe_base::safe_base( const T & t, typename std::enable_if< std::is_integral::value, bool >::type ) : m_t(validated_cast(t)) {} template template constexpr /*explicit*/ safe_base::safe_base( const std::integral_constant & ) : m_t(validated_cast(value)) {} ///////////////////////////////////////////////////////////////// // casting operators // cast to a builtin type from a safe type template< class Stored, Stored Min, Stored Max, class P, class E> template< class R, typename std::enable_if< ! boost::safe_numerics::is_safe::value, int >::type > constexpr safe_base:: operator R () const { // if static values don't overlap, the program can never function #if 0 constexpr const interval r_interval; constexpr const interval 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::min(), std::numeric_limits::max(), Stored, E >::return_value(m_t); } // cast to the underlying builtin type from a safe type template< class Stored, Stored Min, Stored Max, class P, class E> constexpr safe_base:: operator Stored () const { return m_t; } ///////////////////////////////////////////////////////////////// // binary operators template struct common_exception_policy { static_assert(is_safe::value || is_safe::value, "at least one type must be a safe type" ); using t_exception_policy = typename get_exception_policy::type; using u_exception_policy = typename get_exception_policy::type; static_assert( std::is_same::value || std::is_same::value || std::is_same::value, "if the exception policies are different, one must be void!" ); static_assert( ! (std::is_same::value && std::is_same::value), "at least one exception policy must not be void" ); using type = typename std::conditional< !std::is_same::value, u_exception_policy, typename std::conditional< !std::is_same::value, t_exception_policy, // void >::type >::type; static_assert( !std::is_same::value, "exception_policy is void" ); }; template struct common_promotion_policy { static_assert(is_safe::value || is_safe::value, "at least one type must be a safe type" ); using t_promotion_policy = typename get_promotion_policy::type; using u_promotion_policy = typename get_promotion_policy::type; static_assert( std::is_same::value ||std::is_same::value ||std::is_same::value, "if the promotion policies are different, one must be void!" ); static_assert( ! (std::is_same::value && std::is_same::value), "at least one promotion policy must not be void" ); using type = typename std::conditional< ! std::is_same::value, u_promotion_policy, typename std::conditional< ! std::is_same::value, t_promotion_policy, // void >::type >::type; static_assert( ! std::is_same::value, "promotion_policy is void" ); }; // give the resultant base type, figure out what the final result // type will be. Note we currently need this because we support // return of only safe integer types. Someday ..., we'll support // all other safe types including float and user defined ones. // // helper - cast arguments to binary operators to a specified // result type template std::pair constexpr static casting_helper(const T & t, const U & u){ using r_type = checked_result; const r_type tx = heterogeneous_checked_operation< R, typename base_type::type, dispatch_and_return >::cast(base_value(t)); const R tr = tx.exception() ? static_cast(t) : tx.m_r; const r_type ux = heterogeneous_checked_operation< R, typename base_type::type, dispatch_and_return >::cast(base_value(u)); const R ur = ux.exception() ? static_cast(u) : ux.m_r; return std::pair(tr, ur); } // Note: the following global operators will be found via // argument dependent lookup. ///////////////////////////////////////////////////////////////// // addition template struct addition_result { private: using promotion_policy = typename common_promotion_policy::type; using result_base_type = typename promotion_policy::template addition_result::type; // if exception not possible constexpr static result_base_type return_value(const T & t, const U & u, std::false_type){ return static_cast(base_value(t)) + static_cast(base_value(u)); } // if exception possible using exception_policy = typename common_exception_policy::type; using r_type = checked_result; constexpr static result_base_type return_value(const T & t, const U & u, std::true_type){ const std::pair r = casting_helper< exception_policy, result_base_type >(t, u); const r_type rx = checked_operation< result_base_type, dispatch_and_return >::add(r.first, r.second); return rx.exception() ? r.first + r.second : rx.m_r; } using r_type_interval_t = interval; constexpr static const r_type_interval_t get_r_type_interval(){ constexpr const r_type_interval_t t_interval{ checked::cast(base_value(std::numeric_limits::min())), checked::cast(base_value(std::numeric_limits::max())) }; constexpr const r_type_interval_t u_interval{ checked::cast(base_value(std::numeric_limits::min())), checked::cast(base_value(std::numeric_limits::max())) }; return t_interval + u_interval; } constexpr static const r_type_interval_t r_type_interval = get_r_type_interval(); constexpr static const interval return_interval{ r_type_interval.l.exception() ? std::numeric_limits::min() : static_cast(r_type_interval.l), r_type_interval.u.exception() ? std::numeric_limits::max() : static_cast(r_type_interval.u) }; constexpr static bool exception_possible(){ if(r_type_interval.l.exception()) return true; if(r_type_interval.u.exception()) return true; if(! return_interval.includes(r_type_interval)) return true; return false; } constexpr static auto rl = return_interval.l; constexpr static auto ru = return_interval.u; public: using type = safe_base< result_base_type, rl, ru, promotion_policy, exception_policy >; constexpr static type return_value(const T & t, const U & u){ return type( return_value( t, u, std::integral_constant() ), typename type::skip_validation() ); } }; template typename boost::lazy_enable_if_c< is_safe::value || is_safe::value, addition_result >::type constexpr operator+(const T & t, const U & u){ return addition_result::return_value(t, u); } template typename std::enable_if< is_safe::value || is_safe::value, T >::type constexpr operator+=(T & t, const U & u){ t = static_cast(t + u); return t; } ///////////////////////////////////////////////////////////////// // subtraction template struct subtraction_result { private: using promotion_policy = typename common_promotion_policy::type; using result_base_type = typename promotion_policy::template subtraction_result::type; // if exception not possible constexpr static result_base_type return_value(const T & t, const U & u, std::false_type){ return static_cast(base_value(t)) - static_cast(base_value(u)); } // if exception possible using exception_policy = typename common_exception_policy::type; using r_type = checked_result; constexpr static result_base_type return_value(const T & t, const U & u, std::true_type){ const std::pair r = casting_helper< exception_policy, result_base_type >(t, u); const r_type rx = checked_operation< result_base_type, dispatch_and_return >::subtract(r.first, r.second); return rx.exception() ? r.first + r.second : rx.m_r; } using r_type_interval_t = interval; constexpr static const r_type_interval_t get_r_type_interval(){ constexpr const r_type_interval_t t_interval{ checked::cast(base_value(std::numeric_limits::min())), checked::cast(base_value(std::numeric_limits::max())) }; constexpr const r_type_interval_t u_interval{ checked::cast(base_value(std::numeric_limits::min())), checked::cast(base_value(std::numeric_limits::max())) }; return t_interval - u_interval; } static constexpr const r_type_interval_t r_type_interval = get_r_type_interval(); constexpr static const interval return_interval{ r_type_interval.l.exception() ? std::numeric_limits::min() : static_cast(r_type_interval.l), r_type_interval.u.exception() ? std::numeric_limits::max() : static_cast(r_type_interval.u) }; constexpr static bool exception_possible(){ if(r_type_interval.l.exception()) return true; if(r_type_interval.u.exception()) return true; if(! return_interval.includes(r_type_interval)) return true; return false; } public: constexpr static auto rl = return_interval.l; constexpr static auto ru = return_interval.u; using type = safe_base< result_base_type, rl, ru, promotion_policy, exception_policy >; constexpr static type return_value(const T & t, const U & u){ return type( return_value( t, u, std::integral_constant() ), typename type::skip_validation() ); } }; template typename boost::lazy_enable_if_c< is_safe::value || is_safe::value, subtraction_result >::type constexpr operator-(const T & t, const U & u){ return subtraction_result::return_value(t, u); } template typename std::enable_if< is_safe::value || is_safe::value, T >::type constexpr operator-=(T & t, const U & u){ t = static_cast(t - u); return t; } ///////////////////////////////////////////////////////////////// // multiplication template struct multiplication_result { private: using promotion_policy = typename common_promotion_policy::type; using result_base_type = typename promotion_policy::template multiplication_result::type; // if exception not possible constexpr static result_base_type return_value(const T & t, const U & u, std::false_type){ return static_cast(base_value(t)) * static_cast(base_value(u)); } // if exception possible using exception_policy = typename common_exception_policy::type; using r_type = checked_result; constexpr static result_base_type return_value(const T & t, const U & u, std::true_type){ const std::pair r = casting_helper< exception_policy, result_base_type >(t, u); const r_type rx = checked_operation< result_base_type, dispatch_and_return >::multiply(r.first, r.second); return rx.exception() ? r.first * r.second : rx.m_r; } using r_type_interval_t = interval; constexpr static r_type_interval_t get_r_type_interval(){ constexpr const r_type_interval_t t_interval{ checked::cast(base_value(std::numeric_limits::min())), checked::cast(base_value(std::numeric_limits::max())) }; constexpr const r_type_interval_t u_interval{ checked::cast(base_value(std::numeric_limits::min())), checked::cast(base_value(std::numeric_limits::max())) }; return t_interval * u_interval; } static constexpr const r_type_interval_t r_type_interval = get_r_type_interval(); constexpr static const interval return_interval{ r_type_interval.l.exception() ? std::numeric_limits::min() : static_cast(r_type_interval.l), r_type_interval.u.exception() ? std::numeric_limits::max() : static_cast(r_type_interval.u) }; constexpr static bool exception_possible(){ if(r_type_interval.l.exception()) return true; if(r_type_interval.u.exception()) return true; if(! return_interval.includes(r_type_interval)) return true; return false; } constexpr static auto rl = return_interval.l; constexpr static auto ru = return_interval.u; public: using type = safe_base< result_base_type, rl, ru, promotion_policy, exception_policy >; constexpr static type return_value(const T & t, const U & u){ return type( return_value( t, u, std::integral_constant() ), typename type::skip_validation() ); } }; template typename boost::lazy_enable_if_c< is_safe::value || is_safe::value, multiplication_result >::type constexpr operator*(const T & t, const U & u){ // argument dependent lookup should guarentee that we only get here return multiplication_result::return_value(t, u); } template typename std::enable_if< is_safe::value || is_safe::value, T >::type constexpr operator*=(T & t, const U & u){ t = static_cast(t * u); return t; } ///////////////////////////////////////////////////////////////// // division // key idea here - result will never be larger than T template struct division_result { private: using promotion_policy = typename common_promotion_policy::type; using result_base_type = typename promotion_policy::template division_result::type; // if exception not possible constexpr static result_base_type return_value(const T & t, const U & u, std::false_type){ return static_cast(base_value(t)) / static_cast(base_value(u)); } // if exception possible using exception_policy = typename common_exception_policy::type; constexpr static int bits = std::min( std::numeric_limits::digits, std::max(std::initializer_list{ std::numeric_limits::digits, std::numeric_limits::type>::digits, std::numeric_limits::type>::digits }) + (std::numeric_limits::is_signed ? 1 : 0) ); using r_type = checked_result; constexpr static result_base_type return_value(const T & t, const U & u, std::true_type){ using temp_base = typename std::conditional< std::numeric_limits::is_signed, typename boost::int_t::least, typename boost::uint_t::least >::type; using t_type = checked_result; const std::pair r = casting_helper< exception_policy, temp_base >(t, u); const t_type rx = checked_operation< temp_base, dispatch_and_return >::divide(r.first, r.second); return rx.exception() ? r.first / r.second : rx; } using r_type_interval_t = interval; constexpr static r_type_interval_t t_interval(){ return r_type_interval_t{ checked::cast(base_value(std::numeric_limits::min())), checked::cast(base_value(std::numeric_limits::max())) }; }; constexpr static r_type_interval_t u_interval(){ return r_type_interval_t{ checked::cast(base_value(std::numeric_limits::min())), checked::cast(base_value(std::numeric_limits::max())) }; }; constexpr static r_type_interval_t get_r_type_interval(){ constexpr const r_type_interval_t t = t_interval(); constexpr const r_type_interval_t u = u_interval(); if(u.u < r_type(0) || u.l > r_type(0)) return t / u; return utility::minmax( std::initializer_list { t.l / u.l, t.l / r_type(-1), t.l / r_type(1), t.l / u.u, t.u / u.l, t.u / r_type(-1), t.u / r_type(1), t.u / u.u, } ); } static constexpr const r_type_interval_t r_type_interval = get_r_type_interval(); constexpr static const interval return_interval{ r_type_interval.l.exception() ? std::numeric_limits::min() : static_cast(r_type_interval.l), r_type_interval.u.exception() ? std::numeric_limits::max() : static_cast(r_type_interval.u) }; constexpr static bool exception_possible(){ constexpr const r_type_interval_t ri = get_r_type_interval(); constexpr const r_type_interval_t ui = u_interval(); return static_cast(ui.includes(r_type(0))) || ri.l.exception() || ri.u.exception(); } constexpr static auto rl = return_interval.l; constexpr static auto ru = return_interval.u; public: using type = safe_base< result_base_type, rl, ru, promotion_policy, exception_policy >; constexpr static type return_value(const T & t, const U & u){ return type( return_value( t, u, std::integral_constant() ), typename type::skip_validation() ); } }; template typename boost::lazy_enable_if_c< is_safe::value || is_safe::value, division_result >::type constexpr operator/(const T & t, const U & u){ return division_result::return_value(t, u); } template typename std::enable_if< is_safe::value || is_safe::value, T >::type constexpr operator/=(T & t, const U & u){ t = static_cast(t / u); return t; } ///////////////////////////////////////////////////////////////// // modulus template struct modulus_result { private: using promotion_policy = typename common_promotion_policy::type; using result_base_type = typename promotion_policy::template modulus_result::type; // if exception not possible constexpr static result_base_type return_value(const T & t, const U & u, std::false_type){ return static_cast(base_value(t)) % static_cast(base_value(u)); } // if exception possible using exception_policy = typename common_exception_policy::type; constexpr static int bits = std::min( std::numeric_limits::digits, std::max(std::initializer_list{ std::numeric_limits::digits, std::numeric_limits::type>::digits, std::numeric_limits::type>::digits }) + (std::numeric_limits::is_signed ? 1 : 0) ); using r_type = checked_result; constexpr static result_base_type return_value(const T & t, const U & u, std::true_type){ using temp_base = typename std::conditional< std::numeric_limits::is_signed, typename boost::int_t::least, typename boost::uint_t::least >::type; using t_type = checked_result; const std::pair r = casting_helper< exception_policy, temp_base >(t, u); const t_type rx = checked_operation< temp_base, dispatch_and_return >::modulus(r.first, r.second); return rx.exception() ? r.first % r.second : rx; } using r_type_interval_t = interval; constexpr static const r_type_interval_t t_interval(){ return r_type_interval_t{ checked::cast(base_value(std::numeric_limits::min())), checked::cast(base_value(std::numeric_limits::max())) }; }; constexpr static const r_type_interval_t u_interval(){ return r_type_interval_t{ checked::cast(base_value(std::numeric_limits::min())), checked::cast(base_value(std::numeric_limits::max())) }; }; constexpr static const r_type_interval_t get_r_type_interval(){ constexpr const r_type_interval_t t = t_interval(); constexpr const r_type_interval_t u = u_interval(); if(u.u < r_type(0) || u.l > r_type(0)) return t % u; return utility::minmax( std::initializer_list { t.l % u.l, t.l % r_type(-1), t.l % r_type(1), t.l % u.u, t.u % u.l, t.u % r_type(-1), t.u % r_type(1), t.u % u.u, } ); } static constexpr const r_type_interval_t r_type_interval = get_r_type_interval(); constexpr static const interval return_interval{ r_type_interval.l.exception() ? std::numeric_limits::min() : static_cast(r_type_interval.l), r_type_interval.u.exception() ? std::numeric_limits::max() : static_cast(r_type_interval.u) }; constexpr static bool exception_possible(){ constexpr const r_type_interval_t ri = get_r_type_interval(); constexpr const r_type_interval_t ui = u_interval(); return static_cast(ui.includes(r_type(0))) || ri.l.exception() || ri.u.exception(); } constexpr static auto rl = return_interval.l; constexpr static auto ru = return_interval.u; public: using type = safe_base< result_base_type, rl, ru, promotion_policy, exception_policy >; constexpr static type return_value(const T & t, const U & u){ return type( return_value( t, u, std::integral_constant() ), typename type::skip_validation() ); } }; template typename boost::lazy_enable_if_c< is_safe::value || is_safe::value, modulus_result >::type constexpr operator%(const T & t, const U & u){ // see https://en.wikipedia.org/wiki/Modulo_operation return modulus_result::return_value(t, u); } template typename std::enable_if< is_safe::value || is_safe::value, T >::type constexpr operator%=(T & t, const U & u){ t = static_cast(t % u); return t; } ///////////////////////////////////////////////////////////////// // comparison // less than template struct less_than_result { private: using promotion_policy = typename common_promotion_policy::type; using result_base_type = typename promotion_policy::template comparison_result::type; // if exception not possible constexpr static bool return_value(const T & t, const U & u, std::false_type){ return static_cast(base_value(t)) < static_cast(base_value(u)); } using exception_policy = typename common_exception_policy::type; using r_type = checked_result; // if exception possible constexpr static bool return_value(const T & t, const U & u, std::true_type){ const std::pair r = casting_helper< exception_policy, result_base_type >(t, u); return safe_compare::less_than(r.first, r.second); } using r_type_interval_t = interval; constexpr static bool interval_open(const r_type_interval_t & t){ return t.l.exception() || t.u.exception(); } public: constexpr static bool return_value(const T & t, const U & u){ constexpr const r_type_interval_t t_interval{ checked::cast(base_value(std::numeric_limits::min())), checked::cast(base_value(std::numeric_limits::max())) }; constexpr const r_type_interval_t u_interval{ checked::cast(base_value(std::numeric_limits::min())), checked::cast(base_value(std::numeric_limits::max())) }; if(t_interval < u_interval) return true; if(t_interval > u_interval) return false; constexpr bool exception_possible = interval_open(t_interval) || interval_open(u_interval); return return_value( t, u, std::integral_constant() ); } }; template typename std::enable_if< is_safe::value || is_safe::value, bool >::type constexpr operator<(const T & lhs, const U & rhs) { return less_than_result::return_value(lhs, rhs); } template typename std::enable_if< is_safe::value || is_safe::value, bool >::type constexpr operator>(const T & lhs, const U & rhs) { return rhs < lhs; } template typename std::enable_if< is_safe::value || is_safe::value, bool >::type constexpr operator>=(const T & lhs, const U & rhs) { return ! ( lhs < rhs ); } template typename std::enable_if< is_safe::value || is_safe::value, bool >::type constexpr operator<=(const T & lhs, const U & rhs) { return ! ( lhs > rhs ); } // equal template struct equal_result { private: using promotion_policy = typename common_promotion_policy::type; using result_base_type = typename promotion_policy::template comparison_result::type; // if exception not possible constexpr static bool return_value(const T & t, const U & u, std::false_type){ return static_cast(base_value(t)) == static_cast(base_value(u)); } using exception_policy = typename common_exception_policy::type; using r_type = checked_result; // exception possible constexpr static bool return_value(const T & t, const U & u, std::true_type){ const std::pair r = casting_helper< exception_policy, result_base_type >(t, u); return safe_compare::equal(r.first, r.second); } using r_type_interval = interval; constexpr static bool interval_open(const r_type_interval & t){ return t.l.exception() || t.u.exception(); } public: constexpr static bool return_value(const T & t, const U & u){ constexpr const r_type_interval t_interval{ checked::cast(base_value(std::numeric_limits::min())), checked::cast(base_value(std::numeric_limits::max())) }; constexpr const r_type_interval u_interval{ checked::cast(base_value(std::numeric_limits::min())), checked::cast(base_value(std::numeric_limits::max())) }; if(! intersect(t_interval, u_interval)) return false; constexpr bool exception_possible = interval_open(t_interval) || interval_open(u_interval); return return_value( t, u, std::integral_constant() ); } }; template typename std::enable_if< is_safe::value || is_safe::value, bool >::type constexpr operator==(const T & lhs, const U & rhs) { return equal_result::return_value(lhs, rhs); } template typename std::enable_if< is_safe::value || is_safe::value, bool >::type constexpr operator!=(const T & lhs, const U & rhs) { return ! (lhs == rhs); } ///////////////////////////////////////////////////////////////// // shift operators // left shift template struct left_shift_result { private: using promotion_policy = typename common_promotion_policy::type; using result_base_type = typename promotion_policy::template left_shift_result::type; // if exception not possible constexpr static result_base_type return_value(const T & t, const U & u, std::false_type){ return static_cast(base_value(t)) << static_cast(base_value(u)); } // exception possible using exception_policy = typename common_exception_policy::type; using r_type = checked_result; constexpr static result_base_type return_value(const T & t, const U & u, std::true_type){ const std::pair r = casting_helper< exception_policy, result_base_type >(t, u); const r_type rx = checked_operation< result_base_type, dispatch_and_return >::left_shift(r.first, r.second); return rx.exception() ? r.first << r.second : rx.m_r; } using r_type_interval_t = interval; constexpr static r_type_interval_t get_r_type_interval(){ constexpr const r_type_interval_t t_interval{ checked::cast(base_value(std::numeric_limits::min())), checked::cast(base_value(std::numeric_limits::max())) }; constexpr const r_type_interval_t u_interval{ checked::cast(base_value(std::numeric_limits::min())), checked::cast(base_value(std::numeric_limits::max())) }; return (t_interval << u_interval); } static constexpr const r_type_interval_t r_type_interval = get_r_type_interval(); constexpr static const interval return_interval{ r_type_interval.l.exception() ? std::numeric_limits::min() : static_cast(r_type_interval.l), r_type_interval.u.exception() ? std::numeric_limits::max() : static_cast(r_type_interval.u) }; constexpr static bool exception_possible(){ if(r_type_interval.l.exception()) return true; if(r_type_interval.u.exception()) return true; if(! return_interval.includes(r_type_interval)) return true; return false; } constexpr static auto rl = return_interval.l; constexpr static auto ru = return_interval.u; public: using type = safe_base< result_base_type, rl, ru, promotion_policy, exception_policy >; constexpr static type return_value(const T & t, const U & u){ return type( return_value( t, u, std::integral_constant() ), typename type::skip_validation() ); } }; template typename boost::lazy_enable_if_c< // handle safe << int, int << safe, safe << safe // exclude std::ostream << ... (! std::is_base_of::value) && (is_safe::value || is_safe::value), left_shift_result >::type constexpr operator<<(const T & t, const U & u){ // INT13-CPP // C++ standards document N4618 & 5.8.2 static_assert( std::numeric_limits::is_integer, "shifted value must be an integer" ); static_assert( std::numeric_limits::is_integer, "shift amount must be an integer" ); return left_shift_result::return_value(t, u); } template typename std::enable_if< is_safe::value || is_safe::value, T >::type constexpr operator<<=(T & t, const U & u){ t = static_cast(t << u); return t; } // right shift template struct right_shift_result { using promotion_policy = typename common_promotion_policy::type; using result_base_type = typename promotion_policy::template right_shift_result::type; // if exception not possible constexpr static result_base_type return_value(const T & t, const U & u, std::false_type){ return static_cast(base_value(t)) >> static_cast(base_value(u)); } // exception possible using exception_policy = typename common_exception_policy::type; using r_type = checked_result; constexpr static result_base_type return_value(const T & t, const U & u, std::true_type){ const std::pair r = casting_helper< exception_policy, result_base_type >(t, u); const r_type rx = checked_operation< result_base_type, dispatch_and_return >::right_shift(r.first, r.second); return rx.exception() ? r.first >> r.second : rx.m_r; } using r_type_interval_t = interval; constexpr static r_type_interval_t t_interval(){ return r_type_interval_t( checked::cast(base_value(std::numeric_limits::min())), checked::cast(base_value(std::numeric_limits::max())) ); }; constexpr static r_type_interval_t u_interval(){ return r_type_interval_t( checked::cast(base_value(std::numeric_limits::min())), checked::cast(base_value(std::numeric_limits::max())) ); } constexpr static r_type_interval_t get_r_type_interval(){; return (t_interval() >> u_interval()); } static constexpr const r_type_interval_t r_type_interval = get_r_type_interval(); constexpr static const interval return_interval{ r_type_interval.l.exception() ? std::numeric_limits::min() : static_cast(r_type_interval.l), r_type_interval.u.exception() ? std::numeric_limits::max() : static_cast(r_type_interval.u) }; constexpr static bool exception_possible(){ constexpr const r_type_interval_t ri = r_type_interval; constexpr const r_type_interval_t ti = t_interval(); constexpr const r_type_interval_t ui = u_interval(); return static_cast( // note undesirable coupling with checked::shift right here ! ui.u > checked_result( std::numeric_limits::digits ) || ti.l < checked_result(0) || ui.l < checked_result(0) || ri.l.exception() || ri.u.exception() ); } constexpr static auto rl = return_interval.l; constexpr static auto ru = return_interval.u; public: using type = safe_base< result_base_type, rl, ru, promotion_policy, exception_policy >; constexpr static type return_value(const T & t, const U & u){ return type( return_value( t, u, std::integral_constant() ), typename type::skip_validation() ); } }; template typename boost::lazy_enable_if_c< (! std::is_base_of::value) && (is_safe::value || is_safe::value), right_shift_result >::type constexpr operator>>(const T & t, const U & u){ // INT13-CPP static_assert( std::numeric_limits::is_integer, "shifted value must be an integer" ); static_assert( std::numeric_limits::is_integer, "shift amount must be an integer" ); return right_shift_result::return_value(t, u); } template typename std::enable_if< is_safe::value || is_safe::value, T >::type constexpr operator>>=(T & t, const U & u){ t = static_cast(t >> u); return t; } ///////////////////////////////////////////////////////////////// // bitwise operators // operator | template struct bitwise_or_result { private: using promotion_policy = typename common_promotion_policy::type; using result_base_type = typename promotion_policy::template bitwise_or_result::type; // according to the C++ standard, the bitwise operators are executed as if // the operands are consider a logical array of bits. That is, there is no // sense that these are signed numbers. using r_type = typename std::make_unsigned::type; using r_type_interval_t = interval; #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(base_value(std::numeric_limits::max())), static_cast(base_value(std::numeric_limits::max())) ) ) }; #endif using exception_policy = typename common_exception_policy::type; public: // lazy_enable_if_c depends on this using type = safe_base< result_base_type, //r_interval.l, r_type(0), //r_interval.u, utility::round_out( std::max( static_cast(base_value(std::numeric_limits::max())), static_cast(base_value(std::numeric_limits::max())) ) ), promotion_policy, exception_policy >; constexpr static type return_value(const T & t, const U & u){ return type( static_cast(base_value(t)) | static_cast(base_value(u)), typename type::skip_validation() ); } }; template typename boost::lazy_enable_if_c< is_safe::value || is_safe::value, bitwise_or_result >::type constexpr operator|(const T & t, const U & u){ return bitwise_or_result::return_value(t, u); } template typename std::enable_if< is_safe::value || is_safe::value, T >::type constexpr operator|=(T & t, const U & u){ t = static_cast(t | u); return t; } // operator & template struct bitwise_and_result { private: using promotion_policy = typename common_promotion_policy::type; using result_base_type = typename promotion_policy::template bitwise_and_result::type; // according to the C++ standard, the bitwise operators are executed as if // the operands are consider a logical array of bits. That is, there is no // sense that these are signed numbers. using r_type = typename std::make_unsigned::type; using r_type_interval_t = interval; #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(base_value(std::numeric_limits::max())), static_cast(base_value(std::numeric_limits::max())) ) ) }; #endif using exception_policy = typename common_exception_policy::type; public: // lazy_enable_if_c depends on this using type = safe_base< result_base_type, //r_interval.l, r_type(0), //r_interval.u, utility::round_out( std::min( static_cast(base_value(std::numeric_limits::max())), static_cast(base_value(std::numeric_limits::max())) ) ), promotion_policy, exception_policy >; constexpr static type return_value(const T & t, const U & u){ return type( static_cast(base_value(t)) & static_cast(base_value(u)), typename type::skip_validation() ); } }; template typename boost::lazy_enable_if_c< is_safe::value || is_safe::value, bitwise_and_result >::type constexpr operator&(const T & t, const U & u){ return bitwise_and_result::return_value(t, u); } template typename std::enable_if< is_safe::value || is_safe::value, T >::type constexpr operator&=(T & t, const U & u){ t = static_cast(t & u); return t; } // operator ^ template struct bitwise_xor_result { using promotion_policy = typename common_promotion_policy::type; using result_base_type = typename promotion_policy::template bitwise_xor_result::type; // according to the C++ standard, the bitwise operators are executed as if // the operands are consider a logical array of bits. That is, there is no // sense that these are signed numbers. using r_type = typename std::make_unsigned::type; using r_type_interval_t = interval; #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(base_value(std::numeric_limits::max())), static_cast(base_value(std::numeric_limits::max())) ) ) }; #endif using exception_policy = typename common_exception_policy::type; public: // lazy_enable_if_c depends on this using type = safe_base< result_base_type, //r_interval.l, r_type(0), //r_interval.u, utility::round_out( std::max( static_cast(base_value(std::numeric_limits::max())), static_cast(base_value(std::numeric_limits::max())) ) ), promotion_policy, exception_policy >; constexpr static type return_value(const T & t, const U & u){ return type( static_cast(base_value(t)) ^ static_cast(base_value(u)), typename type::skip_validation() ); } }; template typename boost::lazy_enable_if_c< is_safe::value || is_safe::value, bitwise_xor_result >::type constexpr operator^(const T & t, const U & u){ return bitwise_xor_result::return_value(t, u); } template typename std::enable_if< is_safe::value || is_safe::value, T >::type constexpr operator^=(T & t, const U & u){ t = static_cast(t ^ u); return t; } ///////////////////////////////////////////////////////////////// // stream helpers template< class T, T Min, T Max, class P, // promotion polic class E // exception policy > template< class CharT, class Traits > void safe_base::output( std::basic_ostream & os ) const { os << ( (std::is_same::value || std::is_same::value || std::is_same::value ) ? static_cast(m_t) : m_t ); } template< class T, T Min, T Max, class P, // promotion polic class E // exception policy > template< class CharT, class Traits > void safe_base::input( std::basic_istream & is ){ if(std::is_same::value || std::is_same::value || std::is_same::value ){ int x; is >> x; m_t = validated_cast(x); } else{ 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" ); } } } // safe_numerics } // boost #endif // BOOST_NUMERIC_SAFE_BASE_OPERATIONS_HPP