From 12f5843f90ac320bea2b68b715030b9fbe665da2 Mon Sep 17 00:00:00 2001 From: Robert Ramey Date: Sat, 5 Apr 2014 12:22:25 -0700 Subject: [PATCH] Fixed problems in testing addition operations More carefully defined the concepts. That is the concept of the library is now clear: Trap at either compile or runtime integer operations which yield a result which differs from the normal arithmetic result. Pending on this check in: a) only addition is fixed up so far b) uses BOOST_TYPEOF... rather than decltype(... - final decision sill pending. c) comments out some compile time checks which should be re-instated. d) fixed tests so that they actually work! --- CMake/CMakeLists.txt | 34 +++-- doc/boostbook/numeric_concept.xml | 4 +- doc/boostbook/safe_numerics.xml | 12 +- doc/boostbook/safe_signed_range.xml | 14 ++ doc/boostbook/safe_unsigned_range.xml | 17 ++- include/numeric.hpp | 1 + include/safe_cast.hpp | 45 ++++--- include/safe_integer.hpp | 41 +++--- include/safe_range.hpp | 128 +++++++++++++----- test/test.cpp | 2 +- test/test.hpp | 132 ++++--------------- test/test_add.cpp | 143 ++++++++++++++++++-- test/test_add.hpp | 156 +++------------------- test/test_add1.cpp | 37 ------ test/test_add3.cpp | 41 ------ test/test_cast.cpp | 82 ++++++++++++ test/test_conversion.cpp | 174 ++++++------------------ test/test_subtract.cpp | 16 +-- test/test_subtract.hpp | 182 ++++++-------------------- test/test_types.hpp | 22 ++++ test/test_values.hpp | 48 +++++++ 21 files changed, 629 insertions(+), 702 deletions(-) delete mode 100644 test/test_add1.cpp delete mode 100644 test/test_add3.cpp create mode 100644 test/test_cast.cpp create mode 100644 test/test_types.hpp create mode 100644 test/test_values.hpp diff --git a/CMake/CMakeLists.txt b/CMake/CMakeLists.txt index 4c05aa3..c4c1b22 100644 --- a/CMake/CMakeLists.txt +++ b/CMake/CMakeLists.txt @@ -115,12 +115,25 @@ link_directories("${Boost_LIBRARY_DIRS}") ########################### ########################### -# testing +# testing and submitting test results -enable_testing() +include (CTest) + +# the include above includes enable_testing() so the following line isn't +# necessary +# enable_testing() + +message(STATUS "test_cast") +add_executable( test_cast + ../test/test_cast.hpp + ../test/test_cast.cpp +) +target_link_libraries( test_cast ${Boost_LIBRARIES} ) +add_test(NAME test_cast COMMAND test_cast) message(STATUS "test_add") - add_executable( test_add +add_executable( test_add + ../test/test_add.hpp ../test/test_add.cpp ../test/test_add1.cpp ../test/test_add2.cpp @@ -128,10 +141,10 @@ message(STATUS "test_add") ) target_link_libraries( test_add ${Boost_LIBRARIES} ) add_test(NAME test_add COMMAND test_add) -add_custom_target(test_add_header SOURCES ../test/test_add.hpp) message(STATUS "test_subtract") -add_executable( test_subtract +add_executable( test_subtract + ../test/test_subtract.hpp ../test/test_subtract.cpp ../test/test_subtract1.cpp ../test/test_subtract2.cpp @@ -139,10 +152,10 @@ add_executable( test_subtract ) target_link_libraries( test_subtract ${Boost_LIBRARIES} ) add_test(NAME test_subtract COMMAND test_subtract) -add_custom_target(test_subtract_header SOURCES ../test/test_subtract.hpp) message(STATUS "test_multiply") -add_executable( test_multiply +add_executable( test_multiply + ../test/test_multiply.hpp ../test/test_multiply.cpp ../test/test_multiply1.cpp ../test/test_multiply2.cpp @@ -150,10 +163,10 @@ add_executable( test_multiply ) target_link_libraries( test_multiply ${Boost_LIBRARIES} ) add_test(NAME test_multiply COMMAND test_multiply) -add_custom_target(test_multiply_header SOURCES ../test/test_multiply.hpp) message(STATUS "test_divide") -add_executable( test_divide +add_executable( test_divide + ../test/test_divide.hpp ../test/test_divide.cpp ../test/test_divide1.cpp ../test/test_divide2.cpp @@ -161,10 +174,10 @@ add_executable( test_divide ) target_link_libraries( test_divide ${Boost_LIBRARIES} ) add_test(NAME test_divide COMMAND test_divide) -add_custom_target(test_divide_header SOURCES ../test/test_divide.hpp) message(STATUS "test_modulus") add_executable( test_modulus + ../test/test_modulus.hpp ../test/test_modulus.cpp ../test/test_modulus1.cpp ../test/test_modulus2.cpp @@ -172,7 +185,6 @@ add_executable( test_modulus ) target_link_libraries( test_modulus ${Boost_LIBRARIES} ) add_test(NAME test_modulus COMMAND test_modulus) -add_custom_target(test_modulus_header SOURCES ../test/test_modulus.hpp) message(STATUS "test_compare") add_executable( test_compare ../test/test_compare.cpp) diff --git a/doc/boostbook/numeric_concept.xml b/doc/boostbook/numeric_concept.xml index f82028d..6644b93 100644 --- a/doc/boostbook/numeric_concept.xml +++ b/doc/boostbook/numeric_concept.xml @@ -228,7 +228,7 @@ V - Invert sign + subtract u from t @@ -236,7 +236,7 @@ V - unary plus - a no op + add u to t diff --git a/doc/boostbook/safe_numerics.xml b/doc/boostbook/safe_numerics.xml index aae6196..ad36419 100644 --- a/doc/boostbook/safe_numerics.xml +++ b/doc/boostbook/safe_numerics.xml @@ -65,8 +65,8 @@ - I was a lot of code in one header - 6400 lines. Very unwieldy to - understand and modify. + It was a lot of code in one header - 6400 lines. Very unwieldy + to understand and modify. @@ -75,12 +75,12 @@ - I didn't use Boost + It didn't use Boost conventions for naming. - I required porting to different compilers. + It required porting to different compilers. @@ -150,7 +150,7 @@ Why does a binary operation on two - safe<int> values not necessarily return another + safe<int> values not return another safe type ? @@ -159,7 +159,7 @@ - it was hard to implement. + it was too hard to implement. diff --git a/doc/boostbook/safe_signed_range.xml b/doc/boostbook/safe_signed_range.xml index 4a01ea0..a69baaa 100644 --- a/doc/boostbook/safe_signed_range.xml +++ b/doc/boostbook/safe_signed_range.xml @@ -66,6 +66,12 @@ Model of Numeric + + The usage of this type in an arithmetic expression will result in + another type fulfilling the Numeric concept. This will be the + smallest signed integer type of sufficient size to hold the result of the + operation.
@@ -85,6 +91,12 @@ void f(){ i = 0; // error i = 9; // ok i *= 9; // throws overflow exception + + std::int8_t j = 4; + auto k = i + j; + // since i can vary between 7 and 24 and j can vary between 0 and 255 + // the smallest unsigned integer which can hold the result std::int16_t + // j will be of type std::int16_t }
@@ -92,5 +104,7 @@ void f(){ See Also std::out_of_range + + safe_unsigned_range diff --git a/doc/boostbook/safe_unsigned_range.xml b/doc/boostbook/safe_unsigned_range.xml index 2e12ad0..6221bb7 100644 --- a/doc/boostbook/safe_unsigned_range.xml +++ b/doc/boostbook/safe_unsigned_range.xml @@ -9,7 +9,7 @@ This type holds a integer in the range [MIN, MAX]. It will throw a std::out_of_range exception for any operation which would - result in assigning an integer value outside of this range. + result in assigning an integer value outside of this range.
@@ -66,6 +66,12 @@ Model of Numeric + + The usage of this type in an arithmetic expression with another + unsigned type will result in another unsigned type fulfilling the Numeric concept. This will be the + smallest unsigned integer type of sufficient size to hold the result of + the operation.
@@ -86,6 +92,13 @@ void f(){ i = 9; // ok i *= 9; // throws out_of_range exception i = -1; // throws out_of_range exception + + std::uint8_t j = 4; + auto k = i + j; + // since i can vary between 7 and 24 and j can vary between 0 and 255 + // the smallest unsigned integer which can hold the result std::uint16_t + // j will be of type std::uint16_t + }
@@ -93,5 +106,7 @@ void f(){ See Also std::out_of_range + + safe_signed_range diff --git a/include/numeric.hpp b/include/numeric.hpp index 0337f7f..970f41f 100644 --- a/include/numeric.hpp +++ b/include/numeric.hpp @@ -187,6 +187,7 @@ struct max_bits : public >::type {}; + // return # of bit in the result of an addition of two types template struct addition_result_bits : public diff --git a/include/safe_cast.hpp b/include/safe_cast.hpp index 917ac3d..d71c14e 100644 --- a/include/safe_cast.hpp +++ b/include/safe_cast.hpp @@ -16,42 +16,57 @@ #include #include #include +#include +#include + #include "numeric.hpp" #include "overflow.hpp" +#include "safe_compare.hpp" namespace boost { namespace numeric { namespace detail { - // simple case when signess are the same. + // default signature template - struct safe_cast { + struct safe_cast; + + // T signed <- U signed + template<> + struct safe_cast { template inline static T invoke(const U & u){ - if(u > std::numeric_limits::max()) + if(safe_compare::greater_than(u, std::numeric_limits::max())) overflow("safe range overflow"); - if(u < std::numeric_limits::min()) + + if(safe_compare::less_than(u, std::numeric_limits::min())) overflow("safe range underflow"); return static_cast(u); } }; + // T unsigned <- U unsigned + template<> + struct safe_cast { + template + inline static T invoke(const U & u){ + 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); + } + + }; + + // T signed <- U unsigned template<> struct safe_cast { template struct sbits : public boost::mpl::min< - typename boost::mpl::integral_c< - int, - std::numeric_limits::digits - >, - typename boost::mpl::plus< - typename boost::mpl::integral_c< - int, - std::numeric_limits::digits - >, - typename boost::mpl::integral_c - > + boost::numeric::bits, + boost::numeric::bits >::type {}; template diff --git a/include/safe_integer.hpp b/include/safe_integer.hpp index 79d1a38..806fa6a 100644 --- a/include/safe_integer.hpp +++ b/include/safe_integer.hpp @@ -12,30 +12,34 @@ // accompanying file LICENSE_1_0.txt or copy at // http://www.boost.org/LICENSE_1_0.txt) -#include -#include -#include "safe_range.hpp" - #include // BOOST_NOEXCEPT +#include +#include +#include + +#include "safe_range.hpp" +#include "numeric.hpp" namespace boost { namespace numeric { namespace detail{ - template - struct safe_integer_base { - typedef typename boost::mpl::if_c< - std::numeric_limits::is_signed, - safe_signed_range< - boost::integer_traits::const_min, - boost::integer_traits::const_max - >, - safe_unsigned_range< - boost::integer_traits::const_min, - boost::integer_traits::const_max - > - >::type type; - }; + +template +struct safe_integer_base { + typedef typename boost::mpl::if_< + boost::numeric::is_signed, + boost::numeric::safe_signed_range< + static_cast(boost::integer_traits::const_min), + static_cast(boost::integer_traits::const_max) + >, + boost::numeric::safe_unsigned_range< + static_cast(boost::integer_traits::const_min), + static_cast(boost::integer_traits::const_max) + > + >::type type; +}; + } // detail template @@ -79,5 +83,4 @@ public: } // std - #endif // BOOST_NUMERIC_SAFE_INTEGER_HPP diff --git a/include/safe_range.hpp b/include/safe_range.hpp index 92040e0..31eed3a 100644 --- a/include/safe_range.hpp +++ b/include/safe_range.hpp @@ -12,7 +12,6 @@ // accompanying file LICENSE_1_0.txt or copy at // http://www.boost.org/LICENSE_1_0.txt) -//#include #include "numeric.hpp" #include "safe_compare.hpp" #include "safe_cast.hpp" @@ -25,17 +24,25 @@ #include #include #include +#include +#include #include #include #include #include #include +#include +#include #include #include #include +// 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 { @@ -47,12 +54,11 @@ namespace detail { template<> struct check_addition_overflow { template - static typename addition_result_type::type - add(const T & t, const U & u){ - typedef typename addition_result_type::type result_type; - result_type tmp; - tmp = static_cast(t) + static_cast(u); - if(safe_compare::less_than(tmp, t)) + static BOOST_TYPEOF_TPL(T() + U()) + add(const T & t, const U & u) { + BOOST_AUTO_TPL(tmp, t + u); + if(safe_compare::less_than(tmp, t) + || safe_compare::less_than(tmp, u)) overflow("safe range addition out of range"); return tmp; } @@ -61,38 +67,84 @@ namespace detail { template<> struct check_addition_overflow { template - static typename addition_result_type::type + static BOOST_TYPEOF_TPL(T() + U()) add(const T & t, const U & u){ - if(u > 0) - return check_addition_overflow::add(t, u); + typedef BOOST_TYPEOF_TPL(T() + U()) result_type; + if(boost::numeric::is_unsigned::value){ + if(u < 0) + overflow("safe range addition out of range"); + else + //if(u > 0) + return check_addition_overflow::add( + t, + static_cast::type>(u) + ); + } + else{ + if(u > 0){ + auto tmp = t + u; + if(tmp <= 0) + overflow("safe range addition out of range"); + return t + u; + } + } return t + u; + /* + if(u > 0) + return check_addition_overflow::add( + t, + static_cast::type>(u) + ); + BOOST_AUTO_TPL(tmp, t + u); + return tmp; + */ } }; // T signed, U unsigned template<> struct check_addition_overflow { template - static typename addition_result_type::type + static BOOST_TYPEOF_TPL(T() + U()) add(const T & t, const U & u){ - if(t > 0) - return check_addition_overflow::add(t, u); - return t + u; + typedef BOOST_TYPEOF_TPL(T() + U()) result_type; + if(boost::numeric::is_unsigned::value){ + if(t < 0) + overflow("safe range addition out of range"); + else + //if(t > 0) + return check_addition_overflow::add( + static_cast::type>(t), + u + ); + } + else{ + if(t > 0){ + auto tmp = t + u; + if(tmp < 0) + overflow("safe range addition out of range"); + } + } + + BOOST_AUTO_TPL(tmp, t + u); + return tmp; } }; // both arguments signed template<> struct check_addition_overflow { template - static typename addition_result_type::type + static BOOST_TYPEOF_TPL(T() + U()) add(const T & t, const U & u){ - if(t > 0) - check_addition_overflow::add(t, u); - if(u < 0){ - typedef typename addition_result_type::type result_type; - result_type tmp; - tmp = static_cast(t) + static_cast(u); - if(safe_compare::greater_than(tmp, t)) - overflow("safe range addition out of range"); + if(t > 0 && u > 0){ + auto tmp = t + u; + if(tmp < 0) + overflow("safe range addition out of range"); + return tmp; + } + if(t < 0 && u < 0){ + auto tmp = t + u; + if(tmp >= 0) + overflow("safe range addition out of range"); return tmp; } return t + u; @@ -309,29 +361,28 @@ public: ///////////////////////////////////////////////////////////////// // addition // case 1 - no overflow possible +#if 0 template typename boost::enable_if< - typename boost::mpl::less_equal< - addition_result_bits, - // note presumption that size(boost::uintmax) == size(boost::intmax) - bits + typename boost::mpl::greater< + typename boost::mpl::sizeof_< BOOST_TYPEOF_TPL(U() + Stored()) >, + typename boost::mpl::sizeof_ >, - typename addition_result_type::type + BOOST_TYPEOF_TPL(U() + Stored()) >::type inline operator+(const U & rhs) const { - typedef typename addition_result_type::type result_type; - return static_cast(m_t) + static_cast(rhs); + return m_t + rhs; } // case 2 - overflow possible - must be checked at run time template typename boost::enable_if< typename boost::mpl::greater< - addition_result_bits, + bits, // note presumption that size(boost::uintmax) == size(boost::intmax) bits >, - typename addition_result_type::type + BOOST_TYPEOF_TPL(U() + Stored()) >::type inline operator+(const U & rhs) const { return detail::check_addition_overflow< @@ -339,6 +390,15 @@ public: boost::numeric::is_signed::value >::add(m_t, rhs); } +#endif + template + BOOST_TYPEOF_TPL(U() + Stored()) + inline operator+(const U & rhs) const { + return detail::check_addition_overflow< + boost::numeric::is_signed::value, + boost::numeric::is_signed::value + >::add(m_t, rhs); + } ///////////////////////////////////////////////////////////////// // subtraction template @@ -754,9 +814,7 @@ public: template stored_type validate(const T & t) const { const boost::uintmax_t tx = t; - if(MAX < tx - || MIN > tx - ) + if(MAX < tx) overflow("safe range out of range"); return static_cast(t); } diff --git a/test/test.cpp b/test/test.cpp index 3e8bb47..9448b6b 100644 --- a/test/test.cpp +++ b/test/test.cpp @@ -8,6 +8,7 @@ #include // EXIT_SUCCESS #include + #include "../include/safe_range.hpp" #include "../include/safe_integer.hpp" @@ -36,7 +37,6 @@ bool test1(){ int >::type >::type t3; - zi = x + yi; bool success = false; diff --git a/test/test.hpp b/test/test.hpp index b5f4c4a..56d4c43 100644 --- a/test/test.hpp +++ b/test/test.hpp @@ -1,116 +1,40 @@ -#include -#include -#include +#ifndef SAFE_NUMERIC_TEST_HPP +#define SAFE_NUMERIC_TEST_HPP +// MS compatible compilers support #pragma once +#if defined(_MSC_VER) +# 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) #include #include #include -#include -#include -#include -#include -#include -#include -#include "../include/safe_integer.hpp" -#include "../include/safe_cast.hpp" +#define VALUE_ARRAY_SIZE BOOST_PP_ARRAY_SIZE(VALUES) -#define VALUES (20, ( \ - 0x10, \ - 0x20, \ - 0x7f, \ - 0x80, \ - 0xff, \ - 0x1000, \ - 0x2000, \ - 0x7fff, \ - 0x8000, \ - 0xffff, \ - 0x10000000, \ - 0x20000000, \ - 0x7fffffff, \ - 0x80000000, \ - 0xffffffff, \ - 0x100000000000, \ - 0x200000000000, \ - 0x7fffffffffff, \ - 0x80000000ffff, \ - 0xffffffffffff \ -)) +#define TEST_EACH_VALUE2(z, value_index2, value_index1) \ + TESTX(value_index1, value_index2) /**/ -inline unsigned int -count_bits(boost::uintmax_t t){ - unsigned int i = 0; - while(t != 0){ - ++i; - t >>= 1; - } - return i; -} - -inline unsigned int -count_bits(boost::intmax_t t){ - if(t < 0) - t = -t; - unsigned int i = 0; - while(t != 0){ - ++i; - t >>= 1; - } - return i; -} - -#define EACH_VALUE2(z, l, list) \ - BOOST_PP_EXPAND( \ - TESTX \ - BOOST_PP_LIST_TO_TUPLE( \ - BOOST_PP_LIST_CONS( \ - BOOST_PP_ARRAY_ELEM(l, VALUES), \ - list \ - ) \ - ) \ - ) \ +#define TEST_EACH_VALUE1(z, value_index1, nothing) \ + BOOST_PP_REPEAT( \ + BOOST_PP_ARRAY_SIZE(VALUES), \ + TEST_EACH_VALUE2, \ + value_index1 \ + ) /**/ -#define EACH_VALUE1(z, k, types) \ - BOOST_PP_REPEAT( \ - BOOST_PP_ARRAY_SIZE(VALUES), \ - EACH_VALUE2, \ - BOOST_PP_LIST_CONS( \ - BOOST_PP_ARRAY_ELEM(k, VALUES), \ - types \ - ) \ - ) \ -/**/ - -#define EACH_TYPE2(i, j) \ - BOOST_PP_REPEAT( \ - BOOST_PP_ARRAY_SIZE(VALUES), \ - EACH_VALUE1, \ - (i, (j, BOOST_PP_NIL)) \ - ) \ -/**/ - -#define EACH_TYPE1(i) \ - EACH_TYPE2(i, boost::int8_t) \ - EACH_TYPE2(i, boost::uint8_t) \ - EACH_TYPE2(i, boost::int16_t) \ - EACH_TYPE2(i, boost::uint16_t) \ - EACH_TYPE2(i, boost::int32_t) \ - EACH_TYPE2(i, boost::uint32_t) \ - EACH_TYPE2(i, boost::int64_t) \ - EACH_TYPE2(i, boost::uint64_t) \ -/**/ - -#define TEST \ - EACH_TYPE1(boost::int8_t) \ - EACH_TYPE1(boost::uint8_t) \ - EACH_TYPE1(boost::int16_t) \ - EACH_TYPE1(boost::uint16_t) \ - EACH_TYPE1(boost::int32_t) \ - EACH_TYPE1(boost::uint32_t) \ - EACH_TYPE1(boost::int64_t) \ - EACH_TYPE1(boost::uint64_t) \ +#define TEST_EACH_VALUE_PAIR \ + BOOST_PP_REPEAT( \ + BOOST_PP_ARRAY_SIZE(VALUES), \ + TEST_EACH_VALUE1, \ + 0 \ + ) /**/ +#endif diff --git a/test/test_add.cpp b/test/test_add.cpp index d023267..146a3bf 100644 --- a/test/test_add.cpp +++ b/test/test_add.cpp @@ -4,14 +4,139 @@ // accompanying file LICENSE_1_0.txt or copy at // http://www.boost.org/LICENSE_1_0.txt) -bool test_add1(); -bool test_add2(); -bool test_add3(); +#include +#include -int main(int argc, char * argv[]){ - bool result = true; - result = test_add1(); - result &= test_add2(); - result &= test_add3(); - return ! result ; +// 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 + +#include "../include/safe_integer.hpp" + +template +bool test_add( + T1 v1, + T2 v2, + const char *av1, + const char *av2, + char expected_result +){ + std::cout + << "testing " + << av1 << " + " << av2 + << std::endl; + + boost::numeric::safe t1 = v1; + BOOST_TYPEOF_TPL(T1() +T2()) result; + + try{ + result = t1 + v2; + + if(expected_result == 'x'){ + std::cout + << "failed to detect error in addition " + << std::hex << result << "(" << std::dec << result << ")" + << " ! = "<< av1 << " + " << av2 + << std::endl; + try{ + result = t1 + v2; + } + catch(...){} + return false; + } + } + catch(std::range_error){ + if(expected_result == '.'){ + std::cout + << "erroneously detected error in addition " + << std::hex << result << "(" << std::dec << result << ")" + << " == "<< av1 << " + " << av2 + << std::endl; + try{ + result = t1 + v2; + } + catch(...){} + return false; + } + } + + return true; // correct result +} + +#include "test.hpp" +#include "test_values.hpp" + +const char *test_addition_result[VALUE_ARRAY_SIZE] = { +// 0 0 0 0 +// 01234567012345670123456701234567 +// 01234567890123456789012345678901 +/* 0*/ ".........x...x.............x...x", +/* 1*/ ".........x...x.............x...x", +/* 2*/ "..........x...x.........xxxxxxxx", +/* 3*/ "..........x...x.........xxxxxxxx", +/* 4*/ ".........x...x.............x...x", +/* 5*/ ".........x...x.............x...x", +/* 6*/ "..........x...x.........xxxxxxxx", +/* 7*/ "..........x...x.........xxxxxxxx", + +/* 8*/ ".........x...x.............x...x", +/* 9*/ "xx..xx..xx...x..xxxxxxxx...x...x", +/*10*/ "..xx..xx..xx..x.........xxxxxxxx", +/*11*/ "..........x...x.........xxxxxxxx", +/*12*/ ".............x.................x", +/*13*/ "xx..xx..xx..xx..xxxxxxxxxxxx...x", +/*14*/ "..xx..xx..xx..xx............xxxx", +/*15*/ "..............x.............xxxx", + +// 0 0 0 0 +// 01234567012345670123456701234567 +// 01234567890123456789012345678901 +/*16*/ ".........x...x.............x...x", +/*17*/ ".........x...x.............x...x", +/*18*/ ".........x...x.............x...x", +/*19*/ ".........x...x.............x...x", +/*20*/ ".........x...x.............x...x", +/*21*/ ".........x...x.............x...x", +/*22*/ ".........x...x.............x...x", +/*23*/ ".........x...x.............x...x", + +/*24*/ "..xx..xx..xx.x.............x...x", +/*25*/ "..xx..xx..xx.x.............x...x", +/*26*/ "..xx..xx..xx.x............xx...x", +/*27*/ "xxxxxxxxxxxx.x..xxxxxxxxxxxx...x", +/*28*/ "..xx..xx..xx..xx...............x", +/*29*/ "..xx..xx..xx..xx...............x", +/*30*/ "..xx..xx..xx..xx..............xx", +/*31*/ "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" +}; + +#define TEST_IMPL(v1, v2, result) \ + rval &= test_add( \ + v1, \ + v2, \ + BOOST_PP_STRINGIZE(v1), \ + BOOST_PP_STRINGIZE(v2), \ + result \ + ); +/**/ + +#define TESTX(value_index1, value_index2) \ + (std::cout << value_index1 << ',' << value_index2 << ','); \ + TEST_IMPL( \ + BOOST_PP_ARRAY_ELEM(value_index1, VALUES), \ + BOOST_PP_ARRAY_ELEM(value_index2, VALUES), \ + test_addition_result[value_index1][value_index2] \ + ) +/**/ + +#define COUNT sizeof(test_addition_result) +int main(int argc, char * argv[]){ + // sanity check on test matrix - should be symetrical + for(int i = 0; i < VALUE_ARRAY_SIZE; ++i) + for(int j = i + 1; j < VALUE_ARRAY_SIZE; ++j) + assert(test_addition_result[i][j] == test_addition_result[j][i]); + + bool rval = true; + TEST_EACH_VALUE_PAIR + return ! rval ; } diff --git a/test/test_add.hpp b/test/test_add.hpp index a48ade2..baba6b1 100644 --- a/test/test_add.hpp +++ b/test/test_add.hpp @@ -13,166 +13,52 @@ // http://www.boost.org/LICENSE_1_0.txt) #include -#include -#include -#include +#include // max +#include "../include/numeric.hpp" #include "../include/safe_integer.hpp" -#include "../include/safe_cast.hpp" -#include "../include/safe_compare.hpp" #include "test.hpp" -template +template bool test_add( - V v1, - V v2, - const char *at1, - const char *at2, + T1 v1, + T2 v2, const char *av1, - const char *av2 + const char *av2, + char expected_result ){ - bool success; + std::cout + << "testing " + << av1 << " + " << av2 + << std::endl; - T1 t1; - try{ - t1 = boost::numeric::safe_cast(v1); - success = true; - } - catch(std::range_error){ - success = false; - } - if(success){ - if(boost::numeric::safe_compare::greater_than( - v1, - std::numeric_limits::max() - ) - || boost::numeric::safe_compare::less_than( - v1, - std::numeric_limits::min() - ) - ){ - std::cout - << "constructed invalid value " - << at1 << ' ' << av1 - << std::endl; - return false; - } - } - else{ - if(! boost::numeric::safe_compare::greater_than( - v1, - std::numeric_limits::max() - ) - && ! boost::numeric::safe_compare::less_than( - v1, - std::numeric_limits::min() - ) - ){ - std::cout - << "failed to construct valid value " - << at1 << ' ' << av1 - << std::endl; - return false; - } - return true; - } + typename boost::numeric::addition_result_type::type result; - T2 t2; try{ - t2 = boost::numeric::safe_cast(v2); - success = true; - } - catch(std::range_error){ - success = false; - } - if(success){ - if(boost::numeric::safe_compare::greater_than( - v2, - std::numeric_limits::max() - ) - || boost::numeric::safe_compare::less_than( - v2, - std::numeric_limits::min() - )){ - std::cout - << "constructed invalid value " - << at2 << ' ' << av2 - << std::endl; - return false; - } - } - else{ - if(!boost::numeric::safe_compare::greater_than( - v2, - std::numeric_limits::max() - ) - && !boost::numeric::safe_compare::less_than( - v2, - std::numeric_limits::min() - )){ - std::cout - << "failed to construct valid value " - << at2 << ' ' << av2 - << std::endl; - return false; - } - return true; - } + boost::numeric::safe t1 = v1; + result = t1 + v2; - V result; - try{ - result = t1 + t2; - success = true; - } - catch(std::range_error){ - success = false; - } - if(success){ - if(result != v1 + v2){ + if(expected_result == 'x'){ std::cout << "failed to detect error in addition " - << at1 << ' ' << at2 << ' ' << av1 << ' ' << av2 + << av1 << " + " << av2 << std::endl; + result = t1 + v2; return false; } } - else{ - if(boost::numeric::safe_compare::greater_than_equal( - boost::numeric::bits::value, - std::max(count_bits(v1),count_bits(v2)) - )){ + catch(std::range_error){ + if(expected_result != 'x'){ std::cout << "erroneously detected error in addition " - << at1 << ' ' << at2 << ' ' << av1 << ' ' << av2 + << av1 << " + " << av2 << std::endl; return false; } } + return true; // correct result } -template -struct add_result { - typedef typename boost::mpl::if_< - boost::mpl::or_< - boost::numeric::is_signed, - boost::numeric::is_signed - >, - boost::intmax_t, - boost::uintmax_t - >::type type; -}; - -#define TEST_IMPL(a, b, c, d) \ - rval &= test_add( \ - (static_cast::type>(a)), \ - (static_cast::type>(b)), \ - BOOST_PP_STRINGIZE(d), \ - BOOST_PP_STRINGIZE(c), \ - BOOST_PP_STRINGIZE(b), \ - BOOST_PP_STRINGIZE(a) \ - ); \ -/**/ - #endif // TEST_ADD_HPP diff --git a/test/test_add1.cpp b/test/test_add1.cpp deleted file mode 100644 index c670746..0000000 --- a/test/test_add1.cpp +++ /dev/null @@ -1,37 +0,0 @@ -// 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 "test_add.hpp" - -#define TESTX(a, b, c, d) \ - TEST_IMPL( \ - a, \ - b, \ - boost::numeric::safe, \ - boost::numeric::safe \ - ) \ -/**/ - -bool test_add1(){ - bool rval = true; - #pragma message("0") - EACH_TYPE1(boost::int8_t); - #pragma message("1") - EACH_TYPE1(boost::uint8_t); - #pragma message("2") - EACH_TYPE1(boost::int16_t); - #pragma message("3") - EACH_TYPE1(boost::uint16_t); - #pragma message("4") - EACH_TYPE1(boost::int32_t); - #pragma message("5") - EACH_TYPE1(boost::uint32_t); - #pragma message("6") - EACH_TYPE1(boost::int64_t); - #pragma message("7") - EACH_TYPE1(boost::uint64_t); - return rval; -} diff --git a/test/test_add3.cpp b/test/test_add3.cpp deleted file mode 100644 index 94a9020..0000000 --- a/test/test_add3.cpp +++ /dev/null @@ -1,41 +0,0 @@ -// 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 "test_add.hpp" - -#define TESTX(a, b, c, d) \ - TEST_IMPL( \ - a, \ - b, \ - c, \ - boost::numeric::safe \ - ) \ -/**/ - -bool test_add3(){ - bool rval = true; - #pragma message("0") - EACH_TYPE1(boost::int8_t); - #if 0 - #pragma message("1") - EACH_TYPE1(boost::uint8_t); - #pragma message("2") - EACH_TYPE1(boost::int16_t); - #pragma message("3") - EACH_TYPE1(boost::uint16_t); - #pragma message("4") - EACH_TYPE1(boost::int32_t); - #pragma message("5") - EACH_TYPE1(boost::uint32_t); - #pragma message("6") - EACH_TYPE1(boost::int64_t); - #pragma message("7") - EACH_TYPE1(boost::uint64_t); - #endif - - return rval; -} - diff --git a/test/test_cast.cpp b/test/test_cast.cpp new file mode 100644 index 0000000..e7ebca5 --- /dev/null +++ b/test/test_cast.cpp @@ -0,0 +1,82 @@ +// 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 +#include +#include +#include +#include +#include +#include + +#include + +#include "../include/safe_cast.hpp" +#include "../include/safe_compare.hpp" +#include "../include/safe_integer.hpp" + +#include "test_types.hpp" +#include "test_values.hpp" + +// test conversion to T2 from different literal types +template +bool test_cast(V v, const char *t_name, const char *v_name){ + /* test conversion constructor to T1 */ + T1 t1; + try{ + t1 = boost::numeric::safe_cast(v); + if(! boost::numeric::safe_compare::equal(t1, v)){ + std::cout + << "failed to detect error in construction " + << t_name << "<-" << v_name + << std::endl; + return false; + } + } + catch(std::range_error e){ + if(boost::numeric::safe_compare::equal(t1, v)){ + std::cout + << "failed to detect error in construction " + << t_name << "<-" << v_name + << std::endl; + return false; + } + } + return true; // passed test +} + +#define TEST_CAST(T1, v) \ + test_cast( \ + v, \ + BOOST_PP_STRINGIZE(T1), \ + BOOST_PP_STRINGIZE(v) \ + ); +/**/ + +#define EACH_VALUE(z, value_index, type_index) \ + TEST_CAST( \ + BOOST_PP_ARRAY_ELEM(type_index, TYPES), \ + BOOST_PP_ARRAY_ELEM(value_index, VALUES) \ + ) \ +/**/ + +#define EACH_TYPE1(z, type_index, nothing) \ + BOOST_PP_REPEAT( \ + BOOST_PP_ARRAY_SIZE(VALUES), \ + EACH_VALUE, \ + type_index \ + ) \ +/**/ + +int main(int argc, char *argv[]){ + BOOST_PP_REPEAT( + BOOST_PP_ARRAY_SIZE(TYPES), + EACH_TYPE1, + nothing + ) + return 0; +} diff --git a/test/test_conversion.cpp b/test/test_conversion.cpp index f74bff4..c197f09 100644 --- a/test/test_conversion.cpp +++ b/test/test_conversion.cpp @@ -4,177 +4,77 @@ // accompanying file LICENSE_1_0.txt or copy at // http://www.boost.org/LICENSE_1_0.txt) +// test construction conversions + #include + #include -#include #include #include #include -#include #include -#include - -#include "../include/safe_cast.hpp" +#include "../include/safe_compare.hpp" #include "../include/safe_integer.hpp" -#define TYPES (8, ( \ - boost::int8_t, \ - boost::uint8_t, \ - boost::int16_t, \ - boost::uint16_t, \ - boost::int32_t, \ - boost::uint32_t, \ - boost::int64_t, \ - boost::uint64_t \ -)) +#include "test_types.hpp" +#include "test_values.hpp" -#define VALUES (20, ( \ - 0x10, \ - 0x20, \ - 0x7f, \ - 0x80, \ - 0xff, \ - 0x1000, \ - 0x2000, \ - 0x7fff, \ - 0x8000, \ - 0xffff, \ - 0x10000000, \ - 0x20000000, \ - 0x7fffffff, \ - 0x80000000, \ - 0xffffffff, \ - 0x100000000000, \ - 0x200000000000, \ - 0x7fffffffffff, \ - 0x80000000ffff, \ - 0xffffffffffff \ -)) - -// test conversion -template -bool test_conversion(V v, const char *a1, const char *a2, const char *a3){ - bool success; - boost::numeric::safe t2; - /* test conversion constructor */ +// test conversion to T2 from different literal types +template +bool test_conversion(V v, const char *t_name, const char *v_name){ + /* test conversion constructor to T1 */ + boost::numeric::safe t1; try{ - t2 = v; - success = true; - } - catch(std::range_error e){ - success = false; - } - if(success){ - if(t2 > std::numeric_limits::max() - || t2 < std::numeric_limits::min()){ + t1 = v; + if(! boost::numeric::safe_compare::equal(t1, v)){ std::cout << "failed to detect error in construction " - << a1 << ' ' << a2 + << t_name << "<-" << v_name << std::endl; return false; } } - else{ - if(v <= std::numeric_limits::max() - && v >= std::numeric_limits::min()){ - std::cout - << "erroneasly detected error in construction " - << a1 << ' ' << a2 - << std::endl; - return false; - } - return true; // correctly detected error - } - - T1 t1; - try{ - //t1 = t2; - t1 = boost::numeric::safe_cast(t2); - success = true; - } catch(std::range_error e){ - success = false; - } - if(success){ - if(t1 > std::numeric_limits::max() - || t1 < std::numeric_limits::min()){ - std::cout - << "failed to detect error in conversion " - << a1 << ' ' << a2 << ' ' << a3 - << std::endl; - return false; - } - } - else{ - if(t2 <= std::numeric_limits::max() - && t2 >= std::numeric_limits::min()){ + if(boost::numeric::safe_compare::equal(t1, v)){ std::cout - << "erroneasly detected error in conversion " - << a1 << ' ' << a2 << ' ' << a3 + << "failed to detect error in construction " + << t_name << "<-" << v_name << std::endl; return false; } - return true; // correctly detected error } return true; // passed test } -template -struct test_value { - typedef typename boost::mpl::if_< - boost::is_signed, - boost::intmax_t, - boost::uintmax_t - >::type type; -}; - -#define TEST_CONVERSION(T1, T2, v) \ - test_conversion( \ - static_cast::type>(v), \ - BOOST_PP_STRINGIZE(T1), \ - BOOST_PP_STRINGIZE(T2), \ - BOOST_PP_STRINGIZE(v) \ - ); - -#define TEST(z, k, ij) \ - TEST_CONVERSION( \ - BOOST_PP_ARRAY_ELEM( \ - BOOST_PP_TUPLE_ELEM(2, 0, ij),\ - TYPES \ - ), \ - BOOST_PP_ARRAY_ELEM( \ - BOOST_PP_TUPLE_ELEM(2, 1, ij),\ - TYPES \ - ), \ - BOOST_PP_ARRAY_ELEM(k, VALUES) \ - ) - -#define EACH_VALUE(z, j, i) \ - BOOST_PP_REPEAT( \ - BOOST_PP_ARRAY_SIZE(VALUES), \ - TEST, \ - (i, j) \ - ) \ +#define TEST_CONVERSION(T1, v) \ + test_conversion( \ + v, \ + BOOST_PP_STRINGIZE(T1), \ + BOOST_PP_STRINGIZE(v) \ + ); /**/ -#define EACH_TYPE1(z, i, x) \ - BOOST_PP_REPEAT( \ - BOOST_PP_ARRAY_SIZE(TYPES), \ - EACH_VALUE, \ - i \ - ) \ +#define EACH_VALUE(z, value_index, type_index) \ + TEST_CONVERSION( \ + BOOST_PP_ARRAY_ELEM(type_index, TYPES), \ + BOOST_PP_ARRAY_ELEM(value_index, VALUES) \ + ) \ +/**/ + +#define EACH_TYPE1(z, type_index, nothing) \ + BOOST_PP_REPEAT( \ + BOOST_PP_ARRAY_SIZE(VALUES), \ + EACH_VALUE, \ + type_index \ + ) \ /**/ int main(int argc, char *argv[]){ BOOST_PP_REPEAT( BOOST_PP_ARRAY_SIZE(TYPES), EACH_TYPE1, - x + nothing ) - /* - TEST(0, 0, (0, 1)); - TEST_CONVERSION(boost::uint8_t, boost::uint8_t, 0x80) - */ return 0; } diff --git a/test/test_subtract.cpp b/test/test_subtract.cpp index bf52a50..70bf528 100644 --- a/test/test_subtract.cpp +++ b/test/test_subtract.cpp @@ -4,14 +4,14 @@ // accompanying file LICENSE_1_0.txt or copy at // http://www.boost.org/LICENSE_1_0.txt) -bool test_subtract1(); -bool test_subtract2(); -bool test_subtract3(); +#include "test_subtract.hpp" + +#define TESTX(a, b) \ + rval &= test_subtract(a, b, BOOST_PP_STRINGIZE(a), BOOST_PP_STRINGIZE(b)); +/**/ int main(int argc, char * argv[]){ - bool result = true; - result = test_subtract1(); - result &= test_subtract2(); - result &= test_subtract3(); - return ! result ; + bool rval = true; + TEST_ALL + return ! rval ; } diff --git a/test/test_subtract.hpp b/test/test_subtract.hpp index be0be05..59eb077 100644 --- a/test/test_subtract.hpp +++ b/test/test_subtract.hpp @@ -14,144 +14,16 @@ #include #include -#include -#include +#include +#include #include "../include/safe_integer.hpp" #include "../include/safe_cast.hpp" #include "../include/safe_compare.hpp" +#include "../include/numeric.hpp" #include "test.hpp" -template -bool test_subtract( - V v1, - V v2, - const char *at1, - const char *at2, - const char *av1, - const char *av2 -){ - bool success; - - T1 t1; - try{ - t1 = boost::numeric::safe_cast(v1); - success = true; - } - catch(std::range_error){ - success = false; - } - if(success){ - if(boost::numeric::safe_compare::greater_than( - v1, - std::numeric_limits::max() - ) - || boost::numeric::safe_compare::less_than( - v1, - std::numeric_limits::min() - ) - ){ - std::cout - << "constructed invalid value " - << at1 << ' ' << av1 - << std::endl; - return false; - } - } - else{ - if(! boost::numeric::safe_compare::greater_than( - v1, - std::numeric_limits::max() - ) - && ! boost::numeric::safe_compare::less_than( - v1, - std::numeric_limits::min() - ) - ){ - std::cout - << "failed to construct valid value " - << at1 << ' ' << av1 - << std::endl; - return false; - } - return true; - } - - T2 t2; - try{ - t2 = boost::numeric::safe_cast(v2); - success = true; - } - catch(std::range_error){ - success = false; - } - if(success){ - if(boost::numeric::safe_compare::greater_than( - v2, - std::numeric_limits::max() - ) - || boost::numeric::safe_compare::less_than( - v2, - std::numeric_limits::min() - )){ - std::cout - << "constructed invalid value " - << at2 << ' ' << av2 - << std::endl; - return false; - } - } - else{ - if(!boost::numeric::safe_compare::greater_than( - v2, - std::numeric_limits::max() - ) - && !boost::numeric::safe_compare::less_than( - v2, - std::numeric_limits::min() - )){ - std::cout - << "failed to construct valid value " - << at2 << ' ' << av2 - << std::endl; - return false; - } - return true; - } - - V result; - try{ - result = t1 - t2; - success = true; - } - catch(std::range_error){ - success = false; - } - if(success){ - if(result != v1 - v2){ - std::cout - << "failed to detect error in subtraction " - << at1 << ' ' << at2 << ' ' << av1 << ' ' << av2 - << std::endl; - return false; - } - } - else{ - if(boost::numeric::safe_compare::greater_than_equal( - boost::numeric::bits::value, - std::max(count_bits(v1),count_bits(v2)) - )){ - std::cout - << "erroneously detected error in subtraction " - << at1 << ' ' << at2 << ' ' << av1 << ' ' << av2 - << std::endl; - return false; - } - } - return true; // correct result -} - template struct subtract_result { typedef typename boost::mpl::if_< @@ -164,15 +36,43 @@ struct subtract_result { >::type type; }; -#define TEST_IMPL(a, b, c, d) \ - rval &= test_subtract( \ - (static_cast::type>(a)), \ - (static_cast::type>(b)), \ - BOOST_PP_STRINGIZE(d), \ - BOOST_PP_STRINGIZE(c), \ - BOOST_PP_STRINGIZE(b), \ - BOOST_PP_STRINGIZE(a) \ - ); \ -/**/ +template +bool test_subtract( + T1 v1, + T2 v2, + const char *av1, + const char *av2 +){ + typename subtract_result::type result; + try{ + boost::numeric::safe t1 = v1; + + result = t1 - v2; + if(boost::numeric::safe_compare::less_than( + boost::numeric::bits::value, + std::max(count_bits(v1),count_bits(v2)) + )){ + std::cout + << "failed to detect error in subtraction " + << av1 << " - " << av2 + << std::endl; + return false; + } + } + catch(std::range_error){ + if(boost::numeric::safe_compare::greater_than_equal( + boost::numeric::bits::value, + std::max(count_bits(v1),count_bits(v2)) + )){ + std::cout + << "erroneously detected error in subtraction " + << av1 << " - " << av2 + << std::endl; + return false; + } + } + return true; // correct result +} + #endif // TEST_SUBTRACT_HPP diff --git a/test/test_types.hpp b/test/test_types.hpp new file mode 100644 index 0000000..23cd730 --- /dev/null +++ b/test/test_types.hpp @@ -0,0 +1,22 @@ +// +// test_types.hpp +// +// Created by Robert Ramey on 3/27/14. +// +// + +#ifndef test_types_hpp +#define test_types_hpp + +#define TYPES (8, ( \ + boost::int8_t, \ + boost::uint8_t, \ + boost::int16_t, \ + boost::uint16_t, \ + boost::int32_t, \ + boost::uint32_t, \ + boost::int64_t, \ + boost::uint64_t \ +)) + +#endif diff --git a/test/test_values.hpp b/test/test_values.hpp new file mode 100644 index 0000000..95807bd --- /dev/null +++ b/test/test_values.hpp @@ -0,0 +1,48 @@ +// +// test_values.hpp +// +// Created by Robert Ramey on 3/27/14. +// +// + +#ifndef test_values_hpp +#define test_values_hpp + +#include + +#define VALUES (32, ( \ + (std::int8_t)0x01, \ + (std::int8_t)0x7f, \ + (std::int8_t)0x80, \ + (std::int8_t)0xff, \ + (std::int16_t)0x0001, \ + (std::int16_t)0x7fff, \ + (std::int16_t)0x8000, \ + (std::int16_t)0xffff, \ + (std::int32_t)0x00000001, \ + (std::int32_t)0x7fffffff, \ + (std::int32_t)0x80000000, \ + (std::int32_t)0xffffffff, \ + (std::int64_t)0x0000000000000001, \ + (std::int64_t)0x7fffffffffffffff, \ + (std::int64_t)0x8000000000000000, \ + (std::int64_t)0xffffffffffffffff, \ + (std::uint8_t)0x01, \ + (std::uint8_t)0x7f, \ + (std::uint8_t)0x80, \ + (std::uint8_t)0xff, \ + (std::uint16_t)0x0001, \ + (std::uint16_t)0x7fff, \ + (std::uint16_t)0x8000, \ + (std::uint16_t)0xffff, \ + (std::uint32_t)0x00000001, \ + (std::uint32_t)0x7fffffff, \ + (std::uint32_t)0x80000000, \ + (std::uint32_t)0xffffffff, \ + (std::uint64_t)0x0000000000000001,\ + (std::uint64_t)0x7fffffffffffffff,\ + (std::uint64_t)0x8000000000000000,\ + (std::uint64_t)0xffffffffffffffff \ +)) + +#endif