123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309 |
- // (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<iostream>
- #include<iomanip>
- #include<string>
- #include<typeinfo>
- #include<vector>
- #include<algorithm>
- #include "boost/numeric/conversion/converter.hpp"
- #ifdef __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<double , MyFloat> MyFloat_to_double_Traits;
- typedef conversion_traits<int , MyFloat> MyFloat_to_int_Traits;
- typedef conversion_traits<MyInt , MyFloat> MyFloat_to_MyInt_Traits;
- typedef conversion_traits<int , MyInt > MyInt_to_int_Traits;
- typedef conversion_traits<MyFloat, MyInt > MyInt_to_MyFloat_Traits;
- typedef conversion_traits<MyInt , double > 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<MyUDT::MyFloat_to_double_Traits>
- {
- static double low_level_convert ( MyUDT::MyFloat const& s )
- { return s.to_builtin() ; }
- } ;
- template<>
- struct raw_converter<MyUDT::MyFloat_to_int_Traits>
- {
- static int low_level_convert ( MyUDT::MyFloat const& s )
- { return static_cast<int>( s.to_builtin() ) ; }
- } ;
- template<>
- struct raw_converter<MyUDT::MyFloat_to_MyInt_Traits>
- {
- static MyUDT::MyInt low_level_convert ( MyUDT::MyFloat const& s )
- { return MyUDT::MyInt( static_cast<int>(s.to_builtin()) ) ; }
- } ;
- template<>
- struct raw_converter<MyUDT::MyInt_to_int_Traits>
- {
- static int low_level_convert ( MyUDT::MyInt const& s ) { return s.to_builtin() ; }
- } ;
- template<>
- struct raw_converter<MyUDT::MyInt_to_MyFloat_Traits>
- {
- static MyUDT::MyFloat low_level_convert ( MyUDT::MyInt const& s )
- {
- return MyUDT::MyFloat( static_cast<double>(s.to_builtin()) ) ;
- }
- } ;
- template<>
- struct raw_converter<MyUDT::double_to_MyInt_Traits>
- {
- static MyUDT::MyInt low_level_convert ( double s )
- { return MyUDT::MyInt( static_cast<int>(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<N>::type extracts the built-in type of our UDT's
- //
- template<class N> struct get_builtin_type { typedef N type ; } ;
- template<> struct get_builtin_type<MyInt> { typedef int type ; } ;
- template<> struct get_builtin_type<MyFloat> { typedef double type ; } ;
- // U extract_builtin ( T s ) returns 's' converted to the corresponding built-in type U.
- //
- template<class N>
- struct extract_builtin
- {
- static N apply ( N n ) { return n ; }
- } ;
- template<>
- struct extract_builtin<MyInt>
- {
- static int apply ( MyInt const& n ) { return n.to_builtin() ; }
- } ;
- template<>
- struct extract_builtin<MyFloat>
- {
- static double apply ( MyFloat const& n ) { return n.to_builtin() ; }
- } ;
- template<class Traits>
- 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<S>::type builtinS ;
- typedef typename get_builtin_type<T>::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<builtinT,builtinS> InternalConverter ;
- static range_check_result out_of_range ( argument_type s )
- {
- return InternalConverter::out_of_range( extract_builtin<S>::apply(s) );
- }
- static void validate_range ( argument_type s )
- {
- return InternalConverter::validate_range( extract_builtin<S>::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<double>(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<int>(mfbv) );
- MyFloat miv2F ( static_cast<int>(mfbv) );
- MyFloat mfv2 ( static_cast<double>(mibv) );
- MyInt mfv2I ( static_cast<double>(mibv) );
- TEST_SUCCEEDING_CONVERSION_DEF(MyFloat,MyInt,miv2F,miv2);
- TEST_SUCCEEDING_CONVERSION_DEF(MyInt,MyFloat,mfv2I,mfv2);
- }
- template<class T, class S>
- struct GenerateCustomConverter
- {
- typedef conversion_traits<T,S> Traits;
- typedef def_overflow_handler OverflowHandler ;
- typedef Trunc<S> Float2IntRounder ;
- typedef raw_converter<Traits> RawConverter ;
- typedef MyCustomRangeChecker<Traits> RangeChecker ;
- typedef converter<T,S,Traits,OverflowHandler,Float2IntRounder,RawConverter,RangeChecker> 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<double>(mibv) );
- typedef GenerateCustomConverter<MyFloat,int>::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<double>(mibv2) );
- typedef GenerateCustomConverter<MyFloat,MyInt>::type MyInt_to_MyFloat_Conv ;
- TEST_SUCCEEDING_CONVERSION( MyInt_to_MyFloat_Conv, MyFloat, MyInt, mfv2, miv );
- double mfbv = bounds<double>::highest();
- typedef GenerateCustomConverter<MyInt,double>::type double_to_MyInt_Conv ;
- TEST_POS_OVERFLOW_CONVERSION( double_to_MyInt_Conv, MyInt, double, mfbv );
- MyFloat mfv3 ( bounds<double>::lowest() ) ;
- typedef GenerateCustomConverter<int,MyFloat>::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<long double>::digits10 ) ;
- test_udt_conversions_with_defaults();
- test_udt_conversions_with_custom_range_checking();
- return 0;
- }
|