// Copyright (C) 2005, Fernando Luis Cacciola Carballal. // // 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/config.hpp" #include "boost/utility.hpp" #include "boost/limits.hpp" #include "boost/utility.hpp" #include #include #include #include #include "boost/test/included/test_exec_monitor.hpp" #include "boost/numeric/conversion/cast.hpp" using namespace std ; using namespace boost; using namespace numeric; // // This example illustrates how to add support for user defined types (UDTs) // to the Boost Numeric Conversion Library. // It is assumed that you are familiar with the following documentation: // // // // The minimum requirement is that boost::is_arithmetic evaluates to false // (Otherwise the converter code will try to examine the UDT as a built-in type) // // // Let's start with the simpliest case of an UDT which supports standard conversions // struct Double { Double( double v ) : mV(v) {} operator double() const { return mV ; } double mV ; } ; double dv = (numeric_limits::max)() ; double fv = (numeric_limits::max)() ; Double Dv(dv); Double Fv(fv); void simplest_case() { // // conversion_traits<>::udt_builtin_mixture works out of the box as long as boost::is_arithmetic yields false // BOOST_CHECK( (conversion_traits::udt_builtin_mixture::value == udt_to_builtin) ) ; BOOST_CHECK( (conversion_traits::udt_builtin_mixture::value == builtin_to_udt) ) ; BOOST_CHECK( (conversion_traits::udt_builtin_mixture::value == udt_to_udt ) ) ; // BY DEFINITION, a conversion from UDT to Builtin is subranged. No attempt is made to actually compare ranges. BOOST_CHECK( (conversion_traits::subranged::value) == true ) ; BOOST_CHECK( (conversion_traits::subranged::value) == false ) ; // // Conversions to/from FLOATING types, if already supported by an UDT // are also supported out-of-the-box by converter<> in its default configuration. // BOOST_CHECK( numeric_cast(Dv) == static_cast(Dv) ) ; BOOST_CHECK( numeric_cast(dv) == static_cast(dv) ) ; BOOST_CHECK( numeric_cast (Dv) == static_cast (Dv) ) ; BOOST_CHECK( numeric_cast(fv) == static_cast(fv) ) ; // // Range checking is disabled by default if an UDT is either the source or target of the conversion. // BOOST_CHECK( (converter::out_of_range(dv) == cPosOverflow) ); BOOST_CHECK( (converter::out_of_range(Dv) == cInRange) ); } // // The conversion_traits<> class and therefore the converter<> class looks at // numeric_limits::is_integer/is_signed to generate the proper float_in and sign mixtures. // In most implementations, is_integer/is_signed are both false for UDTs if there is no explicit specialization for it. // Therefore, the converter<> will see any UDT for which numeric_limits<> is not specialized as Float AND unsigned. // Signess is used in the converter<> for range checking, but range checking is disabled by default for UDTs, so, // normally, signess is mostly irrelevant as far as the library is concerned, except for the numeric_traits<>::sign_mixture // entry. // is_integer, however, is relevant in that if the conversion is from a float type to an integer type, the conversion is // "rounding" and the rounder policies will participate. // ALL implemented rounder policies require proper definitions for floor(udt) and ceil(udt). // These names will be searched for using ADL, so, if you need to convert TO integral types from a UDT, // you need to supply those functions along with the UDT in right namespace (that is, any namespace that allows // ADL to find them) // If your UDT doesn't supply floor/ceil, conversions to integer types // won't compile unless a custom Float2IntRounder is used. Double floor ( Double v ) { return Double(std::floor(v.mV)) ; } Double ceil ( Double v ) { return Double(std::ceil (v.mV)) ; } void rounding() { BOOST_CHECK( numeric_cast(Dv) == static_cast(Dv) ) ; } // // If your UDT can't or won't provide floor/ceil you can set-up and use your own // Float2IntRounder policy (though doing this is not always required as shown so far) // struct DoubleToInt { static Double nearbyint ( Double const& s ) { return Double(static_cast(s)); } typedef mpl::integral_c< std::float_round_style, std::round_toward_zero> round_style ; } ; void custom_rounding() { typedef converter ,void // By default UDT disable range checking so this won't be used ,DoubleToInt > DoubleToIntConverter ; BOOST_CHECK( DoubleToIntConverter::convert(Dv) == static_cast(Dv) ) ; } // // In the next Level of complexity, your UDTs might not support conversion operators // struct Float { Float( float v ) : mV(v) {} float mV ; } ; struct Int { Int( int v ) : mV(v) {} int mV ; } ; typedef conversion_traits Float2IntTraits ; typedef conversion_traits Int2FloatTraits ; namespace boost { namespace numeric { // // Though static_cast<> won't work with them you can still use numeric_cast<> by specializing // raw_converter as follows: // template<> struct raw_converter { typedef Float2IntTraits::result_type result_type ; typedef Float2IntTraits::argument_type argument_type ; static result_type low_level_convert ( argument_type s ) { return Int((int)s.mV); } } ; template<> struct raw_converter { typedef Int2FloatTraits::result_type result_type ; typedef Int2FloatTraits::argument_type argument_type ; static result_type low_level_convert ( argument_type s ) { return Float(s.mV); } } ; } } void custom_raw_converter() { Float f (12.34); Int i (12); Float fi(12); BOOST_CHECK(numeric_cast (f).mV == i .mV ) ; BOOST_CHECK(numeric_cast(i).mV == fi.mV ) ; } // // Alterntively, the custom raw_converter classes can be defined non-instrusively // (not as specializations) and passed along as policies // struct Float2IntRawConverter { static Int low_level_convert ( Float const& s ) { return Int((int)s.mV); } } ; struct Int2FloatRawConverter { static Float low_level_convert ( Int const& s ) { return Float(s.mV); } } ; void custom_raw_converter2() { Float f (12.34); Int i (12); Float fi(12); typedef converter ,Float2IntRawConverter > Float2IntConverter ; BOOST_CHECK(Float2IntConverter::convert(f).mV == i .mV ) ; } int test_main( int, char* [] ) { cout << setprecision( numeric_limits::digits10 ) ; simplest_case(); rounding(); custom_rounding(); custom_raw_converter(); custom_raw_converter2(); return 0; }