// //! Copyright (c) 2011 //! Brandon Kohn // // 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 <boost/operators.hpp> #include <boost/numeric/conversion/cast.hpp> #include <boost/mpl/for_each.hpp> #include <boost/mpl/vector.hpp> #include <boost/cstdint.hpp> #include <boost/test/minimal.hpp> //! Define a simple custom number struct Double { Double() : v(0) {} template <typename T> explicit Double( T v ) : v(static_cast<double>(v)) {} template <typename T> Double& operator= ( T t ) { v = static_cast<double>(t); return *this; } bool operator < ( const Double& rhs ) const { return v < rhs.v; } template <typename T> bool operator < ( T rhs ) const { return v < static_cast<double>(rhs); } template <typename LHS> friend bool operator < ( const LHS& lhs, const Double& rhs ) { return lhs < rhs.v; } bool operator > ( const Double& rhs ) const { return v > rhs.v; } template <typename LHS> friend bool operator > ( const LHS& lhs, const Double& rhs ) { return lhs > rhs.v; } template <typename T> bool operator > ( T rhs ) const { return v > static_cast<double>(rhs); } bool operator == ( const Double& rhs ) const { return v == rhs.v; } template <typename T> bool operator == ( T rhs ) const { return v == static_cast<double>(rhs); } template <typename LHS> friend bool operator == ( const LHS& lhs, const Double& rhs ) { return lhs == rhs.v; } bool operator !() const { return v == 0; } Double operator -() const { return Double(-v); } Double& operator +=( const Double& t ) { v += t.v; return *this; } template <typename T> Double& operator +=( T t ) { v += static_cast<double>(t); return *this; } Double& operator -=( const Double& t ) { v -= t.v; return *this; } template <typename T> Double& operator -=( T t ) { v -= static_cast<double>(t); return *this; } Double& operator *= ( const Double& factor ) { v *= factor.v; return *this; } template <typename T> Double& operator *=( T t ) { v *= static_cast<double>(t); return *this; } Double& operator /= (const Double& divisor) { v /= divisor.v; return *this; } template <typename T> Double& operator /=( T t ) { v /= static_cast<double>(t); return (*this); } double v; }; //! Define numeric_limits for the custom type. namespace std { template<> class numeric_limits< Double > : public numeric_limits<double> { public: //! Limit our Double to a range of +/- 100.0 static Double (min)() { return Double(1.e-2); } static Double (max)() { return Double(1.e2); } static Double epsilon() { return Double( std::numeric_limits<double>::epsilon() ); } }; } //! Define range checking and overflow policies. namespace custom { //! Define a custom range checker template<typename Traits, typename OverFlowHandler> struct range_checker { typedef typename Traits::argument_type argument_type ; typedef typename Traits::source_type S; typedef typename Traits::target_type T; //! Check range of integral types. static boost::numeric::range_check_result out_of_range( argument_type s ) { using namespace boost::numeric; if( s > bounds<T>::highest() ) return cPosOverflow; else if( s < bounds<T>::lowest() ) return cNegOverflow; else return cInRange; } static void validate_range ( argument_type s ) { BOOST_STATIC_ASSERT( std::numeric_limits<T>::is_bounded ); OverFlowHandler()( out_of_range(s) ); } }; //! Overflow handler struct positive_overflow{}; struct negative_overflow{}; struct overflow_handler { void operator() ( boost::numeric::range_check_result r ) { using namespace boost::numeric; if( r == cNegOverflow ) throw negative_overflow() ; else if( r == cPosOverflow ) throw positive_overflow() ; } }; //! Define a rounding policy and specialize on the custom type. template<class S> struct Ceil : boost::numeric::Ceil<S>{}; template<> struct Ceil<Double> { typedef Double source_type; typedef Double const& argument_type; static source_type nearbyint ( argument_type s ) { #if !defined(BOOST_NO_STDC_NAMESPACE) using std::ceil ; #endif return Double( ceil(s.v) ); } typedef boost::mpl::integral_c< std::float_round_style, std::round_toward_infinity> round_style; }; //! Define a rounding policy and specialize on the custom type. template<class S> struct Trunc: boost::numeric::Trunc<S>{}; template<> struct Trunc<Double> { typedef Double source_type; typedef Double const& argument_type; static source_type nearbyint ( argument_type s ) { #if !defined(BOOST_NO_STDC_NAMESPACE) using std::floor; #endif return Double( floor(s.v) ); } typedef boost::mpl::integral_c< std::float_round_style, std::round_toward_zero> round_style; }; }//namespace custom; namespace boost { namespace numeric { //! Define the numeric_cast_traits specializations on the custom type. template <typename S> struct numeric_cast_traits<Double, S> { typedef custom::overflow_handler overflow_policy; typedef custom::range_checker < boost::numeric::conversion_traits<Double, S> , overflow_policy > range_checking_policy; typedef boost::numeric::Trunc<S> rounding_policy; }; template <typename T> struct numeric_cast_traits<T, Double> { typedef custom::overflow_handler overflow_policy; typedef custom::range_checker < boost::numeric::conversion_traits<T, Double> , overflow_policy > range_checking_policy; typedef custom::Trunc<Double> rounding_policy; }; //! Define the conversion from the custom type to built-in types and vice-versa. template<typename T> struct raw_converter< conversion_traits< T, Double > > { static T low_level_convert ( const Double& n ) { return static_cast<T>( n.v ); } }; template<typename S> struct raw_converter< conversion_traits< Double, S > > { static Double low_level_convert ( const S& n ) { return Double(n); } }; }}//namespace boost::numeric; #define BOOST_TEST_CATCH_CUSTOM_POSITIVE_OVERFLOW( CastCode ) \ try { CastCode; BOOST_CHECK( false ); } \ catch( custom::positive_overflow& ){} \ catch(...){ BOOST_CHECK( false ); } \ /***/ #define BOOST_TEST_CATCH_CUSTOM_NEGATIVE_OVERFLOW( CastCode ) \ try { CastCode; BOOST_CHECK( false ); } \ catch( custom::negative_overflow& ){} \ catch(...){ BOOST_CHECK( false ); } \ /***/ struct test_cast_traits { template <typename T> void operator()(T) const { Double d = boost::numeric_cast<Double>( static_cast<T>(50) ); BOOST_CHECK( d.v == 50. ); T v = boost::numeric_cast<T>( d ); BOOST_CHECK( v == 50 ); } }; void test_numeric_cast_traits() { typedef boost::mpl::vector < boost::int8_t , boost::uint8_t , boost::int16_t , boost::uint16_t , boost::int32_t , boost::uint32_t #if !defined( BOOST_NO_INT64_T ) , boost::int64_t , boost::uint64_t #endif , float , double , long double > types; boost::mpl::for_each<types>( test_cast_traits() ); //! Check overflow handler. Double d( 56.0 ); BOOST_TEST_CATCH_CUSTOM_POSITIVE_OVERFLOW( d = boost::numeric_cast<Double>( 101 ) ); BOOST_CHECK( d.v == 56. ); BOOST_TEST_CATCH_CUSTOM_NEGATIVE_OVERFLOW( d = boost::numeric_cast<Double>( -101 ) ); BOOST_CHECK( d.v == 56.); //! Check custom round policy. d = 5.9; int five = boost::numeric_cast<int>( d ); BOOST_CHECK( five == 5 ); } int test_main( int argc, char * argv[] ) { test_numeric_cast_traits(); return 0; } #undef BOOST_TEST_CATCH_CUSTOM_POSITIVE_OVERFLOW #undef BOOST_TEST_CATCH_CUSTOM_NEGATIVE_OVERFLOW