// (C) Copyright 2003, 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 #include #include #include #include #include #include "boost/numeric/conversion/converter.hpp" #ifdef BOOST_BORLANDC #pragma hdrstop #endif #include "test_helpers.cpp" #include "test_helpers2.cpp" #include "test_helpers3.cpp" using namespace std ; using namespace boost ; using namespace numeric ; using namespace MyUDT ; //------------------------------------------------------------------------- // These are the typical steps that are required to install support for // conversions from/to UDT which need special treatment. //------------------------------------------------------------------------- // // (1) Instantiate specific convesions traits. // This step is only for convenience. // These traits instances are required in order to define the specializations // that follow (and which *are required* to make the library work with MyInt and MyFloat) // namespace MyUDT { typedef conversion_traits MyFloat_to_double_Traits; typedef conversion_traits MyFloat_to_int_Traits; typedef conversion_traits MyFloat_to_MyInt_Traits; typedef conversion_traits MyInt_to_int_Traits; typedef conversion_traits MyInt_to_MyFloat_Traits; typedef conversion_traits double_to_MyInt_Traits; } // namespace MyUDT // // (2) Define suitable raw converters. // // Our sample UDTs don't support implicit conversions. // Therefore, the default raw_converter<> doesn't work, // and we need to define our own. // // There are two ways of doing this: // // (a) One is to simply specialize boost::numeric::raw_converter<> directly. // This way, the default converter will work out of the box, which means, for instance, // that numeric_cast<> can be used with these UDTs. // // (b) Define a user class with the appropriate interface and supply it explicitely // as a policy to a converter instance. // // This test uses chice (a). // namespace boost { namespace numeric { template<> struct raw_converter { static double low_level_convert ( MyUDT::MyFloat const& s ) { return s.to_builtin() ; } } ; template<> struct raw_converter { static int low_level_convert ( MyUDT::MyFloat const& s ) { return static_cast( s.to_builtin() ) ; } } ; template<> struct raw_converter { static MyUDT::MyInt low_level_convert ( MyUDT::MyFloat const& s ) { return MyUDT::MyInt( static_cast(s.to_builtin()) ) ; } } ; template<> struct raw_converter { static int low_level_convert ( MyUDT::MyInt const& s ) { return s.to_builtin() ; } } ; template<> struct raw_converter { static MyUDT::MyFloat low_level_convert ( MyUDT::MyInt const& s ) { return MyUDT::MyFloat( static_cast(s.to_builtin()) ) ; } } ; template<> struct raw_converter { static MyUDT::MyInt low_level_convert ( double s ) { return MyUDT::MyInt( static_cast(s) ) ; } } ; } // namespace numeric } // namespace boost // // (3) Define suitable range checkers // // By default, if a UDT is involved in a conversion, internal range checking is disabled. // This is so because a UDT type can have any sort of range, even unbounded, thus // the library doesn't attempt to automatically figure out the appropriate range checking logic. // (as it does when builtin types are involved) // However, this situation is a bit unsufficient in practice, specially from doing narrowing (subranged) // conversions from UDTs. // The library provides a rudimentary hook to help this out: The user can plug in his own // range checker to the converter instance. // // This test shows how to define and use a custom range checker. // namespace MyUDT { // // The following are metaprogramming tools to allow us the implement the // MyCustomRangeChecker generically, for either builtin or UDT types. // // get_builtin_type::type extracts the built-in type of our UDT's // template struct get_builtin_type { typedef N type ; } ; template<> struct get_builtin_type { typedef int type ; } ; template<> struct get_builtin_type { typedef double type ; } ; // U extract_builtin ( T s ) returns 's' converted to the corresponding built-in type U. // template struct extract_builtin { static N apply ( N n ) { return n ; } } ; template<> struct extract_builtin { static int apply ( MyInt const& n ) { return n.to_builtin() ; } } ; template<> struct extract_builtin { static double apply ( MyFloat const& n ) { return n.to_builtin() ; } } ; template struct MyCustomRangeChecker { typedef typename Traits::argument_type argument_type ; // This custom range checker uses the fact that our 'fake' UDT are merely wrappers // around builtin types; so it just forward the logic to the correspoding range // checkers for the wrapped builtin types. // typedef typename Traits::source_type S ; typedef typename Traits::target_type T ; // NOTE: S and/or T can be either UDT or builtin types. typedef typename get_builtin_type::type builtinS ; typedef typename get_builtin_type::type builtinT ; // NOTE: The internal range checker used by default is *built* when you instantiate // a converter<> with a given Traits according to the properties of the involved types. // Currently, there is no way to instantiate this range checker as a separate class. // However, you can see it as part of the interface of the converter // (since the converter inherits from it) // Therefore, here we instantiate a converter corresponding to the builtin types to access // their associated builtin range checker. // typedef boost::numeric::converter InternalConverter ; static range_check_result out_of_range ( argument_type s ) { return InternalConverter::out_of_range( extract_builtin::apply(s) ); } static void validate_range ( argument_type s ) { return InternalConverter::validate_range( extract_builtin::apply(s) ); } } ; } // namespace MyUDT // // Test here // void test_udt_conversions_with_defaults() { cout << "Testing UDT conversion with default policies\n" ; // MyInt <--> int int mibv = rand(); MyInt miv(mibv); TEST_SUCCEEDING_CONVERSION_DEF(MyInt,int,miv,mibv); TEST_SUCCEEDING_CONVERSION_DEF(int,MyInt,mibv,miv); // MyFloat <--> double double mfbv = static_cast(rand()) / 3.0 ; MyFloat mfv (mfbv); TEST_SUCCEEDING_CONVERSION_DEF(MyFloat,double,mfv,mfbv); TEST_SUCCEEDING_CONVERSION_DEF(double,MyFloat,mfbv,mfv); // MyInt <--> MyFloat MyInt miv2 ( static_cast(mfbv) ); MyFloat miv2F ( static_cast(mfbv) ); MyFloat mfv2 ( static_cast(mibv) ); MyInt mfv2I ( static_cast(mibv) ); TEST_SUCCEEDING_CONVERSION_DEF(MyFloat,MyInt,miv2F,miv2); TEST_SUCCEEDING_CONVERSION_DEF(MyInt,MyFloat,mfv2I,mfv2); } template struct GenerateCustomConverter { typedef conversion_traits Traits; typedef def_overflow_handler OverflowHandler ; typedef Trunc Float2IntRounder ; typedef raw_converter RawConverter ; typedef MyCustomRangeChecker RangeChecker ; typedef converter type ; } ; void test_udt_conversions_with_custom_range_checking() { cout << "Testing UDT conversions with custom range checker\n" ; int mibv = rand(); MyFloat mfv ( static_cast(mibv) ); typedef GenerateCustomConverter::type int_to_MyFloat_Conv ; TEST_SUCCEEDING_CONVERSION( int_to_MyFloat_Conv, MyFloat, int, mfv, mibv ); int mibv2 = rand(); MyInt miv (mibv2); MyFloat mfv2 ( static_cast(mibv2) ); typedef GenerateCustomConverter::type MyInt_to_MyFloat_Conv ; TEST_SUCCEEDING_CONVERSION( MyInt_to_MyFloat_Conv, MyFloat, MyInt, mfv2, miv ); double mfbv = bounds::highest(); typedef GenerateCustomConverter::type double_to_MyInt_Conv ; TEST_POS_OVERFLOW_CONVERSION( double_to_MyInt_Conv, MyInt, double, mfbv ); MyFloat mfv3 ( bounds::lowest() ) ; typedef GenerateCustomConverter::type MyFloat_to_int_Conv ; TEST_NEG_OVERFLOW_CONVERSION( MyFloat_to_int_Conv, int, MyFloat, mfv3 ); } int test_main( int, char* [] ) { cout << setprecision( numeric_limits::digits10 ) ; test_udt_conversions_with_defaults(); test_udt_conversions_with_custom_range_checking(); return 0; }