| // (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; |
| } |
| |
| |
| |
| |
| |
| |