// //! 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 #include #include #include #include #include //! Define a simple custom number struct Double { Double() : v(0) {} template explicit Double( T v ) : v(static_cast(v)) {} template Double& operator= ( T t ) { v = static_cast(t); return *this; } bool operator < ( const Double& rhs ) const { return v < rhs.v; } template bool operator < ( T rhs ) const { return v < static_cast(rhs); } template friend bool operator < ( const LHS& lhs, const Double& rhs ) { return lhs < rhs.v; } bool operator > ( const Double& rhs ) const { return v > rhs.v; } template friend bool operator > ( const LHS& lhs, const Double& rhs ) { return lhs > rhs.v; } template bool operator > ( T rhs ) const { return v > static_cast(rhs); } bool operator == ( const Double& rhs ) const { return v == rhs.v; } template bool operator == ( T rhs ) const { return v == static_cast(rhs); } template 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 Double& operator +=( T t ) { v += static_cast(t); return *this; } Double& operator -=( const Double& t ) { v -= t.v; return *this; } template Double& operator -=( T t ) { v -= static_cast(t); return *this; } Double& operator *= ( const Double& factor ) { v *= factor.v; return *this; } template Double& operator *=( T t ) { v *= static_cast(t); return *this; } Double& operator /= (const Double& divisor) { v /= divisor.v; return *this; } template Double& operator /=( T t ) { v /= static_cast(t); return (*this); } double v; }; //! Define numeric_limits for the custom type. namespace std { template<> class numeric_limits< Double > : public numeric_limits { 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::epsilon() ); } }; } //! Define range checking and overflow policies. namespace custom { //! Define a custom range checker template 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::highest() ) return cPosOverflow; else if( s < bounds::lowest() ) return cNegOverflow; else return cInRange; } static void validate_range ( argument_type s ) { BOOST_STATIC_ASSERT( std::numeric_limits::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 struct Ceil : boost::numeric::Ceil{}; template<> struct Ceil { 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 struct Trunc: boost::numeric::Trunc{}; template<> struct Trunc { 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 struct numeric_cast_traits { typedef custom::overflow_handler overflow_policy; typedef custom::range_checker < boost::numeric::conversion_traits , overflow_policy > range_checking_policy; typedef boost::numeric::Trunc rounding_policy; }; template struct numeric_cast_traits { typedef custom::overflow_handler overflow_policy; typedef custom::range_checker < boost::numeric::conversion_traits , overflow_policy > range_checking_policy; typedef custom::Trunc rounding_policy; }; //! Define the conversion from the custom type to built-in types and vice-versa. template struct raw_converter< conversion_traits< T, Double > > { static T low_level_convert ( const Double& n ) { return static_cast( n.v ); } }; template 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 void operator()(T) const { Double d = boost::numeric_cast( static_cast(50) ); BOOST_CHECK( d.v == 50. ); T v = boost::numeric_cast( 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( test_cast_traits() ); //! Check overflow handler. Double d( 56.0 ); BOOST_TEST_CATCH_CUSTOM_POSITIVE_OVERFLOW( d = boost::numeric_cast( 101 ) ); BOOST_CHECK( d.v == 56. ); BOOST_TEST_CATCH_CUSTOM_NEGATIVE_OVERFLOW( d = boost::numeric_cast( -101 ) ); BOOST_CHECK( d.v == 56.); //! Check custom round policy. d = 5.9; int five = boost::numeric_cast( 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