#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 #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 { template constexpr static R return_value( const T & t ){ // INT08-C const r_type rx = heterogeneous_checked_operation< R, Min, Max, typename base_type::type, dispatch_and_return >::cast(t); return rx; } }; struct exception_not_possible { template constexpr static R return_value( const T & t ){ return static_cast(base_value(t)); } }; template 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 != static_cast(r_interval.excludes(t_interval)), "can't cast from ranges that don't overlap" ); return std::conditional< static_cast(r_interval.includes(t_interval)), exception_not_possible, exception_possible >::type::return_value(t); } }; template template constexpr inline Stored safe_base:: validated_cast(const T & t) const { return validate_detail::return_value(t); } ///////////////////////////////////////////////////////////////// // constructors // default constructor template constexpr inline /*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 constexpr inline /*explicit*/ safe_base::safe_base( const Stored & rhs, skip_validation ) : m_t(rhs) {} // construct an instance from an instance of a convertible underlying type. template template< class T, typename std::enable_if< std::is_convertible::value, bool >::type > constexpr inline /*explicit*/ safe_base::safe_base(const T &t) : m_t(validated_cast(t)) {} // construct an instance of a safe type from a literal value template template constexpr inline /*explicit*/ safe_base::safe_base( const safe_literal_impl & t ) : m_t(validated_cast(t)) {} ///////////////////////////////////////////////////////////////// // 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 inline safe_base:: operator R () const { // if static values don't overlap, the program can never function 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" ); return validate_detail< R, std::numeric_limits::min(), std::numeric_limits::max(), E >::return_value(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 constexpr inline static std::pair casting_helper(const T & t, const U & u){ using r_type = checked_result; const r_type tx = heterogeneous_checked_operation< R, std::numeric_limits::min(), std::numeric_limits::max(), typename base_type::type, dispatch_and_return >::cast(base_value(t)); const R tr = tx.exception() ? static_cast(t) : tx.m_contents.m_r; const r_type ux = heterogeneous_checked_operation< R, std::numeric_limits::min(), std::numeric_limits::max(), typename base_type::type, dispatch_and_return >::cast(base_value(u)); const R ur = ux.exception() ? static_cast(u) : ux.m_contents.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_contents.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 inline 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 inline 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_contents.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; } 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 inline 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 inline 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_contents.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; } 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, multiplication_result >::type constexpr inline 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 inline 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 const 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, } ); } 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(){ 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 inline 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 inline 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 const 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, } ); } 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(){ 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 inline 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 inline 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 inline 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 inline operator>(const T & lhs, const U & rhs) { return rhs < lhs; } template typename std::enable_if< is_safe::value || is_safe::value, bool >::type constexpr inline operator>=(const T & lhs, const U & rhs) { return ! ( lhs < rhs ); } template typename std::enable_if< is_safe::value || is_safe::value, bool >::type constexpr inline 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 inline 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 inline operator!=(const T & lhs, const U & rhs) { return ! (lhs == rhs); } ///////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////// // The following operators only make sense when applied to integet types ///////////////////////////////////////////////////////////////////////// // 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_contents.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); } 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 const auto rl = return_interval.l; constexpr static const 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 inline operator<<(const T & t, const U & u){ // INT13-CPP // C++ standards document N4618 & 5.8.2 static_assert( boost::safe_numerics::Integer::value, "shifted value must be an integer" ); static_assert( boost::safe_numerics::Integer::value, "bit shift count 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 inline 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_contents.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()); } 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(){ 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 inline operator>>(const T & t, const U & u){ // INT13-CPP static_assert( boost::safe_numerics::Integer::value, "shifted value must be an integer" ); static_assert( boost::safe_numerics::Integer::value, "bit shift count 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 inline 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; 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 inline operator|(const T & t, const U & u){ static_assert( boost::safe_numerics::Integer::value, "bitwise or arguments must be an integers" ); static_assert( boost::safe_numerics::Integer::value, "bitwise or arguments must be an integers" ); return bitwise_or_result::return_value(t, u); } template typename std::enable_if< is_safe::value || is_safe::value, T >::type constexpr inline 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; 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 inline operator&(const T & t, const U & u){ static_assert( boost::safe_numerics::Integer::value, "bitwise and arguments must be an integers" ); static_assert( boost::safe_numerics::Integer::value, "bitwise and arguments must be an integers" ); return bitwise_and_result::return_value(t, u); } template typename std::enable_if< is_safe::value || is_safe::value, T >::type constexpr inline 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; 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 inline operator^(const T & t, const U & u){ static_assert( boost::safe_numerics::Integer::value, "bitwise xor arguments must be an integers" ); static_assert( boost::safe_numerics::Integer::value, "bitwise xor arguments must be an integers" ); return bitwise_xor_result::return_value(t, u); } template typename std::enable_if< is_safe::value || is_safe::value, T >::type constexpr inline 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 > inline 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 > inline 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{ if(std::is_unsigned::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; 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); } } } // safe_numerics } // boost #endif // BOOST_NUMERIC_SAFE_BASE_OPERATIONS_HPP