diff --git a/CMake/CMakeLists.txt b/CMake/CMakeLists.txt index 8b57007..864b108 100644 --- a/CMake/CMakeLists.txt +++ b/CMake/CMakeLists.txt @@ -142,6 +142,8 @@ endif() # necessary # enable_testing() +if(0) + message(STATUS "test_cast") add_executable( test_cast ../test/test_cast.cpp @@ -199,6 +201,20 @@ add_executable( test ../test/test.cpp) target_link_libraries( test ${Boost_LIBRARIES} ) add_test(NAME test COMMAND test) +endif() + +file(GLOB test_files + RELATIVE "${CMAKE_CURRENT_SOURCE_DIR}" + "${CMAKE_CURRENT_SOURCE_DIR}/../test/*.cpp" +) +foreach(test_file_path ${test_files} ) + set(test_file_name get_filename_component( NAME ${test_file_path} ) ) + message(STATUS "${test_file_path"}) + add_executable( ${test_file_path} ../test/${test_file_path}.cpp) + target_link_libraries( ${test_file_path} ${Boost_LIBRARIES} ) + add_test(NAME${test_file_path} COMMAND ${test_file_path}) +endforeach(t) + # end test targets #################### diff --git a/include/checked.hpp b/include/checked.hpp new file mode 100644 index 0000000..297fe3e --- /dev/null +++ b/include/checked.hpp @@ -0,0 +1,366 @@ +#ifndef BOOST_NUMERIC_CHECKED_HPP +#define BOOST_NUMERIC_CHECKED_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) + +// contains operations for doing checked aritmetic on native +// C++ types. + +#include "overflow.hpp" +#include "safe_compare.hpp" +#include "safe_cast.hpp" + +// we could have used decltype and auto for C++11 but we've decided +// to use boost/typeof to be compatible with older compilers +#include + +namespace boost { +namespace numeric { +namespace checked { + + namespace detail { + template + struct addition; + + // both arguments unsigned + template<> + struct addition { + template + static constexpr bool overflow(const R & r, const T & t, const U & u){ + return boost::numeric::safe_compare::less_than(r, t) + || boost::numeric::safe_compare::less_than(r, u); + } + template + static constexpr bool addition_overflow(const T & t, const U & u){ + return overflow(t + u, t, u); + } + template + static R add(const T & t, const U & u) { + R tmp = t + u; + if(overflow(tmp, t, u)) + boost::numeric::overflow("safe range addition result overflow"); + return tmp; + } + }; + // both arguments signed + template<> + struct addition { + template + static constexpr bool overflow(const R & r, const T & t, const U & u){ + return boost::numeric::safe_compare::less_than(r, t) + || boost::numeric::safe_compare::less_than(r, u); + } + template + static constexpr bool addition_overflow(const T & t, const U & u){ + return overflow(t + u, t, u); + } + template + static R add(const T & t, const U & u) { + if(t > 0 && u > 0){ + R tmp = t + u; + if(tmp < 0) + boost::numeric::overflow("safe range addition result overflow"); + return tmp; + } + if(t < 0 && u < 0){ + R tmp = t + u; + if(tmp >= 0) + boost::numeric::overflow("safe range addition result underflow"); + return tmp; + } + return t + u; + } + }; + // T unsigned, U signed + template<> + struct addition { + template + static R add(const T & t, const U & u){ + if(boost::numeric::is_unsigned::value){ + if(u < 0) + overflow("safe range right operand value altered"); + return addition::add( + t, + static_cast::type>(u) + ); + } + else{ + if(u > 0){ + R tmp = t + u; + if(tmp <= 0) + overflow("safe range addition result overflow"); + return t + u; + } + } + return t + u; + } + }; + // T signed, U unsigned + template<> + struct addition { + template + static R add(const T & t, const U & u){ + return addition::add(u, t); + } + }; + } // detail + + template + R add(const T & t, const U & u){ + return detail::addition< + boost::is_unsigned::value, + boost::is_unsigned::value + >::template add( + t, u + ); + } +} // checked + +namespace detail { + + //////////////////////////////////////////////////// + // layer 0 - detect overflows / alteration at the + // atomic operation level taking care to work around + // otherwise undetect alterations in integers due + // to machine architecture. Note presumption of twos + // complement integer arithmetic + + ///////////////////////////// + // subtraction implementation + template + struct check_subtraction_overflow{}; + + // both arguments signed + template<> + struct check_subtraction_overflow { + template + static BOOST_TYPEOF_TPL(T() + U()) + subtract(const T & t, const U & u){ + BOOST_AUTO_TPL(tmp, t - u); + if(t > 0 && u < 0){ + if(tmp < 0) + overflow("safe range subtraction result overflow"); + } + if(t < 0 && u > 0) + if(tmp >= 0){ + overflow("safe range subtraction result underflow"); + } + return tmp; + } + }; + // both arguments unsigned + template<> + struct check_subtraction_overflow { + template + static BOOST_TYPEOF_TPL(T() + U()) + subtract(const T & t, const U & u) { + if(safe_compare::less_than(t, u)) + overflow("safe range subtraction unsigned difference less than zero"); + return t - u; + } + }; + // T unsigned, U signed + template<> + struct check_subtraction_overflow { + template + static BOOST_TYPEOF_TPL(T() + U()) + subtract(const T & t, const U & u){ + typedef BOOST_TYPEOF_TPL(T() + U()) result_type; + if(boost::numeric::is_unsigned::value){ + if(u < 0) + overflow("safe range left operand value altered"); + // u >= 0 + if(u > t) + overflow("unsigned result is negative"); + } + // result is signed + return t - u; + } + }; + // T signed, U unsigned + template<> + struct check_subtraction_overflow { + template + static BOOST_TYPEOF_TPL(T() + U()) + subtract(const T & t, const U & u){ + typedef BOOST_TYPEOF_TPL(T() + U()) result_type; + if(boost::numeric::is_unsigned::value){ + return check_subtraction_overflow::subtract( + safe_cast(t), + safe_cast(u) + ); + } + // result is signed + return check_subtraction_overflow::subtract( + t, + safe_cast(u) + ); + } + }; + + //////////////////////////////// + // multiplication implementation + + template + BOOST_TYPEOF_TPL(T() * U()) + check_multiplication_overflow(const T & t, const U & u){ + typedef BOOST_TYPEOF_TPL(T() * U()) result_type; + char const * const msg = "safe range multiplication overflow"; + // presume that size of uintmax_t and intmax_t are the same + typedef bits available_bits; + + if(multiply_result_bits::value + <= boost::numeric::bits::value) + return t * u; + + if(multiply_result_bits::value <= available_bits::value){ + typedef typename multiply_result_type::type temp_type; + temp_type tmp = static_cast(t) * temp_type(u); + // the following works for both positive and negative results + // and for both signed and unsigned numbers + if(tmp > boost::integer_traits::const_max) + boost::numeric::overflow(msg); + if(tmp < boost::integer_traits::const_min) + boost::numeric::overflow(msg); + return static_cast(tmp); + } + + // when the there is no native type which can hold the product + // use multible precision + + // t is factored as (a << temp_bits) + b + // u is factored as (c << temp_bits) + d + // so we use multi-precision: + // a + b + // c + d + // ----- + // bd + // ad + // cb + // ac + // ----- + // .. + + if(boost::numeric::is_unsigned::value + && (t < 0 || u < 0)) + overflow("conversion of negative value to unsigned"); + + if(t == 1) + return u; + if(u == 1) + return t; + + result_type rt = t; + if(rt < 0){ + rt = ~rt + 1; + // address + if(rt < 0) + overflow("overflow of negative value"); + } + result_type ru = u; + if(ru < 0){ + ru = ~ru + 1; + // address + if(ru < 0) + overflow("overflow of negative value"); + } + + // check positive values for overflow + + // t is factored as (a << temp_bits) + b + // u is factored as (c << temp_bits) + d + // so we use multi-precision: + // a + b + // c + d + // ----- + // bd + // ad + // cb + // ac + // ----- + // .. + + typedef boost::uintmax_t accumulator_type; + const int temp_bits = bits::value / 2; + typedef typename boost::uint_t::least temp_type; + + temp_type a = (static_cast(rt) >> temp_bits); + temp_type c = (static_cast(ru) >> temp_bits); + if(0 != a && 0 != c) + overflow(msg); + + temp_type b = static_cast(rt); + if((static_cast(b) * static_cast(c) >> temp_bits) > 0) + overflow(msg); + + temp_type d = static_cast(ru); + if(0 != (static_cast(a) * static_cast(d) >> temp_bits)) + overflow(msg); + + return t * u; + } + template + BOOST_TYPEOF_TPL(T() / U()) + check_division_overflow(const T & t, const U & u){ + if(0 == u) + overflow("divide by zero"); + + if(boost::numeric::is_signed::value){ + // t unsigned, u signed + if(boost::numeric::is_unsigned::value){ + if(u < 0){ + overflow("conversion of negative value to unsigned"); + } + } + else{ + // both signed + // pathological case: change sign on negative number so it overflows + if(t == boost::integer_traits::const_min && u == -1) + overflow("overflow in result"); + } + } + // both unsigned + // t signed, u unsigned + return t / u; + } + template + BOOST_TYPEOF_TPL(T() / U()) + check_modulus_overflow(const T & t, const U & u){ + if(0 == u) + overflow("modulus divide by zero"); + + if(boost::numeric::is_signed::value){ + // t unsigned, u signed + if(boost::numeric::is_unsigned::value){ + if(u < 0){ + overflow("conversion of negative value to unsigned"); + } + } + else{ + // both signed + // pathological case: change sign on negative number so it overflows + if(t == boost::integer_traits::const_min && u == -1) + overflow("overflow in result"); + } + } + // both unsigned + // t signed, u unsigned + return t % u; + } + +} // detail + + +} // numeric +} // boost + +#endif // BOOST_NUMERIC__HPP diff --git a/include/native.hpp b/include/native.hpp index 2a404f5..dffdab2 100644 --- a/include/native.hpp +++ b/include/native.hpp @@ -34,7 +34,7 @@ struct native { typedef decltype( typename boost::numeric::base_type::type() + typename boost::numeric::base_type::type() - ) type; + ) native_result_type; }; }; diff --git a/include/safe_range.hpp b/include/safe_range.hpp index b5559c5..baa649f 100644 --- a/include/safe_range.hpp +++ b/include/safe_range.hpp @@ -12,6 +12,8 @@ // accompanying file LICENSE_1_0.txt or copy at // http://www.boost.org/LICENSE_1_0.txt) +#include "checked.hpp" + #include "numeric.hpp" #include "safe_compare.hpp" #include "safe_cast.hpp" @@ -53,102 +55,6 @@ #include namespace boost { -namespace numeric { - -namespace checked { - template - struct addition; - - // both arguments unsigned - template<> - struct addition { - template - static constexpr bool overflow(const R & r, const T & t, const U & u){ - return boost::numeric::safe_compare::less_than(r, t) - || boost::numeric::safe_compare::less_than(r, u); - } - template - static constexpr bool addition_overflow(const T & t, const U & u){ - return overflow(t + u, t, u); - } - template - static R add(const T & t, const U & u) { - R tmp = t + u; - if(overflow(tmp, t, u)) - boost::numeric::overflow("safe range addition result overflow"); - return tmp; - } - }; - // both arguments signed - template<> - struct addition { - template - static constexpr bool overflow(const R & r, const T & t, const U & u){ - return boost::numeric::safe_compare::less_than(r, t) - || boost::numeric::safe_compare::less_than(r, u); - } - template - static constexpr bool addition_overflow(const T & t, const U & u){ - return overflow(t + u, t, u); - } - template - static R add(const T & t, const U & u) { - if(t > 0 && u > 0){ - R tmp = t + u; - if(tmp < 0) - boost::numeric::overflow("safe range addition result overflow"); - return tmp; - } - if(t < 0 && u < 0){ - R tmp = t + u; - if(tmp >= 0) - boost::numeric::overflow("safe range addition result underflow"); - return tmp; - } - return t + u; - } - }; - // T unsigned, U signed - template<> - struct addition { - template - static R add(const T & t, const U & u){ - if(boost::numeric::is_unsigned::value){ - if(u < 0) - overflow("safe range right operand value altered"); - return addition::add( - t, - static_cast::type>(u) - ); - } - else{ - if(u > 0){ - R tmp = t + u; - if(tmp <= 0) - overflow("safe range addition result overflow"); - return t + u; - } - } - return t + u; - } - }; - // T signed, U unsigned - template<> - struct addition { - template - static R add(const T & t, const U & u){ - return addition::add(u, t); - } - }; - - template - R add(const T & t, const U & u){ - return addition::value, boost::is_unsigned::value> - ::template add( - t, u - ); - } -} namespace detail { //////////////////////////////////////////////////// @@ -172,11 +78,11 @@ namespace detail { BOOST_AUTO_TPL(tmp, t - u); if(t > 0 && u < 0){ if(tmp < 0) - overflow("safe range subtraction result overflow"); + boost::numeric::overflow("safe range subtraction result overflow"); } if(t < 0 && u > 0) if(tmp >= 0){ - overflow("safe range subtraction result underflow"); + boost::numeric::overflow("safe range subtraction result underflow"); } return tmp; } @@ -187,8 +93,8 @@ namespace detail { template static BOOST_TYPEOF_TPL(T() + U()) subtract(const T & t, const U & u) { - if(safe_compare::less_than(t, u)) - overflow("safe range subtraction unsigned difference less than zero"); + if(boost::numeric::safe_compare::less_than(t, u)) + boost::numeric::overflow("safe range subtraction unsigned difference less than zero"); return t - u; } }; @@ -201,10 +107,10 @@ namespace detail { typedef BOOST_TYPEOF_TPL(T() + U()) result_type; if(boost::numeric::is_unsigned::value){ if(u < 0) - overflow("safe range left operand value altered"); + boost::numeric::overflow("safe range left operand value altered"); // u >= 0 if(u > t) - overflow("unsigned result is negative"); + boost::numeric::overflow("unsigned result is negative"); } // result is signed return t - u; @@ -219,14 +125,14 @@ namespace detail { typedef BOOST_TYPEOF_TPL(T() + U()) result_type; if(boost::numeric::is_unsigned::value){ return check_subtraction_overflow::subtract( - safe_cast(t), - safe_cast(u) + boost::numeric::safe_cast(t), + boost::numeric::safe_cast(u) ); } // result is signed return check_subtraction_overflow::subtract( t, - safe_cast(u) + boost::numeric::safe_cast(u) ); } }; @@ -240,14 +146,14 @@ namespace detail { typedef BOOST_TYPEOF_TPL(T() * U()) result_type; char const * const msg = "safe range multiplication overflow"; // presume that size of uintmax_t and intmax_t are the same - typedef bits available_bits; + typedef boost::numeric::bits available_bits; - if(multiply_result_bits::value + if(boost::numeric::multiply_result_bits::value <= boost::numeric::bits::value) return t * u; - if(multiply_result_bits::value <= available_bits::value){ - typedef typename multiply_result_type::type temp_type; + if(boost::numeric::multiply_result_bits::value <= boost::numeric::available_bits::value){ + typedef typename boost::numeric::multiply_result_type::type temp_type; temp_type tmp = static_cast(t) * temp_type(u); // the following works for both positive and negative results // and for both signed and unsigned numbers @@ -276,7 +182,7 @@ namespace detail { if(boost::numeric::is_unsigned::value && (t < 0 || u < 0)) - overflow("conversion of negative value to unsigned"); + boost::numeric::overflow("conversion of negative value to unsigned"); if(t == 1) return u; @@ -288,14 +194,14 @@ namespace detail { rt = ~rt + 1; // address if(rt < 0) - overflow("overflow of negative value"); + boost::numeric::overflow("overflow of negative value"); } result_type ru = u; if(ru < 0){ ru = ~ru + 1; // address if(ru < 0) - overflow("overflow of negative value"); + boost::numeric::overflow("overflow of negative value"); } // check positive values for overflow @@ -314,21 +220,21 @@ namespace detail { // .. typedef boost::uintmax_t accumulator_type; - const int temp_bits = bits::value / 2; + const int temp_bits = boost::numeric::bits::value / 2; typedef typename boost::uint_t::least temp_type; temp_type a = (static_cast(rt) >> temp_bits); temp_type c = (static_cast(ru) >> temp_bits); if(0 != a && 0 != c) - overflow(msg); + boost::numeric::overflow(msg); temp_type b = static_cast(rt); if((static_cast(b) * static_cast(c) >> temp_bits) > 0) - overflow(msg); + boost::numeric::overflow(msg); temp_type d = static_cast(ru); if(0 != (static_cast(a) * static_cast(d) >> temp_bits)) - overflow(msg); + boost::numeric::overflow(msg); return t * u; } @@ -336,20 +242,20 @@ namespace detail { BOOST_TYPEOF_TPL(T() / U()) check_division_overflow(const T & t, const U & u){ if(0 == u) - overflow("divide by zero"); + boost::numeric::overflow("divide by zero"); if(boost::numeric::is_signed::value){ // t unsigned, u signed if(boost::numeric::is_unsigned::value){ if(u < 0){ - overflow("conversion of negative value to unsigned"); + boost::numeric::overflow("conversion of negative value to unsigned"); } } else{ // both signed // pathological case: change sign on negative number so it overflows if(t == boost::integer_traits::const_min && u == -1) - overflow("overflow in result"); + boost::numeric::overflow("overflow in result"); } } // both unsigned @@ -360,20 +266,20 @@ namespace detail { BOOST_TYPEOF_TPL(T() / U()) check_modulus_overflow(const T & t, const U & u){ if(0 == u) - overflow("modulus divide by zero"); + boost::numeric::overflow("modulus divide by zero"); if(boost::numeric::is_signed::value){ // t unsigned, u signed if(boost::numeric::is_unsigned::value){ if(u < 0){ - overflow("conversion of negative value to unsigned"); + boost::numeric::overflow("conversion of negative value to unsigned"); } } else{ // both signed // pathological case: change sign on negative number so it overflows if(t == boost::integer_traits::const_min && u == -1) - overflow("overflow in result"); + boost::numeric::overflow("overflow in result"); } } // both unsigned @@ -517,27 +423,27 @@ public: // comparison operators template bool operator<(const U & rhs) const { - return safe_compare::less_than(m_t, rhs); + return boost::numeric::safe_compare::less_than(m_t, rhs); } template bool operator>(const U & rhs) const { - return safe_compare::greater_than(m_t, rhs); + return boost::numeric::safe_compare::greater_than(m_t, rhs); } template bool operator==(const U & rhs) const { - return safe_compare::equal(m_t, rhs); + return boost::numeric::safe_compare::equal(m_t, rhs); } template bool inline operator!=(const U & rhs) const { - return ! safe_compare::equal(m_t,rhs); + return ! boost::numeric::safe_compare::equal(m_t,rhs); } template bool inline operator>=(const U & rhs) const { - return ! safe_compare::less_than(m_t, rhs); + return ! boost::numeric::safe_compare::less_than(m_t, rhs); } template bool inline operator<=(const U & rhs) const { - return ! safe_compare::greater_than(m_t, rhs); + return ! boost::numeric::safe_compare::greater_than(m_t, rhs); } ///////////////////////////////////////////////////////////////// @@ -577,7 +483,7 @@ public: return detail::check_subtraction_overflow< boost::numeric::is_signed::value, boost::numeric::is_signed::value - >::subtract(m_t, safe_cast(rhs)); + >::subtract(m_t, boost::numeric::safe_cast(rhs)); } ///////////////////////////////////////////////////////////////// // multiplication @@ -646,10 +552,10 @@ public: "right hand side is not an integer type" ); if(m_t < 0) - overflow("right shift of negative number undefined"); + boost::numeric::overflow("right shift of negative number undefined"); typedef BOOST_TYPEOF_TPL(Stored() >> U()) result_type; if(rhs > boost::numeric::bits::value) - overflow("conversion of negative value to unsigned"); + boost::numeric::overflow("conversion of negative value to unsigned"); return m_t >> rhs; } @@ -661,10 +567,10 @@ public: "right hand side is not an integer type" ); if(m_t < 0) - overflow("right shift of negative number undefined"); + boost::numeric::overflow("right shift of negative number undefined"); typedef BOOST_TYPEOF_TPL(Stored() >> U()) result_type; if(rhs > boost::numeric::bits::value) - overflow("conversion of negative value to unsigned"); + boost::numeric::overflow("conversion of negative value to unsigned"); return m_t << rhs; } @@ -679,7 +585,7 @@ public: // ... // --x; if(boost::integer_traits::const_max == m_t) - overflow("safe range unary negation overflow"); + boost::numeric::overflow("safe range unary negation overflow"); return -m_t; } @@ -712,7 +618,7 @@ struct get_policy { typedef typename boost::mpl::if_< is_safe, - typename boost::numeric::get_policy_t, + typename get_policy_t, boost::mpl::identity >::type::type policy_t; @@ -720,7 +626,7 @@ struct get_policy { typedef typename boost::mpl::if_< is_safe, - typename boost::numeric::get_policy_t, + typename get_policy_t, boost::mpl::identity >::type::type policy_u;