diff --git a/examples/example1.cpp b/examples/example1.cpp index 0355f7e..2162091 100644 --- a/examples/example1.cpp +++ b/examples/example1.cpp @@ -1,5 +1,5 @@ #include -#include +#include #include #include "../include/safe_integer.hpp" @@ -26,7 +26,7 @@ int main(int argc, const char * argv[]){ std::cout << static_cast(z) << " != " << x + y << std::endl; detected_msg(false); } - catch(...){ + catch(std::exception){ assert(false); // never arrive here } // solution: replace char with safe diff --git a/examples/example2.cpp b/examples/example2.cpp index e084324..0cd02e3 100644 --- a/examples/example2.cpp +++ b/examples/example2.cpp @@ -1,5 +1,5 @@ #include -#include +#include #include #include "../include/safe_integer.hpp" @@ -18,7 +18,7 @@ int main(int argc, const char * argv[]){ std::cout << x << " != " << INT_MAX << " + 1" << std::endl; detected_msg(false); } - catch(...){ + catch(std::exception){ assert(false); // never arrive here } // solution: replace int with safe diff --git a/examples/example3.cpp b/examples/example3.cpp index 6351b5c..d1e3eb3 100644 --- a/examples/example3.cpp +++ b/examples/example3.cpp @@ -1,5 +1,5 @@ #include -#include +#include #include #include "../include/safe_integer.hpp" @@ -18,7 +18,7 @@ int main(int argc, const char * argv[]){ char y = x; detected_msg(false); } - catch(...){ + catch(std::exception){ assert(false); // never arrive here } // solution: replace int with safe and char with safe diff --git a/examples/example4.cpp b/examples/example4.cpp index 9a28759..c98b0f5 100644 --- a/examples/example4.cpp +++ b/examples/example4.cpp @@ -1,5 +1,5 @@ #include -#include +#include #include #include "../include/safe_integer.hpp" @@ -27,7 +27,7 @@ int main(int argc, const char * argv[]){ std::cout << x << " != " << -1; detected_msg(false); } - catch(...){ + catchstd::exception){ assert(false); // never arrive here } // solution: replace unsigned int with safe diff --git a/include/checked.hpp b/include/checked.hpp index 5ff4f68..baac9bf 100644 --- a/include/checked.hpp +++ b/include/checked.hpp @@ -19,7 +19,10 @@ #include // is_integral #include -#include "safe_base.hpp" +#include +#include + +#include "safe_common.hpp" #include "checked_result.hpp" namespace boost { @@ -30,308 +33,216 @@ namespace checked { // layer 0 - implment safe operations for intrinsic integers // Note presumption of twos complement integer arithmetic - //////////////////////////////////////////////////// - // safe comparison on primitive types - namespace detail { - template - struct make_unsigned { - typedef typename boost::mpl::eval_if_c< - std::numeric_limits::is_signed, - typename std::make_unsigned, - typename boost::mpl::identity - >::type type; - }; - // both arguments unsigned or signed - template - struct less_than { - template - SAFE_NUMERIC_CONSTEXPR static bool invoke(const T & t, const U & u){ - return t < u; - } - }; - - // T unsigned, U signed - template<> - struct less_than { - template - SAFE_NUMERIC_CONSTEXPR static bool invoke(const T & t, const U & u){ - return - (u < 0) ? - false - : - less_than::invoke( - t, - static_cast::type &>(u) - ) - ; - } - }; - // T signed, U unsigned - template<> - struct less_than { - template - SAFE_NUMERIC_CONSTEXPR static bool invoke(const T & t, const U & u){ - return - (t < 0) ? - true - : - less_than::invoke( - static_cast::type &>(t), - u - ) - ; - } - }; - } // detail - - template - SAFE_NUMERIC_CONSTEXPR bool less_than(const T & lhs, const U & rhs) { - static_assert(std::is_integral::value, "only intrinsic integers permitted"); - static_assert(std::is_integral::value, "only intrinsic integers permitted"); - return detail::less_than< +//////////////////////////////////////////////////// +// safe comparison on primitive types +namespace detail { + template + struct make_unsigned { + typedef typename boost::mpl::eval_if_c< std::numeric_limits::is_signed, - std::numeric_limits::is_signed - >::template invoke(lhs, rhs); - } - - template - SAFE_NUMERIC_CONSTEXPR bool greater_than_equal(const T & lhs, const U & rhs) { - static_assert(std::is_integral::value, "only intrinsic integers permitted"); - static_assert(std::is_integral::value, "only intrinsic integers permitted"); - return less_than(rhs, lhs); - } - - namespace detail { - // both arguments unsigned or signed - template - struct greater_than { - template - SAFE_NUMERIC_CONSTEXPR static bool invoke(const T & t, const U & u){ - return t > u; - } - }; - - // T unsigned, U signed - template<> - struct greater_than { - template - SAFE_NUMERIC_CONSTEXPR static bool invoke(const T & t, const U & u){ - return - (u < 0) ? - true - : - greater_than::invoke( - t, - static_cast::type &>(u) - ) - ; - } - }; - // T signed, U unsigned - template<> - struct greater_than { - template - SAFE_NUMERIC_CONSTEXPR static bool invoke(const T & t, const U & u){ - return - (t < 0) ? - false - : - greater_than::invoke( - static_cast::type &>(t), - u - ) - ; - } - }; - } // detail - - template - SAFE_NUMERIC_CONSTEXPR bool greater_than(const T & lhs, const U & rhs) { - static_assert(std::is_integral::value, "only intrinsic integers permitted"); - static_assert(std::is_integral::value, "only intrinsic integers permitted"); - return detail::greater_than< - std::numeric_limits::is_signed, - std::numeric_limits::is_signed - >::template invoke(lhs, rhs); - } - - template - SAFE_NUMERIC_CONSTEXPR bool less_than_equal(const T & lhs, const U & rhs) { - static_assert(std::is_integral::value, "only intrinsic integers permitted"); - static_assert(std::is_integral::value, "only intrinsic integers permitted"); - return greater_than(rhs, lhs); - } - - template - SAFE_NUMERIC_CONSTEXPR bool equal(const T & lhs, const U & rhs) { - static_assert(std::is_integral::value, "only intrinsic integers permitted"); - static_assert(std::is_integral::value, "only intrinsic integers permitted"); - return ! less_than(lhs, rhs) && ! greater_than(lhs, rhs); - } - - template - SAFE_NUMERIC_CONSTEXPR bool not_equal(const T & lhs, const U & rhs) { - static_assert(std::is_integral::value, "only intrinsic integers permitted"); - static_assert(std::is_integral::value, "only intrinsic integers permitted"); - return ! equal(lhs, rhs); - } - - //////////////////////////////////////////////////// - // safe casting on primitive types - - namespace detail { - - template - SAFE_NUMERIC_CONSTEXPR checked_result - cast( - const T & t - ){ - return - std::numeric_limits::is_signed ? - // T is signed - std::numeric_limits::is_signed ? - t > std::numeric_limits::max() ? - checked_result( - checked_result::exception_type::range_error, - "converted signed value too large" - ) - : - t < std::numeric_limits::min() ? - checked_result( - checked_result::exception_type::range_error, - "converted signed value too small" - ) - : - checked_result(t) - : // T is unsigned - t > std::numeric_limits::max() ? - checked_result( - checked_result::exception_type::range_error, - "converted unsigned value too large" - ) - : - checked_result(t) - : // std::numeric_limits::is_signed - // T is signed - ! std::numeric_limits::is_signed ? - t > std::numeric_limits::max() ? - checked_result( - checked_result::exception_type::range_error, - "converted unsigned value too large" - ) - : - checked_result(t) - : // T is signed - t < 0 ? - checked_result( - checked_result::exception_type::range_error, - "converted negative value to unsigned" - ) - : - t > std::numeric_limits::max() ? - checked_result( - checked_result::exception_type::range_error, - "converted signed value too large" - ) - : - checked_result(t) - ; - } - - } // detail - - //////////////////////////////////////////////////// - // safe addition on primitive types - - namespace detail { - - // result unsigned - template - typename boost::enable_if< - typename std::is_unsigned, - checked_result - >::type - SAFE_NUMERIC_CONSTEXPR add( - const R & minr, - const R & maxr, - const R t, - const R u - ) { - return - maxr - u < t ? - checked_result( - checked_result::exception_type::overflow_error, - "addition overflow" - ) - : - checked_result(t + u) - ; + typename std::make_unsigned, + typename boost::mpl::identity + >::type type; + }; + // both arguments unsigned or signed + template + struct less_than { + template + SAFE_NUMERIC_CONSTEXPR static bool invoke(const T & t, const U & u){ + return t < u; } + }; - // result signed - template - typename boost::enable_if< - typename std::is_signed, - checked_result - >::type - SAFE_NUMERIC_CONSTEXPR add( - const R & minr, - const R & maxr, - const R t, - const R u - ) { + // T unsigned, U signed + template<> + struct less_than { + template + SAFE_NUMERIC_CONSTEXPR static bool invoke(const T & t, const U & u){ return - ((u > 0) && (t > (maxr - u))) - || ((u < 0) && (t < (minr - u))) ? - checked_result( - checked_result::exception_type::overflow_error, - "addition overflow" - ) + (u < 0) ? + false : - checked_result(t + u) - ; + less_than::invoke( + t, + static_cast::type &>(u) + ) + ; } + }; + // T signed, U unsigned + template<> + struct less_than { + template + SAFE_NUMERIC_CONSTEXPR static bool invoke(const T & t, const U & u){ + return + (t < 0) ? + true + : + less_than::invoke( + static_cast::type &>(t), + u + ) + ; + } + }; +} // detail - } // namespace detail +template +SAFE_NUMERIC_CONSTEXPR bool less_than(const T & lhs, const U & rhs) { + static_assert(std::is_integral::value, "only intrinsic integers permitted"); + static_assert(std::is_integral::value, "only intrinsic integers permitted"); + return detail::less_than< + std::numeric_limits::is_signed, + std::numeric_limits::is_signed + >::template invoke(lhs, rhs); +} - template - SAFE_NUMERIC_CONSTEXPR checked_result add( - const R & minr, - const R & maxr, - const T & t, - const U & u - ) { - static_assert(std::is_integral::value, "only intrinsic integers permitted"); - static_assert(std::is_integral::value, "only intrinsic integers permitted"); - return - detail::cast(t) != checked_result::exception_type::no_exception ? - detail::cast(t) +template +SAFE_NUMERIC_CONSTEXPR bool greater_than_equal(const T & lhs, const U & rhs) { + static_assert(std::is_integral::value, "only intrinsic integers permitted"); + static_assert(std::is_integral::value, "only intrinsic integers permitted"); + return less_than(rhs, lhs); +} + +namespace detail { + // both arguments unsigned or signed + template + struct greater_than { + template + SAFE_NUMERIC_CONSTEXPR static bool invoke(const T & t, const U & u){ + return t > u; + } + }; + + // T unsigned, U signed + template<> + struct greater_than { + template + SAFE_NUMERIC_CONSTEXPR static bool invoke(const T & t, const U & u){ + return + (u < 0) ? + true + : + greater_than::invoke( + t, + static_cast::type &>(u) + ) + ; + } + }; + // T signed, U unsigned + template<> + struct greater_than { + template + SAFE_NUMERIC_CONSTEXPR static bool invoke(const T & t, const U & u){ + return + (t < 0) ? + false + : + greater_than::invoke( + static_cast::type &>(t), + u + ) + ; + } + }; +} // detail + +template +SAFE_NUMERIC_CONSTEXPR bool greater_than(const T & lhs, const U & rhs) { + static_assert(std::is_integral::value, "only intrinsic integers permitted"); + static_assert(std::is_integral::value, "only intrinsic integers permitted"); + return detail::greater_than< + std::numeric_limits::is_signed, + std::numeric_limits::is_signed + >::template invoke(lhs, rhs); +} + +template +SAFE_NUMERIC_CONSTEXPR bool less_than_equal(const T & lhs, const U & rhs) { + static_assert(std::is_integral::value, "only intrinsic integers permitted"); + static_assert(std::is_integral::value, "only intrinsic integers permitted"); + return greater_than(rhs, lhs); +} + +template +SAFE_NUMERIC_CONSTEXPR bool equal(const T & lhs, const U & rhs) { + static_assert(std::is_integral::value, "only intrinsic integers permitted"); + static_assert(std::is_integral::value, "only intrinsic integers permitted"); + return ! less_than(lhs, rhs) && ! greater_than(lhs, rhs); +} + +template +SAFE_NUMERIC_CONSTEXPR bool not_equal(const T & lhs, const U & rhs) { + static_assert(std::is_integral::value, "only intrinsic integers permitted"); + static_assert(std::is_integral::value, "only intrinsic integers permitted"); + return ! equal(lhs, rhs); +} + +//////////////////////////////////////////////////// +// safe casting on primitive types + +template +SAFE_NUMERIC_CONSTEXPR checked_result +cast( + const T & t +) { + return + std::numeric_limits::is_signed ? + // T is signed + std::numeric_limits::is_signed ? + t > std::numeric_limits::max() ? + checked_result( + checked_result::exception_type::range_error, + "converted signed value too large" + ) : - detail::cast(u) != checked_result::exception_type::no_exception ? - detail::cast(u) + t < std::numeric_limits::min() ? + checked_result( + checked_result::exception_type::range_error, + "converted signed value too small" + ) : - detail::add(minr, maxr, t, u) - ; - } + checked_result(t) + : // T is unsigned + t > std::numeric_limits::max() ? + checked_result( + checked_result::exception_type::range_error, + "converted unsigned value too large" + ) + : + checked_result(t) + : // std::numeric_limits::is_signed + // T is signed + ! std::numeric_limits::is_signed ? + t > std::numeric_limits::max() ? + checked_result( + checked_result::exception_type::range_error, + "converted unsigned value too large" + ) + : + checked_result(t) + : // T is signed + t < 0 ? + checked_result( + checked_result::exception_type::range_error, + "converted negative value to unsigned" + ) + : + t > std::numeric_limits::max() ? + checked_result( + checked_result::exception_type::range_error, + "converted signed value too large" + ) + : + checked_result(t) + ; +} - template - SAFE_NUMERIC_CONSTEXPR checked_result add( - const T & t, - const U & u - ) { - return - add( - std::numeric_limits::min(), - std::numeric_limits::max(), - t, - u - ) - ; - } +//////////////////////////////////////////////////// +// safe addition on primitive types - //////////////////////////////////////////////////// - // safe subtraction on primitive types - namespace detail { +namespace detail { // result unsigned template @@ -339,21 +250,20 @@ namespace checked { typename std::is_unsigned, checked_result >::type - SAFE_NUMERIC_CONSTEXPR subtract( + SAFE_NUMERIC_CONSTEXPR add( const R & minr, const R & maxr, const R t, const R u ) { - // INT30-C return - t < u ? + maxr - u < t ? checked_result( checked_result::exception_type::overflow_error, - "subtraction overflow" + "addition overflow" ) : - checked_result(t - u) + checked_result(t + u) ; } @@ -363,405 +273,497 @@ namespace checked { typename std::is_signed, checked_result >::type - SAFE_NUMERIC_CONSTEXPR subtract( + SAFE_NUMERIC_CONSTEXPR add( const R & minr, const R & maxr, const R t, const R u - ) { // INT32-C + ) { return - ((u > 0) && (t < (minr + u))) - || ((u < 0) && (t > (maxr + u))) ? + ((u > 0) && (t > (maxr - u))) + || ((u < 0) && (t < (minr - u))) ? checked_result( checked_result::exception_type::overflow_error, - "subtraction overflow" + "addition overflow" ) : - checked_result(t - u) + checked_result(t + u) ; } - } // namespace detail +} // namespace detail - template - SAFE_NUMERIC_CONSTEXPR checked_result subtract( - const R & minr, - const R & maxr, - const T & t, - const U & u - ) { - static_assert(std::is_integral::value, "only intrinsic integers permitted"); - static_assert(std::is_integral::value, "only intrinsic integers permitted"); - return - detail::cast(t) != checked_result::exception_type::no_exception ? - detail::cast(t) - : - detail::cast(u) != checked_result::exception_type::no_exception ? - detail::cast(u) - : - detail::subtract(minr, maxr, t, u) - ; - } - - template - SAFE_NUMERIC_CONSTEXPR checked_result subtract( - const T & t, - const U & u - ) { - return - subtract( - std::numeric_limits::min(), - std::numeric_limits::max(), - t, - u +template +SAFE_NUMERIC_CONSTEXPR checked_result add( + const R & minr, + const R & maxr, + const T & t, + const U & u +) { + static_assert(std::is_integral::value, "only intrinsic integers permitted"); + static_assert(std::is_integral::value, "only intrinsic integers permitted"); + const checked_result ru = cast(u); + const checked_result rt = cast(t); + return + rt != checked_result::exception_type::no_exception ? + rt + : + ru != checked_result::exception_type::no_exception ? + ru + : + detail::add(minr, maxr, t, u) + ; +} + +template +SAFE_NUMERIC_CONSTEXPR checked_result add( + const T & t, + const U & u +) { + return + add( + std::numeric_limits::min(), + std::numeric_limits::max(), + t, + u + ) + ; +} + +//////////////////////////////////////////////////// +// safe subtraction on primitive types +namespace detail { + +// result unsigned +template +typename boost::enable_if< + typename std::is_unsigned, + checked_result +>::type +SAFE_NUMERIC_CONSTEXPR subtract( + const R & minr, + const R & maxr, + const R t, + const R u +) { + // INT30-C + return + t < u ? + checked_result( + checked_result::exception_type::overflow_error, + "subtraction overflow" ) - ; - } + : + checked_result(t - u) + ; +} - //////////////////////////////////////////////////// - // safe multiplication on unsafe types - namespace detail { +// result signed +template +typename boost::enable_if< + typename std::is_signed, + checked_result +>::type +SAFE_NUMERIC_CONSTEXPR subtract( + const R & minr, + const R & maxr, + const R t, + const R u +) { // INT32-C + return + ((u > 0) && (t < (minr + u))) + || ((u < 0) && (t > (maxr + u))) ? + checked_result( + checked_result::exception_type::overflow_error, + "subtraction overflow" + ) + : + checked_result(t - u) + ; +} - // result unsigned - template - typename boost::enable_if_c< - std::is_unsigned::value && (sizeof(R) <= (sizeof(std::uintmax_t) / 2)), - checked_result - >::type - SAFE_NUMERIC_CONSTEXPR multiply( - const R & minr, - const R & maxr, - const R t, - const R u - ) { - // INT30-C - // fast method using intermediate result guaranteed not to overflow - // todo - replace std::uintmax_t with a size double the size of R - typedef std::uintmax_t i_type; - return +} // namespace detail + +template +SAFE_NUMERIC_CONSTEXPR checked_result subtract( + const R & minr, + const R & maxr, + const T & t, + const U & u +) { + static_assert(std::is_integral::value, "only intrinsic integers permitted"); + static_assert(std::is_integral::value, "only intrinsic integers permitted"); + return + cast(t) != checked_result::exception_type::no_exception ? + cast(t) + : + cast(u) != checked_result::exception_type::no_exception ? + cast(u) + : + detail::subtract(minr, maxr, t, u) + ; +} + +template +SAFE_NUMERIC_CONSTEXPR checked_result subtract( + const T & t, + const U & u +) { + return + subtract( + std::numeric_limits::min(), + std::numeric_limits::max(), + t, + u + ) + ; +} + +//////////////////////////////////////////////////// +// safe multiplication on primitive types + +namespace detail { + +// result unsigned +template +typename boost::enable_if_c< + std::is_unsigned::value && (sizeof(R) <= (sizeof(std::uintmax_t) / 2)), + checked_result +>::type +SAFE_NUMERIC_CONSTEXPR multiply( + const R & minr, + const R & maxr, + const R t, + const R u +) { + // INT30-C + // fast method using intermediate result guaranteed not to overflow + // todo - replace std::uintmax_t with a size double the size of R + typedef std::uintmax_t i_type; + return + static_cast(t) * static_cast(u) + > std::numeric_limits::max() ? + checked_result( + checked_result::exception_type::overflow_error, + "multiplication overflow" + ) + : + checked_result(t * u) + ; +} +template +typename boost::enable_if_c< + std::is_unsigned::value && (sizeof(R) > sizeof(std::uintmax_t) / 2), + checked_result +>::type +SAFE_NUMERIC_CONSTEXPR multiply( + const R & minr, + const R & maxr, + const R t, + const R u +) { + // INT30-C + return + u > 0 && t > std::numeric_limits::max() / u ? + checked_result( + checked_result::exception_type::overflow_error, + "multiplication overflow" + ) + : + checked_result(t * u) + ; +} + +// result signed +template +typename boost::enable_if_c< + std::is_signed::value && (sizeof(R) <= (sizeof(std::intmax_t) / 2)), + checked_result +>::type +SAFE_NUMERIC_CONSTEXPR multiply( + const R & minr, + const R & maxr, + const R t, + const R u +) { + // INT30-C + // fast method using intermediate result guaranteed not to overflow + // todo - replace std::uintmax_t with a size double the size of R + typedef std::intmax_t i_type; + return + ( static_cast(t) * static_cast(u) - > std::numeric_limits::max() ? - checked_result( - checked_result::exception_type::overflow_error, - "multiplication overflow" - ) - : - checked_result(t * u) - ; - } - template - typename boost::enable_if_c< - std::is_unsigned::value && (sizeof(R) > sizeof(std::uintmax_t) / 2), - checked_result - >::type - SAFE_NUMERIC_CONSTEXPR multiply( - const R & minr, - const R & maxr, - const R t, - const R u - ) { - // INT30-C - return - u > 0 && t > std::numeric_limits::max() / u ? - checked_result( - checked_result::exception_type::overflow_error, - "multiplication overflow" - ) - : - checked_result(t * u) - ; - } - - // result signed - template - typename boost::enable_if_c< - std::is_signed::value && (sizeof(R) <= (sizeof(std::intmax_t) / 2)), - checked_result - >::type - SAFE_NUMERIC_CONSTEXPR multiply( - const R & minr, - const R & maxr, - const R t, - const R u - ) { - // INT30-C - // fast method using intermediate result guaranteed not to overflow - // todo - replace std::uintmax_t with a size double the size of R - typedef std::intmax_t i_type; - return - ( - static_cast(t) * static_cast(u) - > static_cast(std::numeric_limits::max()) - ) ? - checked_result( - checked_result::exception_type::overflow_error, - "multiplication overflow" - ) - : - ( - static_cast(t) * static_cast(u) - < static_cast(std::numeric_limits::min()) - ) ? - checked_result( - checked_result::exception_type::underflow_error, - "multiplication underflow" - ) - : - checked_result(t * u) - ; - } - template - typename boost::enable_if_c< - std::is_signed::value && (sizeof(R) > (sizeof(std::intmax_t) / 2)), - checked_result - >::type - SAFE_NUMERIC_CONSTEXPR multiply( - const R & minr, - const R & maxr, - const R t, - const R u - ) { // INT32-C - return t > 0 ? - u > 0 ? - t > std::numeric_limits::max() / u ? - checked_result( - checked_result::exception_type::overflow_error, - "multiplication overflow" - ) - : - checked_result(t * u) - : // u <= 0 - u < std::numeric_limits::min() / t ? - checked_result( - checked_result::exception_type::overflow_error, - "multiplication overflow" - ) - : - checked_result(t * u) - : // t <= 0 - u > 0 ? - t < std::numeric_limits::min() / u ? - checked_result( - checked_result::exception_type::overflow_error, - "multiplication overflow" - ) - : - checked_result(t * u) - : // u <= 0 - t != 0 && u < std::numeric_limits::max() / t ? - checked_result( - checked_result::exception_type::overflow_error, - "multiplication overflow" - ) - : - checked_result(t * u) - ; - } - - } // namespace detail - - template - SAFE_NUMERIC_CONSTEXPR checked_result multiply( - const R & minr, - const R & maxr, - const T & t, - const U & u - ) { - static_assert(std::is_integral::value, "only intrinsic integers permitted"); - static_assert(std::is_integral::value, "only intrinsic integers permitted"); - return - detail::cast(t) != checked_result::exception_type::no_exception ? - detail::cast(t) - : - detail::cast(u) != checked_result::exception_type::no_exception ? - detail::cast(u) - : - sizeof(R) >= sizeof(T) + sizeof(U) ? - checked_result(static_cast(t) * static_cast(u)) - : - detail::multiply(minr, maxr, t, u) - ; - } - template - SAFE_NUMERIC_CONSTEXPR checked_result multiply( - const T & t, - const U & u - ) { - return - multiply( - std::numeric_limits::min(), - std::numeric_limits::max(), - t, - u + > static_cast(std::numeric_limits::max()) + ) ? + checked_result( + checked_result::exception_type::overflow_error, + "multiplication overflow" ) + : + ( + static_cast(t) * static_cast(u) + < static_cast(std::numeric_limits::min()) + ) ? + checked_result( + checked_result::exception_type::underflow_error, + "multiplication underflow" + ) + : + checked_result(t * u) + ; +} +template +typename boost::enable_if_c< + std::is_signed::value && (sizeof(R) > (sizeof(std::intmax_t) / 2)), + checked_result +>::type +SAFE_NUMERIC_CONSTEXPR multiply( + const R & minr, + const R & maxr, + const R t, + const R u +) { // INT32-C + return t > 0 ? + u > 0 ? + t > std::numeric_limits::max() / u ? + checked_result( + checked_result::exception_type::overflow_error, + "multiplication overflow" + ) + : + checked_result(t * u) + : // u <= 0 + u < std::numeric_limits::min() / t ? + checked_result( + checked_result::exception_type::overflow_error, + "multiplication overflow" + ) + : + checked_result(t * u) + : // t <= 0 + u > 0 ? + t < std::numeric_limits::min() / u ? + checked_result( + checked_result::exception_type::overflow_error, + "multiplication overflow" + ) + : + checked_result(t * u) + : // u <= 0 + t != 0 && u < std::numeric_limits::max() / t ? + checked_result( + checked_result::exception_type::overflow_error, + "multiplication overflow" + ) + : + checked_result(t * u) + ; +} + +} // namespace detail + +template +SAFE_NUMERIC_CONSTEXPR checked_result multiply( + const R & minr, + const R & maxr, + const T & t, + const U & u +) { + static_assert(std::is_integral::value, "only intrinsic integers permitted"); + static_assert(std::is_integral::value, "only intrinsic integers permitted"); + return + cast(t) != checked_result::exception_type::no_exception ? + cast(t) + : + cast(u) != checked_result::exception_type::no_exception ? + cast(u) + : + sizeof(R) >= sizeof(T) + sizeof(U) ? + checked_result(static_cast(t) * static_cast(u)) + : + multiply(minr, maxr, t, u) + ; +} +template +SAFE_NUMERIC_CONSTEXPR checked_result multiply( + const T & t, + const U & u +) { + return + multiply( + std::numeric_limits::min(), + std::numeric_limits::max(), + t, + u + ) + ; +} + +//////////////////////////////// +// safe division on unsafe types +namespace detail { + +template +typename boost::enable_if_c< + std::is_unsigned::value, + checked_result +>::type +SAFE_NUMERIC_CONSTEXPR divide( + const R & minr, + const R & maxr, + const R t, + const R u +) { + return checked_result(t / u); +} + +template +typename boost::enable_if_c< + std::is_signed::value, + checked_result +>::type +SAFE_NUMERIC_CONSTEXPR divide( + const R & minr, + const R & maxr, + const R t, + const R u +){ + return + // note presumption of two's complement arithmetic + (u < 0 && t == std::numeric_limits::min()) ? + checked_result( + checked_result::exception_type::domain_error, + "divide by zero" + ) + : + checked_result(t / u) ; - } +} - //////////////////////////////// - // safe division on unsafe types - namespace detail { +} // namespace detail - template - typename boost::enable_if_c< - std::is_unsigned::value, - checked_result - >::type - SAFE_NUMERIC_CONSTEXPR divide( - const R & minr, - const R & maxr, - const R t, - const R u - ) { - return checked_result(t / u); - } - - template - typename boost::enable_if_c< - std::is_signed::value, - checked_result - >::type - SAFE_NUMERIC_CONSTEXPR divide( - const R & minr, - const R & maxr, - const R t, - const R u - ){ - return - // note presumption of two's complement arithmetic - (u < 0 && t == std::numeric_limits::min()) ? +template +SAFE_NUMERIC_CONSTEXPR checked_result divide( + const R & minr, + const R & maxr, + const T & t, + const U & u +) { + static_assert(std::is_integral::value, "only intrinsic integers permitted"); + static_assert(std::is_integral::value, "only intrinsic integers permitted"); + return + cast(t) != checked_result::exception_type::no_exception ? + cast(t) + : + cast(u) != checked_result::exception_type::no_exception ? + cast(u) + : + u == 0 ? checked_result( checked_result::exception_type::domain_error, "divide by zero" ) : - checked_result(t / u) - ; - } + detail::divide(minr, maxr, t, u) + ; +} +template +SAFE_NUMERIC_CONSTEXPR checked_result divide( + const T & t, + const U & u +) { + return + divide( + std::numeric_limits::min(), + std::numeric_limits::max(), + t, + u + ) + ; +} - } // namespace detail +//////////////////////////////// +// safe modulus on unsafe types +namespace detail { - template - SAFE_NUMERIC_CONSTEXPR checked_result divide( - const R & minr, - const R & maxr, - const T & t, - const U & u - ) { - static_assert(std::is_integral::value, "only intrinsic integers permitted"); - static_assert(std::is_integral::value, "only intrinsic integers permitted"); - return - detail::cast(t) != checked_result::exception_type::no_exception ? - detail::cast(t) - : - detail::cast(u) != checked_result::exception_type::no_exception ? - detail::cast(u) - : - u == 0 ? - checked_result( - checked_result::exception_type::domain_error, - "divide by zero" - ) - : - detail::divide(minr, maxr, t, u) - ; - } - template - SAFE_NUMERIC_CONSTEXPR checked_result divide( - const T & t, - const U & u - ) { - return - divide( - std::numeric_limits::min(), - std::numeric_limits::max(), - t, - u +template +typename boost::enable_if_c< + std::is_unsigned::value, + checked_result +>::type +SAFE_NUMERIC_CONSTEXPR modulus( + const R & minr, + const R & maxr, + const R t, + const R u +) { + return checked_result(t % u); +} + +template +typename boost::enable_if_c< + std::is_signed::value, + checked_result +>::type +SAFE_NUMERIC_CONSTEXPR modulus( + const R & minr, + const R & maxr, + const R t, + const R u +){ + return + // note presumption of two's complement arithmetic + (u < 0 && t == std::numeric_limits::min()) ? + checked_result( + checked_result::exception_type::domain_error, + "divide by zero" ) + : + checked_result(t % u) ; - } +} - //////////////////////////////// - // safe modulus on unsafe types - namespace detail { +} // namespace detail - template - typename boost::enable_if_c< - std::is_unsigned::value, - checked_result - >::type - SAFE_NUMERIC_CONSTEXPR modulus( - const R & minr, - const R & maxr, - const R t, - const R u - ) { - return checked_result(t % u); - } - - template - typename boost::enable_if_c< - std::is_signed::value, - checked_result - >::type - SAFE_NUMERIC_CONSTEXPR modulus( - const R & minr, - const R & maxr, - const R t, - const R u - ){ - return - // note presumption of two's complement arithmetic - (u < 0 && t == std::numeric_limits::min()) ? +template +SAFE_NUMERIC_CONSTEXPR checked_result modulus( + const R & minr, + const R & maxr, + const T & t, + const U & u +) { + static_assert(std::is_integral::value, "only intrinsic integers permitted"); + static_assert(std::is_integral::value, "only intrinsic integers permitted"); + return + cast(t) != checked_result::exception_type::no_exception ? + cast(t) + : + cast(u) != checked_result::exception_type::no_exception ? + cast(u) + : + u == 0 ? checked_result( checked_result::exception_type::domain_error, "divide by zero" ) : - checked_result(t % u) - ; - } - - } // namespace detail - - template - SAFE_NUMERIC_CONSTEXPR checked_result modulus( - const R & minr, - const R & maxr, - const T & t, - const U & u - ) { - static_assert(std::is_integral::value, "only intrinsic integers permitted"); - static_assert(std::is_integral::value, "only intrinsic integers permitted"); - return - detail::cast(t) != checked_result::exception_type::no_exception ? - detail::cast(t) - : - detail::cast(u) != checked_result::exception_type::no_exception ? - detail::cast(u) - : - u == 0 ? - checked_result( - checked_result::exception_type::domain_error, - "divide by zero" - ) - : - detail::divide(minr, maxr, t, u) - ; - } - template - SAFE_NUMERIC_CONSTEXPR checked_result modulus( - const T & t, - const U & u - ) { - return - divide( - std::numeric_limits::min(), - std::numeric_limits::max(), - t, - u - ) - ; - } + detail::divide(minr, maxr, t, u) + ; +} +template +SAFE_NUMERIC_CONSTEXPR checked_result modulus( + const T & t, + const U & u +) { + return + divide( + std::numeric_limits::min(), + std::numeric_limits::max(), + t, + u + ) + ; +} } // checked } // numeric diff --git a/include/checked_result.hpp b/include/checked_result.hpp index c1bb21b..1d979c4 100644 --- a/include/checked_result.hpp +++ b/include/checked_result.hpp @@ -10,7 +10,7 @@ #define BOOST_NUMERIC_CHECKED_RESULT #include -#include "safe_base.hpp" // SAFE_NUMERIC_CONSTEXPR +#include "safe_common.hpp" // SAFE_NUMERIC_CONSTEXPR #include "exception_policies.hpp" namespace boost { @@ -32,8 +32,8 @@ struct checked_result { const char * m_msg; }; // constructors - /* // breaks add/subtrac etc.!!! + /* SAFE_NUMERIC_CONSTEXPR checked_result(const checked_result & r) : m_e(r.m_e) { @@ -65,7 +65,7 @@ struct checked_result { */ // accesors SAFE_NUMERIC_CONSTEXPR operator R() const { - return m_r; + return static_cast(m_r); } SAFE_NUMERIC_CONSTEXPR operator exception_type() const { return m_e; @@ -115,8 +115,12 @@ struct checked_result { } }; +/* template -SAFE_NUMERIC_CONSTEXPR inline const checked_result min(const checked_result & t, const checked_result & u){ +SAFE_NUMERIC_CONSTEXPR inline const checked_result min( + const checked_result & t, + const checked_result & u +){ return (t.m_e == checked_result::exception_type::no_exception && u.m_e == checked_result::exception_type::no_exception) ? @@ -131,7 +135,25 @@ SAFE_NUMERIC_CONSTEXPR inline const checked_result min(const checked_result +SAFE_NUMERIC_CONSTEXPR inline const checked_result min( + const checked_result & t, + const checked_result & u +){ + return + (t.m_e == checked_result::exception_type::no_exception + && u.m_e == checked_result::exception_type::no_exception) ? + checked_result(std::min(t.m_r, u.m_r)) + : + checked_result( + checked_result::exception_type::range_error, + "Can't compare values without values" + ) + ; +} +/* template SAFE_NUMERIC_CONSTEXPR inline const checked_result max(const checked_result & t, const checked_result & u){ return @@ -148,6 +170,20 @@ SAFE_NUMERIC_CONSTEXPR inline const checked_result max(const checked_result +SAFE_NUMERIC_CONSTEXPR inline const checked_result max(const checked_result & t, const checked_result & u){ + return + (t.m_e == checked_result::exception_type::no_exception + && u.m_e == checked_result::exception_type::no_exception) ? + checked_result(std::max(t.m_r, u.m_r)) + : + checked_result( + checked_result::exception_type::range_error, + "Can't compare values without values" + ) + ; +} } // numeric } // boost diff --git a/include/exception_policies.hpp b/include/exception_policies.hpp index bdae4d9..2c62e95 100644 --- a/include/exception_policies.hpp +++ b/include/exception_policies.hpp @@ -14,7 +14,7 @@ #include #include // is_base_of, is_same -//#include +#include #include "concept/exception_policy.hpp" #include "boost/concept/assert.hpp" diff --git a/include/interval.hpp b/include/interval.hpp index 97e1137..a072acf 100644 --- a/include/interval.hpp +++ b/include/interval.hpp @@ -15,6 +15,8 @@ #include #include // min, max +#include + #include "safe_base.hpp" #include "checked_result.hpp" #include "checked.hpp" @@ -69,21 +71,21 @@ SAFE_NUMERIC_CONSTEXPR interval operator*(const interval & t, const interv return interval( min( - min( + std::min( checked::multiply(static_cast(t.l), static_cast(u.l)), checked::multiply(static_cast(t.l), static_cast(u.u)) ), - min( + std::min( checked::multiply(static_cast(t.u), static_cast(u.l)), checked::multiply(static_cast(t.u), static_cast(u.u)) ) ), - max( - max( + std::max( + std::max( checked::multiply(static_cast(t.l), static_cast(u.l)), checked::multiply(static_cast(t.l), static_cast(u.u)) ), - max( + std::max( checked::multiply(static_cast(t.u), static_cast(u.l)), checked::multiply(static_cast(t.u), static_cast(u.u)) ) @@ -149,7 +151,27 @@ SAFE_NUMERIC_CONSTEXPR interval operator%( ; } -} // umeric + +template +SAFE_NUMERIC_CONSTEXPR boost::logic::tribool operator<( + const interval & t, + const interval & u +){ + return + (t.no_exception() || u.no_exception) ? + boost::logic::indeterminate + : + (t.u < u.l) ? + boost::logic::tribool(true) + : + (t.l > u.u) ? + boost::logic::tribool(true) + : + boost::logic::indeterminate + ; +} + +} // numeric } // boost #endif // BOOST_NUMERIC_INTERVAL_HPP diff --git a/include/safe_base.hpp b/include/safe_base.hpp index b47e041..a0ff378 100644 --- a/include/safe_base.hpp +++ b/include/safe_base.hpp @@ -20,8 +20,8 @@ #include #include -// don't use constexpr so we can debug -#define SAFE_NUMERIC_CONSTEXPR constexpr +#include "checked.hpp" +#include "safe_common.hpp" namespace boost { namespace numeric { @@ -29,7 +29,7 @@ namespace numeric { template< class Stored, class Derived, - class P, // promotion policy + class P, // promotion polic class E // exception policy > class safe_base { @@ -37,6 +37,13 @@ class safe_base { derived() const { return static_cast(*this); } + template + SAFE_NUMERIC_CONSTEXPR bool validate(const T & t) const { + return ! ( + boost::numeric::checked::greater_than(t, base_value(Derived::max())) + && boost::numeric::checked::less_than(t, base_value(Derived::min())) + ); + } Stored m_t; protected: // note: Rule of Three. Don't specify custom move, copy etc. @@ -51,29 +58,28 @@ protected: {} template SAFE_NUMERIC_CONSTEXPR safe_base(T & t) : - m_t(t) + m_t(static_cast(t)) { // verify that this is convertible to the storable type static_assert( std::is_convertible::value, "Constructor argument is convertible to the storable type" ); - if(! derived().validate(t)){ - E::range_error( - "Invalid value" - ); - } - } - - bool validate() const { - if(! derived().validate(m_t)){ - E::range_error( - "Invalid value" - ); + if(! validate(t)){ + E::range_error("Invalid value"); } } public: + ///////////////////////////////////////////////////////////////// + // casting operators for intrinsic integers + template + explicit SAFE_NUMERIC_CONSTEXPR operator R () const { + return static_cast(m_t); + } + explicit SAFE_NUMERIC_CONSTEXPR operator const Stored & () const { + return m_t; + } // used to implement stream i/o operators Stored & get_stored_value() { return m_t; @@ -156,20 +162,16 @@ public: } Derived operator++(int){ // post increment Stored t = m_t; - if(! derived().validate(*this + 1)){ - E::overflow_error( - "Overflow on increment" - ); + if(! validate(*this + 1)){ + E::overflow_error("Overflow on increment"); } ++t; return derived(); } Derived & operator--(int){ // post decrement Stored t = m_t; - if(! derived().validate(*this - 1)){ - E::overflow_error( - "Overflow on increment" - ); + if(! validate(*this - 1)){ + E::overflow_error("Overflow on increment"); } --t; return derived(); @@ -187,10 +189,8 @@ public: std::numeric_limits::is_signed, "Bitwise inversion of unsigned value is an error" ); - if(! derived().validate(~m_t)){ - E::overflow_error( - "Overflow on increment" - ); + if(! validate(~m_t)){ + E::overflow_error("Overflow on increment"); } return derived(); } @@ -260,44 +260,10 @@ public: } */ - ///////////////////////////////////////////////////////////////// - // casting operators for intrinsic integers - explicit SAFE_NUMERIC_CONSTEXPR operator const Stored & () const { - return m_t; - } + }; } // numeric } // boost -namespace boost { -namespace numeric { - -// default implementations for required meta-functions -template -struct is_safe : public std::false_type -{}; - -template -struct base_type { - typedef T type; -}; - -template -SAFE_NUMERIC_CONSTEXPR const typename base_type::type & base_value(const T & t) { - return static_cast::type & >(t); -} - -template -struct get_promotion_policy { - typedef void type; -}; - -template -struct get_exception_policy { - typedef void type; -}; -} // numeric -} // boost - #endif // BOOST_NUMERIC_SAFE_BASE_HPP diff --git a/include/safe_base_operations.hpp b/include/safe_base_operations.hpp index d571434..d596b69 100644 --- a/include/safe_base_operations.hpp +++ b/include/safe_base_operations.hpp @@ -18,7 +18,7 @@ #include #include #include -//#include +#include #include @@ -104,40 +104,6 @@ struct common_policies { // These should catch things like U < safe_base<...> and implement them // as safe_base<...> >= U which should be handled above. -///////////////////////////////////////////////////////////////// -// cast - -/* -template -T safe_cast(const U & u) { - - typedef typename base_type::type result_base_type; - - // filter out case were overflow cannot occur - SAFE_NUMERIC_CONSTEXPR const interval r_interval; - SAFE_NUMERIC_CONSTEXPR const interval u_interval( - base_value::min()>, - base_value::max()> - ); - - - = operator*( - interval::type>(), - interval::type>() - ); - - - if(std::numeric_limits::is_unsigned) - if(u < 0) - overflow("casting alters value"); - if(safe_compare::greater_than(u, std::numeric_limits::max())) - overflow("safe range overflow"); - if(safe_compare::less_than(u, std::numeric_limits::min())) - overflow("safe range underflow"); - return static_cast(u); -} -*/ - ///////////////////////////////////////////////////////////////// // addition @@ -160,10 +126,11 @@ typename boost::lazy_enable_if< >, addition_result >::type -inline operator+(const T & t, const U & u){ +SAFE_NUMERIC_CONSTEXPR inline operator+(const T & t, const U & u){ // argument dependent lookup should guarentee that we only get here // only if one of the types is a safe type. Verify this here typedef addition_result ar; + typedef typename ar::P::exception_policy exception_policy; typedef typename ar::type result_type; static_assert( boost::numeric::is_safe::value, @@ -175,7 +142,7 @@ inline operator+(const T & t, const U & u){ typedef typename base_type::type u_base_type; // filter out case were overflow cannot occur - // note: subtle trickery. Suppose is safe_range. Then + // note: subtle trickery. Suppose t is safe_range. Then // std::numeric_limits::min() will be safe_range::max()) }; - // when we add the temporary intervals above, we'll get a new interva - // with the correct range for the sum ! Same goes for all the operations - // defined below. + // when we add the temporary intervals above, we'll get a new interval + // with the correct range for the sum ! SAFE_NUMERIC_CONSTEXPR const interval r_interval = operator+(t_interval, u_interval); @@ -206,9 +172,9 @@ inline operator+(const T & t, const U & u){ base_value(u) ); - r.template dispatch(); - - return static_cast(r); + r.template dispatch(); + + return result_type(static_cast(r)); } ///////////////////////////////////////////////////////////////// @@ -232,10 +198,11 @@ typename boost::lazy_enable_if< >, subtraction_result >::type -inline operator-(const T & t, const U & u){ +SAFE_NUMERIC_CONSTEXPR operator-(const T & t, const U & u){ // argument dependent lookup should guarentee that we only get here // only if one of the types is a safe type. Verify this here typedef subtraction_result ar; + typedef typename ar::P::exception_policy exception_policy; typedef typename ar::type result_type; static_assert( boost::numeric::is_safe::value, @@ -271,13 +238,14 @@ inline operator-(const T & t, const U & u){ base_value(u) ); - r.template dispatch(); + r.template dispatch(); - return static_cast(r); + return result_type(static_cast(r)); } ///////////////////////////////////////////////////////////////// // multiplication + template struct multiplication_result { typedef common_policies P; @@ -297,11 +265,12 @@ typename boost::lazy_enable_if< >, multiplication_result >::type -inline operator*(const T & t, const U & u){ +SAFE_NUMERIC_CONSTEXPR operator*(const T & t, const U & u){ // argument dependent lookup should guarentee that we only get here // only if one of the types is a safe type. Verify this here - typedef multiplication_result ar; - typedef typename ar::type result_type; + typedef multiplication_result mr; + typedef typename mr::P::exception_policy exception_policy; + typedef typename mr::type result_type; static_assert( boost::numeric::is_safe::value, "Promotion failed to return safe type" @@ -336,9 +305,9 @@ inline operator*(const T & t, const U & u){ base_value(u) ); - r.template dispatch(); - - return static_cast(r); + r.template dispatch(); + + return result_type(static_cast(r)); } ///////////////////////////////////////////////////////////////// @@ -366,8 +335,9 @@ typename boost::lazy_enable_if< inline operator/(const T & t, const U & u){ // argument dependent lookup should guarentee that we only get here // only if one of the types is a safe type. Verify this here - typedef division_result ar; - typedef typename ar::type result_type; + typedef division_result dr; + typedef typename dr::P::exception_policy exception_policy; + typedef typename dr::type result_type; static_assert( boost::numeric::is_safe::value, "Promotion failed to return safe type" @@ -402,9 +372,9 @@ inline operator/(const T & t, const U & u){ base_value(u) ); - r.template dispatch(); - - return static_cast(r); + r.template dispatch(); + + return result_type(static_cast(r)); } ///////////////////////////////////////////////////////////////// @@ -475,6 +445,35 @@ inline operator%(const T & t, const U & u){ ///////////////////////////////////////////////////////////////// // comparison +/* implement this later - requires C++14 +template +typename boost::lazy_enable_if< + boost::mpl::or_< + boost::numeric::is_safe, + boost::numeric::is_safe + >, + boost::mpl::identity +>::type +SAFE_NUMERIC_CONSTEXPR operator<(const T & lhs, const U & rhs) { + typedef typename base_type::type t_base_type; + typedef typename base_type::type u_base_type; + SAFE_NUMERIC_CONSTEXPR const boost::logic::tribool r = ( + interval( + base_value(std::numeric_limits::min()), + base_value(std::numeric_limits::max()) + ) + < + interval( + base_value(std::numeric_limits::min()), + base_value(std::numeric_limits::max()) + ) + ); + if(r != boost::logic::tribool::indeterminate_value) + return r; + return + checked::less_than(base_value(lhs), base_value(rhs)); +} +*/ template typename boost::lazy_enable_if< @@ -580,6 +579,9 @@ inline operator^(const T & lhs, const safe_base & rhs } // numeric } // boost +#include +#include + template typename std::enable_if< boost::numeric::is_safe::value, diff --git a/include/safe_cast.hpp b/include/safe_cast.hpp index bcd39d1..8a7c44d 100644 --- a/include/safe_cast.hpp +++ b/include/safe_cast.hpp @@ -16,22 +16,17 @@ #include #include -//#include "overflow.hpp" -#include "safe_compare.hpp" +#include "safe_common.hpp" +#include "safe_base_operations.hpp" namespace boost { namespace numeric { -template -T safe_cast(const U & u) { - if(std::numeric_limits::is_unsigned) - if(u < 0) - overflow("casting alters value"); - if(safe_compare::greater_than(u, std::numeric_limits::max())) - overflow("safe range overflow"); - if(safe_compare::less_than(u, std::numeric_limits::min())) - overflow("safe range underflow"); - return static_cast(u); +template +R safe_cast(const U & u) { + checked_result r = checked::detail::cast(base_value(u)); + r.template dispatch(); + return static_cast(r); } } // numeric diff --git a/include/safe_common.hpp b/include/safe_common.hpp new file mode 100644 index 0000000..adcfe1c --- /dev/null +++ b/include/safe_common.hpp @@ -0,0 +1,48 @@ +#ifndef BOOST_NUMERIC_SAFE_COMMON_HPP +#define BOOST_NUMERIC_SAFE_COMMON_HPP + +// MS compatible compilers support #pragma once +#if defined(_MSC_VER) && (_MSC_VER >= 1020) +# pragma once +#endif + +// 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) + +// don't use constexpr so we can debug +#define SAFE_NUMERIC_CONSTEXPR constexpr + +namespace boost { +namespace numeric { + +// default implementations for required meta-functions +template +struct is_safe : public std::false_type +{}; + +template +struct base_type { + typedef T type; +}; + +template +SAFE_NUMERIC_CONSTEXPR const typename base_type::type & base_value(const T & t) { + return static_cast::type & >(t); +} + +template +struct get_promotion_policy { + typedef void type; +}; + +template +struct get_exception_policy { + typedef void type; +}; +} // numeric +} // boost + +#endif // BOOST_NUMERIC_SAFE_COMMON_HPP diff --git a/include/safe_integer.hpp b/include/safe_integer.hpp index affa4b3..9e6e250 100644 --- a/include/safe_integer.hpp +++ b/include/safe_integer.hpp @@ -13,7 +13,6 @@ // http://www.boost.org/LICENSE_1_0.txt) #include "safe_base.hpp" -#include "safe_base_operations.hpp" #include "concept/numeric.hpp" #include "exception_policies.hpp" #include "native.hpp" @@ -71,10 +70,10 @@ template< class P, class E > -SAFE_NUMERIC_CONSTEXPR const T & base_value( +SAFE_NUMERIC_CONSTEXPR T base_value( const safe & st ) { - return static_cast(st); + return static_cast(st); } template< @@ -86,12 +85,14 @@ struct safe : public safe_base, P, E>{ private: typedef safe_base, P, E > base_type; friend base_type; - static_assert(std::is_integral::value, "Only integer types supported"); + BOOST_CONCEPT_ASSERT((Integer)); BOOST_CONCEPT_ASSERT((PromotionPolicy

)); BOOST_CONCEPT_ASSERT((ExceptionPolicy)); - - SAFE_NUMERIC_CONSTEXPR bool validate(const T & t) const { - return true; + SAFE_NUMERIC_CONSTEXPR static const T min(){ + return std::numeric_limits::min(); + } + SAFE_NUMERIC_CONSTEXPR static const T max(){ + return std::numeric_limits::max(); } public: // note: Rule of Three. Don't specify custom move, copy etc. @@ -126,10 +127,16 @@ class numeric_limits > typedef boost::numeric::safe SI; public: // these expressions are not SAFE_NUMERIC_CONSTEXPR until C++14 so re-implement them here - SAFE_NUMERIC_CONSTEXPR static SI min() noexcept { return std::numeric_limits::min(); } - SAFE_NUMERIC_CONSTEXPR static SI max() noexcept { return std::numeric_limits::max(); } + SAFE_NUMERIC_CONSTEXPR static SI min() noexcept { + return boost::numeric::safe(std::numeric_limits::min()); + } + SAFE_NUMERIC_CONSTEXPR static SI max() noexcept { + return boost::numeric::safe(std::numeric_limits::max()); + } }; } // std +#include "safe_base_operations.hpp" + #endif // BOOST_NUMERIC_SAFE_INTEGER_HPP diff --git a/include/safe_range.hpp b/include/safe_range.hpp index 1f1738a..ac62771 100644 --- a/include/safe_range.hpp +++ b/include/safe_range.hpp @@ -12,12 +12,9 @@ // accompanying file LICENSE_1_0.txt or copy at // http://www.boost.org/LICENSE_1_0.txt) -//#include -//#include "numeric.hpp" #include "safe_base.hpp" #include "safe_base_operations.hpp" #include "native.hpp" -//#include "concept/numeric.hpp" #include "exception_policies.hpp" #include "native.hpp" @@ -66,8 +63,8 @@ namespace boost { namespace numeric { template< -template< - class T, + boost::intmax_t MIN, + boost::intmax_t MAX, class P, class E > @@ -79,7 +76,16 @@ template< class P, class E > -struct get_promotion_policy > { +struct is_safe > : public std::true_type +{}; + +template< + boost::intmax_t MIN, + boost::intmax_t MAX, + class P, + class E +> +struct get_promotion_policy > { typedef P type; }; @@ -89,7 +95,7 @@ template< class P, class E > -struct get_exception_policy > { +struct get_exception_policy > { typedef E type; }; @@ -99,15 +105,15 @@ template< class P, class E > -struct base_type > { +struct base_type > { typedef T type; }; template< boost::intmax_t MIN, boost::intmax_t MAX, - class P = boost::numeric::native - class E = dboost::numeric::throw_exception + class P = native + class E = throw_exception > class safe_signed_range : public safe_base< @@ -118,22 +124,19 @@ class safe_signed_range : public > { static_assert( - MIN < MAX, - "minimum must be less than maximum" + MIN <= MAX, + "minimum cannot exceed maximum" ); BOOST_CONCEPT_ASSERT((PromotionPolicy

)); BOOST_CONCEPT_ASSERT((ExceptionPolicy)); + typedef typename detail::signed_stored_type::type stored_type; + typedef typename boost::numeric::safe_base< - typename detail::signed_stored_type::type, + stored_type, safe_signed_range > base_type; friend base_type; - template - SAFE_NUMERIC_CONSTEXPR bool validate(const T & t) const { - return safe_compare::less_than(t, MIN) - || safe_compare::greater_than(t, MAX); - } public: // note: Rule of Three. Don't specify custom move, copy etc. SAFE_NUMERIC_CONSTEXPR safe_signed_range() : @@ -183,8 +186,55 @@ namespace numeric { template< boost::uintmax_t MIN, boost::uintmax_t MAX, - class P = boost::numeric::native - class E = dboost::numeric::throw_exception + class P, + class E +> +struct safe_unsigned_range; + +template< + boost::uintmax_t MIN, + boost::uintmax_t MAX, + class P, + class E +> +struct is_safe > : public std::true_type +{}; + +template< + boost::uintmax_t MIN, + boost::uintmax_t MAX, + class P, + class E +> +struct get_promotion_policy > { + typedef P type; +}; + +template< + boost::uintmax_t MIN, + boost::uintmax_t MAX, + class P, + class E +> +struct get_exception_policy > { + typedef E type; +}; + +template< + boost::uintmax_t MIN, + boost::uintmax_t MAX, + class P, + class E +> +struct base_type > { + typedef T type; +}; + +template< + boost::uintmax_t MIN, + boost::uintmax_t MAX, + class P = native + class E = throw_exception > class safe_unsigned_range : public safe_base< @@ -209,11 +259,6 @@ private > base_type; friend base_type; - template - SAFE_NUMERIC_CONSTEXPR bool validate(const T & t) const { - return safe_compare::less_than(t, MIN) - || safe_compare::greater_than(t, MAX); - } public: // note: Rule of Three. Don't specify custom move, copy etc. SAFE_NUMERIC_CONSTEXPR safe_unsigned_range() : diff --git a/test/test_add.cpp b/test/test_add.cpp index 6d9cc7e..9707027 100644 --- a/test/test_add.cpp +++ b/test/test_add.cpp @@ -5,6 +5,7 @@ // http://www.boost.org/LICENSE_1_0.txt) #include +#include #include "../include/safe_integer.hpp" @@ -42,11 +43,11 @@ bool test_add( try{ t1 + v2; } - catch(...){} + catch(std::exception){} return false; } } - catch(std::exception & e){ + catch(std::exception){ if(expected_result == '.'){ std::cout << "erroneously detected error in addition " @@ -56,7 +57,7 @@ bool test_add( try{ t1 + v2; } - catch(...){} + catch(std::exception){} return false; } } @@ -85,11 +86,11 @@ bool test_add( try{ t1 +t2; } - catch(...){} + catch(std::exception){} return false; } } - catch(std::exception & e){ + catch(std::exception){ if(expected_result == '.'){ std::cout << "erroneously detected error in addition " @@ -99,7 +100,7 @@ bool test_add( try{ t1 + t2; } - catch(...){} + catch(std::exception){} return false; } } diff --git a/test/test_cast copy.cpp b/test/test_cast copy.cpp index fde6ce3..e834f7f 100644 --- a/test/test_cast copy.cpp +++ b/test/test_cast copy.cpp @@ -6,56 +6,57 @@ #include #include - #include // EXIT_SUCCESS -#include "../include/safe_integer.hpp" -#include "../include/safe_compare.hpp" +#include "safe_integer.hpp" // test conversion to T2 from different literal types template bool test_cast(T1 v1, const char *t2_name, const char *t1_name){ std::cout - << "testing " - << av1 << " + " << av2 + << "testing static_cast(" << t1_name << ")" << std::endl; - { - - /* test conversion constructor to T1 */ - T2 v2; + /* test conversion constructor to safe */ + boost::numeric::safe s2; try{ - v2 = boost::numeric::safe_cast(v1); - if(! boost::numeric::safe_compare::equal(v2, v1)){ + s2 = static_cast >(v1); + if(! (s2 == v1)){ std::cout - << "failed to detect error in cast " + << "failed to detect error in construction " << t2_name << "<-" << t1_name << std::endl; try{ - v2 = boost::numeric::safe_cast(v1); + static_cast >(v1); } - catch(...){} + catch(std::exception){} return false; } } - catch(std::exception & e){ - if(boost::numeric::safe_compare::equal(v2, v1)){ + catch(std::exception){ + if( s2 == v1 ){ std::cout - << "failed to detect error in cast " + << "erroneously emitted error " << t1_name << "<-" << t2_name << std::endl; try{ - v2 = boost::numeric::safe_cast(v1); + static_cast >(v1); } - catch(...){} + catch(std::exception){} return false; } } return true; // passed test } -#include "test.hpp" + +#include +#include +#include +#include + #include "test_types.hpp" #include "test_values.hpp" +#include "test.hpp" #define TEST_CAST(T1, v) \ rval = rval && test_cast( \ diff --git a/test/test_cast.cpp b/test/test_cast.cpp index b62ac34..a6ebdfb 100644 --- a/test/test_cast.cpp +++ b/test/test_cast.cpp @@ -5,52 +5,51 @@ // http://www.boost.org/LICENSE_1_0.txt) #include +#include #include // EXIT_SUCCESS - -#include "../include/safe_cast.hpp" -#include "../include/safe_compare.hpp" - -#include "test_types.hpp" -#include "test_values.hpp" +#include "../include/checked.hpp" // test conversion to T2 from different literal types template -bool test_cast(T1 v1, const char *t2_name, const char *t1_name){ - /* test conversion constructor to T1 */ - T2 v2; - try{ - v2 = boost::numeric::safe_cast(v1); - if(! boost::numeric::safe_compare::equal(v2, v1)){ +bool test_cast(const T1 & v1, const char *t2_name, const char *t1_name){ + std::cout + << "testing static_cast(" << t1_name << ")" + << std::endl; + + constexpr const T1 cev1 = 0; + constexpr const boost::numeric::checked_result r2 = boost::numeric::checked::cast(cev1); + + if(r2 == boost::numeric::checked_result::exception_type::no_exception){ + if(! (r2 == v1)){ std::cout << "failed to detect error in construction " << t2_name << "<-" << t1_name << std::endl; - try{ - v2 = boost::numeric::safe_cast(v1); - } - catch(...){} + boost::numeric::checked::cast(v1); return false; } } - catch(std::range_error e){ - if(boost::numeric::safe_compare::equal(v2, v1)){ + else{ + if( r2 == v1 ){ std::cout - << "failed to detect error in construction " + << "erroneously emitted error " << t1_name << "<-" << t2_name << std::endl; - try{ - v2 = boost::numeric::safe_cast(v1); - } - catch(...){} - return false; + boost::numeric::checked::cast(v1); } } return true; // passed test } -#include "test.hpp" +#include +#include +#include +#include + +#include "test_types.hpp" #include "test_values.hpp" +#include "test.hpp" #define TEST_CAST(T1, v) \ rval = rval && test_cast( \ diff --git a/test/test_compare.cpp b/test/test_compare.cpp index 9cc0b12..afa85c4 100644 --- a/test/test_compare.cpp +++ b/test/test_compare.cpp @@ -10,7 +10,6 @@ #include #include "../include/safe_integer.hpp" -#include "../include/safe_compare.hpp" template void print_argument_types( @@ -65,44 +64,6 @@ bool test_compare_detail( return true; } -template -bool test_compare_detail2( - T1 v1, - T2 v2, - char expected_result -){ - print_argument_types(v1, v2); - switch(expected_result){ - case '=': { - if(! boost::numeric::safe_compare::equal(v1, v2)) - return false; - if(boost::numeric::safe_compare::less_than(v1, v2)) - return false; - if(boost::numeric::safe_compare::greater_than(v1, v2)) - return false; - break; - } - case '<': { - if(! boost::numeric::safe_compare::less_than(v1, v2)) - return false; - if(boost::numeric::safe_compare::equal(v1, v2)) - return false; - if(boost::numeric::safe_compare::greater_than(v1, v2)) - return false; - break; - } - case '>':{ - if(! boost::numeric::safe_compare::greater_than(v1, v2)) - return false; - if(boost::numeric::safe_compare::less_than(v1, v2)) - return false; - if(boost::numeric::safe_compare::equal(v1, v2)) - return false; - break; - } - } - return true; -} template bool test_compare( @@ -117,15 +78,6 @@ bool test_compare( << av1 << ' ' << expected_result << ' ' << av2 << std::endl; - if(! test_compare_detail2(v1, v2,expected_result)){ - std::cout - << "error " - << av1 << ' ' << expected_result << ' ' << av2 - << std::endl; - test_compare_detail2(v1, v2,expected_result); - return false; - } - boost::numeric::safe t1 = v1; if(!test_compare_detail(t1, v2, expected_result)){ std::cout diff --git a/test/test_conversion.cpp b/test/test_conversion.cpp index 0462538..ec1a56b 100644 --- a/test/test_conversion.cpp +++ b/test/test_conversion.cpp @@ -9,7 +9,6 @@ #include #include -#include "../include/safe_compare.hpp" #include "../include/safe_integer.hpp" // test conversion to T2 from different literal types @@ -19,7 +18,7 @@ bool test_conversion(T1 v1, const char *t2_name, const char *t1_name){ boost::numeric::safe t2; try{ t2 = v1; - if(! boost::numeric::safe_compare::equal(t2, v1)){ + if(! (t2 == v1)){ std::cout << "failed to detect error in construction " << t2_name << "<-" << t1_name @@ -27,12 +26,12 @@ bool test_conversion(T1 v1, const char *t2_name, const char *t1_name){ try{ t2 = v1; } - catch(...){} + catch(std::exception){} return false; } } - catch(std::exception & e){ - if(boost::numeric::safe_compare::equal(t2, v1)){ + catch(std::exception){ + if(t2 == v1){ std::cout << "failed to detect error in construction " << t2_name << "<-" << t2_name @@ -40,14 +39,14 @@ bool test_conversion(T1 v1, const char *t2_name, const char *t1_name){ try{ t2 = v1; } - catch(...){} + catch(std::exception){} return false; } } boost::numeric::safe t1 = v1; try{ t2 = t1; - if(! boost::numeric::safe_compare::equal(t2, t1)){ + if(! (t2 == t1)){ std::cout << "failed to detect error in construction " << t2_name << "<-" << t1_name @@ -55,12 +54,12 @@ bool test_conversion(T1 v1, const char *t2_name, const char *t1_name){ try{ t2 = t1; } - catch(...){} + catch(std::exception){} return false; } } - catch(std::exception & e){ - if(boost::numeric::safe_compare::equal(t2, t1)){ + catch(std::exception){ + if(t2 == t1){ std::cout << "failed to detect error in construction " << t2_name << "<-" << t2_name @@ -68,7 +67,7 @@ bool test_conversion(T1 v1, const char *t2_name, const char *t1_name){ try{ t2 = t1; } - catch(...){} + catch(std::exception){} return false; } } diff --git a/test/test_divide.cpp b/test/test_divide.cpp index fe92f9c..ccaf606 100644 --- a/test/test_divide.cpp +++ b/test/test_divide.cpp @@ -5,6 +5,7 @@ // http://www.boost.org/LICENSE_1_0.txt) #include +#include #include "../include/safe_integer.hpp" @@ -40,11 +41,11 @@ bool test_divide( try{ t1 / v2; } - catch(...){} + catch(std::exception){} return false; } } - catch(std::exception & e){ + catch(std::exception){ if(expected_result == '.'){ std::cout << "erroneously detected error in division " @@ -54,7 +55,7 @@ bool test_divide( try{ t1 / v2; } - catch(...){} + catch(std::exception){} return false; } } @@ -83,11 +84,11 @@ bool test_divide( try{ t1 / t2; } - catch(...){} + catch(std::exception){} return false; } } - catch(std::exception & e){ + catch(std::exception){ if(expected_result == '.'){ std::cout << "erroneously detected error in division " @@ -97,7 +98,7 @@ bool test_divide( try{ t1 / t2; } - catch(...){} + catch(std::exception){} return false; } } diff --git a/test/test_modulus.cpp b/test/test_modulus.cpp index 916a231..37f31a0 100644 --- a/test/test_modulus.cpp +++ b/test/test_modulus.cpp @@ -5,7 +5,7 @@ // http://www.boost.org/LICENSE_1_0.txt) #include -#include +#include #include "../include/safe_integer.hpp" @@ -43,11 +43,11 @@ bool test_modulus( try{ result = t1 % v2; } - catch(...){} + catch(std::exception){} return false; } } - catch(std::exception & e){ + catch(std::exception){ if(expected_result != 'x'){ std::cout << "erroneously detected error in division " @@ -57,7 +57,7 @@ bool test_modulus( try{ t1 % v2; } - catch(...){} + catch(std::exception){} return false; } } @@ -81,11 +81,11 @@ bool test_modulus( try{ result = t1 % t2; } - catch(...){} + catch(std::exception){} return false; } } - catch(std::exception & e){ + catch(std::exception){ if(expected_result != 'x'){ std::cout << "erroneously detected error in division " @@ -95,7 +95,7 @@ bool test_modulus( try{ t1 % t2; } - catch(...){} + catch(std::exception){} return false; } } diff --git a/test/test_multiply.cpp b/test/test_multiply.cpp index ddeeb96..f684787 100644 --- a/test/test_multiply.cpp +++ b/test/test_multiply.cpp @@ -5,7 +5,8 @@ // http://www.boost.org/LICENSE_1_0.txt) #include -#include // is same +#include +#include // is_same #include "../include/safe_integer.hpp" @@ -23,43 +24,44 @@ bool test_multiply( << "testing " << av1 << " * " << av2 << std::endl; + { + boost::numeric::safe t1 = v1; + // presuming native policy + boost::numeric::safe result; - boost::numeric::safe t1 = v1; - // presuming native policy - boost::numeric::safe result; + try{ + result = t1 * v2; + static_assert( + boost::numeric::is_safe::value, + "Expression failed to return safe type" + ); - try{ - result = t1 * v2; - static_assert( - boost::numeric::is_safe::value, - "Expression failed to return safe type" - ); - - if(expected_result == 'x'){ - std::cout - << "failed to detect error in multiplication " - << std::hex << result << "(" << std::dec << result << ")" - << " ! = "<< av1 << " * " << av2 - << std::endl; - try{ - t1 * v2; + if(expected_result == 'x'){ + std::cout + << "failed to detect error in multiplication " + << std::hex << result << "(" << std::dec << result << ")" + << " ! = "<< av1 << " * " << av2 + << std::endl; + try{ + t1 * v2; + } + catch(std::exception){} + return false; } - catch(...){} - return false; } - } - catch(std::exception & e){ - if(expected_result == '.'){ - std::cout - << "erroneously detected error in multiplication " - << std::hex << result << "(" << std::dec << result << ")" - << " == "<< av1 << " * " << av2 - << std::endl; - try{ - t1 * v2; + catch(std::exception){ + if(expected_result == '.'){ + std::cout + << "erroneously detected error in multiplication " + << std::hex << result << "(" << std::dec << result << ")" + << " == "<< av1 << " * " << av2 + << std::endl; + try{ + t1 * v2; + } + catch(std::exception){} + return false; } - catch(...){} - return false; } } { @@ -67,11 +69,11 @@ bool test_multiply( boost::numeric::safe t2 = v2; // presuming native policy - boost::numeric::safe result; + boost::numeric::safe result; static_assert( std::is_same< - boost::numeric::safe, + boost::numeric::safe, decltype(t1 * t2) >::value, "unexpected result type" @@ -94,11 +96,11 @@ bool test_multiply( try{ t1 * t2; } - catch(...){} + catch(std::exception){} return false; } } - catch(std::exception & e){ + catch(std::exception){ if(expected_result == '.'){ std::cout << "erroneously detected error in multiplication " @@ -108,7 +110,7 @@ bool test_multiply( try{ t1 * t2; } - catch(...){} + catch(std::exception){} return false; } } diff --git a/test/test_subtract.cpp b/test/test_subtract.cpp index fcc583d..5313968 100644 --- a/test/test_subtract.cpp +++ b/test/test_subtract.cpp @@ -5,6 +5,7 @@ // http://www.boost.org/LICENSE_1_0.txt) #include +#include #include // we could have used decltype and auto for C++11 but we've decided @@ -45,11 +46,11 @@ bool test_subtract( try{ result = t1 - v2; } - catch(...){} + catch(std::exception){} return false; } } - catch(std::exception & e){ + catch(std::exception){ if(expected_result == '.'){ std::cout << "erroneously detected error in subtraction " @@ -59,7 +60,7 @@ bool test_subtract( try{ result = t1 - v2; } - catch(...){} + catch(std::exception){} return false; } } @@ -86,11 +87,11 @@ bool test_subtract( try{ result = t1 - t2; } - catch(...){} + catch(std::exception){} return false; } } - catch(std::exception & e){ + catch(std::exception){ if(expected_result == '.'){ std::cout << "erroneously detected error in subtraction " @@ -100,7 +101,7 @@ bool test_subtract( try{ result = t1 - t2; } - catch(...){} + catch(std::exception){} return false; } } diff --git a/test/test_z.cpp b/test/test_z.cpp index 2cf84c2..ca511b5 100644 --- a/test/test_z.cpp +++ b/test/test_z.cpp @@ -1,11 +1,13 @@ - #include -#include "safe_integer.hpp" + +constexpr int factorial (int n) +{ + return n > 0 ? n * factorial( n - 1 ) : 1; +} int main(){ - boost::numeric::safe t1(0x80000000); - int8_t v2(0xff); - - std::cout << t1 / v2; + int n; + std::cin >> n; + constexpr int x = factorial( n ); return 0; }