#include #include #include #include #include #include #include // std::domain_error #include #include #include #include #include #include #include #include #include #include namespace core { namespace mpl = boost::mpl; namespace detail { template struct value_holder { BOOST_STATIC_ASSERT_MSG(boost::is_arithmetic::value, "T must be a fundamental type"); typedef T fundamental_type; typedef std::numeric_limits limits; /*constexpr*/ value_holder() { } template /*constexpr*/ value_holder(T2 d) : m_data(d) { } fundamental_type m_data; }; } template struct operators : boost::less_than_comparable < T , boost::equality_comparable < T , boost::addable < T , boost::subtractable < T , boost::multipliable < T , boost::dividable < T > > > > > > { }; //////////////////////////////////////////////////////////////////////////// template struct intrinsic_type : detail::value_holder, operators< intrinsic_type > { typedef detail::value_holder base; typedef typename detail::value_holder::fundamental_type builtin_type; typedef intrinsic_type type; // default constructor /*constexpr*/ intrinsic_type() { } // copy constructor /*constexpr*/ intrinsic_type(intrinsic_type const& other) : base(other.m_data) { } // copy constructor /*constexpr*/ intrinsic_type(detail::value_holder const& other) : base(other.m_data) { } template /*constexpr*/ intrinsic_type(T2 d) : base(d) { } builtin_type to_builtin() const { return base::m_data; } builtin_type& to_builtin() { return base::m_data; } bool operator<(intrinsic_type const& rhs) const { return to_builtin() < rhs.to_builtin(); } intrinsic_type operator -() const { return intrinsic_type(-to_builtin()); } bool operator==(intrinsic_type const& rhs) const { return to_builtin() == rhs.to_builtin(); } intrinsic_type operator+=(intrinsic_type const& rhs) { to_builtin() += rhs.to_builtin(); return *this; } intrinsic_type operator-=(intrinsic_type const& rhs) { to_builtin() -= rhs.to_builtin(); return *this; } intrinsic_type operator*=(intrinsic_type const& rhs) { to_builtin() *= rhs.to_builtin(); return *this; } intrinsic_type operator/=(intrinsic_type const& rhs) { to_builtin() /= rhs.to_builtin(); return *this; } template std::basic_ostream& print_on(std::basic_ostream& os) const { os << to_builtin(); return os; } }; //////////////////////////////////////////////////////////////////////////// template struct ceil { typedef intrinsic_type value_type; static inline value_type apply(value_type const& value) { return std::ceil(value.to_builtin()); } }; template struct floor { typedef intrinsic_type value_type; static inline value_type apply(value_type const& value) { return std::floor(value.to_builtin()); } }; template struct trunc { typedef intrinsic_type value_type; static inline value_type apply(value_type const& value) { return value.to_builtin() < static_cast(0) ? std::ceil(value.to_builtin()) : std::floor(value.to_builtin()); } }; template struct math_round { typedef intrinsic_type value_type; static inline value_type apply(value_type const& value) { return value.to_builtin() < static_cast(0) ? std::ceil( value.to_builtin() - epsilon()) : std::floor(value.to_builtin() + epsilon()); } static inline typename value_type::fundamental_type epsilon() { return static_cast(0.5); } }; //////////////////////////////////////////////////////////////////////////// template static inline std::basic_ostream& operator<<(std::basic_ostream& os, intrinsic_type const& v) { return v.print_on(os); } } //////////////////////////////////////////////////////////////////////////////// // // The Numeric Conversion Library *requires* User Defined Numeric Types // to properly specialize std::numeric_limits<> namespace std { template struct numeric_limits > : public numeric_limits::fundamental_type> { typedef core::intrinsic_type this_type; static const/*constexpr*/ bool is_specialized = true; static /*constexpr*/ this_type (min)() throw() { return (this_type::base::limits::min)(); } static /*constexpr*/ this_type (max)() throw() { return (this_type::base::limits::max)(); } }; } //////////////////////////////////////////////////////////////////////////////// //! boost.NumericConversation range checking and overflow policies namespace core { enum range_check_result { cNonFinite = 0, cInRange = 1, cNegOverflow = 2, cPosOverflow = 3 } ; //! Define a custom range checker template struct range_checker; template struct range_checker < boost::numeric::conversion_traits< core::intrinsic_type, SourceT > , OverFlowHandler > { typedef boost::numeric::conversion_traits< core::intrinsic_type, SourceT > conv_traits; typedef typename conv_traits::argument_type argument_type; typedef typename conv_traits::source_type S; typedef typename conv_traits::target_type T; //! Check range of integral types. static range_check_result out_of_range(argument_type value) { if(!boost::math::isfinite(value)) // Neither infinity nor NaN. return cNonFinite; else if(T(value) > boost::numeric::bounds::highest()) return cPosOverflow; else if(T(value) < boost::numeric::bounds::lowest()) return cNegOverflow; else return cInRange; } static void validate_range(argument_type value) { BOOST_STATIC_ASSERT(std::numeric_limits::is_bounded); OverFlowHandler()(out_of_range(value)); } }; template struct range_checker < boost::numeric::conversion_traits< TargetT, core::intrinsic_type > , OverFlowHandler > { typedef boost::numeric::conversion_traits< TargetT, core::intrinsic_type > conv_traits; typedef typename conv_traits::argument_type argument_type; typedef typename conv_traits::source_type S; typedef typename conv_traits::target_type T; //! Check range of integral types. static range_check_result out_of_range(argument_type value) { if(!boost::math::isfinite(value.to_builtin())) // Neither infinity nor NaN. return cNonFinite; else if(value.to_builtin() > boost::numeric::bounds::highest()) return cPosOverflow; else if(value.to_builtin() < boost::numeric::bounds::lowest()) return cNegOverflow; else return cInRange; } static void validate_range(argument_type value) { BOOST_STATIC_ASSERT(std::numeric_limits::is_bounded); OverFlowHandler()(out_of_range(value)); } }; //! Overflow handler struct bad_numeric_cast : virtual boost::exception, virtual std::bad_cast { virtual const char * what() const throw() { return "bad numeric conversion: overflow"; } }; struct rounding_error : virtual boost::exception, std::domain_error { rounding_error() : std::domain_error("NaN or Infinity") {} virtual const char * what() const throw() { return "bad numeric conversion: can not be represented in the target integer type"; } }; struct negative_overflow : virtual bad_numeric_cast { virtual const char * what() const throw() { return "bad numeric conversion: negative overflow"; } }; struct positive_overflow : virtual bad_numeric_cast { virtual const char * what() const throw() { return "bad numeric conversion: positive overflow"; } }; struct overflow_handler { void operator()(range_check_result result) { if(result == cNonFinite) BOOST_THROW_EXCEPTION(rounding_error()); else if(result == cNegOverflow) BOOST_THROW_EXCEPTION(negative_overflow()); else if(result == cPosOverflow) BOOST_THROW_EXCEPTION(positive_overflow()); } }; //! Define a rounding policy and specialize on the custom type. template struct Ceil : boost::numeric::Ceil {}; template struct Ceil > { typedef mpl::integral_c< std::float_round_style, std::round_toward_infinity > round_style; typedef intrinsic_type value_type; static value_type nearbyint(value_type const& value) { return ceil::apply(value); } }; //! Define a rounding policy and specialize on the custom type. template struct Floor: boost::numeric::Trunc {}; template struct Floor > { typedef boost::mpl::integral_c< std::float_round_style, std::round_toward_neg_infinity > round_style; typedef intrinsic_type value_type; static value_type nearbyint(value_type const& value) { return floor::apply(value); } }; //! Define a rounding policy and specialize on the custom type. template struct Trunc: boost::numeric::Trunc {}; template struct Trunc > { typedef boost::mpl::integral_c< std::float_round_style, std::round_toward_zero > round_style; typedef intrinsic_type value_type; static value_type nearbyint(value_type const& value) { return trunc::apply(value); } }; //! Define a rounding policy and specialize on the custom type. template struct MathRound: boost::numeric::Trunc {}; template struct MathRound > { typedef boost::mpl::integral_c< std::float_round_style, std::round_to_nearest > round_style; typedef intrinsic_type value_type; static value_type nearbyint(value_type const& value) { return math_round::apply(value); } }; } namespace boost { namespace numeric { //! Define the numeric_cast_traits specializations on the custom type. template struct numeric_cast_traits, SourceT> { typedef SourceT source_type; typedef core::intrinsic_type target_type; typedef conversion_traits< target_type, source_type > conv_traits; typedef core::overflow_handler overflow_policy; typedef core::range_checker< conv_traits, overflow_policy > range_checking_policy; typedef boost::numeric::Trunc rounding_policy; }; template struct numeric_cast_traits > { typedef core::intrinsic_type source_type; typedef TargetT target_type; typedef conversion_traits< target_type, source_type > conv_traits; typedef core::overflow_handler overflow_policy; typedef core::range_checker< conv_traits, overflow_policy > range_checking_policy; typedef core::MathRound rounding_policy; }; //! Define the conversion from the custom type to built-in types and vice-versa. template struct raw_converter > > { typedef core::intrinsic_type source_type; typedef TargetT target_type; static target_type low_level_convert(source_type const& value) { return static_cast(value.to_builtin()); } }; template struct raw_converter, SourceT> > { typedef SourceT source_type; typedef core::intrinsic_type target_type; static target_type low_level_convert(source_type const& value) { return value; } }; }} //////////////////////////////////////////////////////////////////////////////// BOOST_AUTO_TEST_CASE(TestUDTNumericCast) { typedef core::intrinsic_type my_type; try { { double z = (std::numeric_limits::max)(); z *= std::numeric_limits::epsilon(); std::cout << z << std::endl; std::cout << boost::numeric_cast(z) << '\n'; } { typedef core::intrinsic_type my_type; my_type x(1.499); my_type y(1.500); std::cout << x << "->" << boost::numeric_cast(x) << '\n'; std::cout << y << "->" << boost::numeric_cast(y) << '\n'; } { typedef core::intrinsic_type my_type; const int x = 42; my_type y = boost::numeric_cast(x); BOOST_CHECK_CLOSE(y.to_builtin(), 42., 1e-5); } } catch(core::positive_overflow& e) { std::cerr << "CORE: " << boost::diagnostic_information(e) << std::endl; } catch(boost::numeric::positive_overflow& e) { std::cerr << "Boost.Numeric: " << e.what() << std::endl; } catch(boost::exception& e) { std::cerr << "Boost: " << boost::diagnostic_information(e) << std::endl; } catch(std::exception& e) { std::cerr << "STD: " << e.what() << std::endl; } }