1 // (C) Copyright 2003, Fernando Luis Cacciola Carballal.
3 // Distributed under the Boost Software License, Version 1.0. (See
4 // accompanying file LICENSE_1_0.txt or copy at
5 // http://www.boost.org/LICENSE_1_0.txt)
15 #include "boost/numeric/conversion/converter.hpp"
21 #include "test_helpers.cpp"
22 #include "test_helpers2.cpp"
23 #include "test_helpers3.cpp"
26 using namespace boost
;
27 using namespace numeric
;
28 using namespace MyUDT
;
30 //-------------------------------------------------------------------------
31 // These are the typical steps that are required to install support for
32 // conversions from/to UDT which need special treatment.
33 //-------------------------------------------------------------------------
38 // (1) Instantiate specific convesions traits.
39 // This step is only for convenience.
40 // These traits instances are required in order to define the specializations
41 // that follow (and which *are required* to make the library work with MyInt and MyFloat)
45 typedef conversion_traits
<double , MyFloat
> MyFloat_to_double_Traits
;
46 typedef conversion_traits
<int , MyFloat
> MyFloat_to_int_Traits
;
47 typedef conversion_traits
<MyInt
, MyFloat
> MyFloat_to_MyInt_Traits
;
48 typedef conversion_traits
<int , MyInt
> MyInt_to_int_Traits
;
49 typedef conversion_traits
<MyFloat
, MyInt
> MyInt_to_MyFloat_Traits
;
50 typedef conversion_traits
<MyInt
, double > double_to_MyInt_Traits
;
56 // (2) Define suitable raw converters.
58 // Our sample UDTs don't support implicit conversions.
59 // Therefore, the default raw_converter<> doesn't work,
60 // and we need to define our own.
62 // There are two ways of doing this:
64 // (a) One is to simply specialize boost::numeric::raw_converter<> directly.
65 // This way, the default converter will work out of the box, which means, for instance,
66 // that numeric_cast<> can be used with these UDTs.
68 // (b) Define a user class with the appropriate interface and supply it explicitely
69 // as a policy to a converter instance.
71 // This test uses chice (a).
78 struct raw_converter
<MyUDT::MyFloat_to_double_Traits
>
80 static double low_level_convert ( MyUDT::MyFloat
const& s
)
81 { return s
.to_builtin() ; }
85 struct raw_converter
<MyUDT::MyFloat_to_int_Traits
>
87 static int low_level_convert ( MyUDT::MyFloat
const& s
)
88 { return static_cast<int>( s
.to_builtin() ) ; }
92 struct raw_converter
<MyUDT::MyFloat_to_MyInt_Traits
>
94 static MyUDT::MyInt
low_level_convert ( MyUDT::MyFloat
const& s
)
95 { return MyUDT::MyInt( static_cast<int>(s
.to_builtin()) ) ; }
99 struct raw_converter
<MyUDT::MyInt_to_int_Traits
>
101 static int low_level_convert ( MyUDT::MyInt
const& s
) { return s
.to_builtin() ; }
105 struct raw_converter
<MyUDT::MyInt_to_MyFloat_Traits
>
107 static MyUDT::MyFloat
low_level_convert ( MyUDT::MyInt
const& s
)
109 return MyUDT::MyFloat( static_cast<double>(s
.to_builtin()) ) ;
114 struct raw_converter
<MyUDT::double_to_MyInt_Traits
>
116 static MyUDT::MyInt
low_level_convert ( double s
)
117 { return MyUDT::MyInt( static_cast<int>(s
) ) ; }
120 } // namespace numeric
127 // (3) Define suitable range checkers
129 // By default, if a UDT is involved in a conversion, internal range checking is disabled.
130 // This is so because a UDT type can have any sort of range, even unbounded, thus
131 // the library doesn't attempt to automatically figure out the appropriate range checking logic.
132 // (as it does when builtin types are involved)
133 // However, this situation is a bit unsufficient in practice, specially from doing narrowing (subranged)
134 // conversions from UDTs.
135 // The library provides a rudimentary hook to help this out: The user can plug in his own
136 // range checker to the converter instance.
138 // This test shows how to define and use a custom range checker.
144 // The following are metaprogramming tools to allow us the implement the
145 // MyCustomRangeChecker generically, for either builtin or UDT types.
148 // get_builtin_type<N>::type extracts the built-in type of our UDT's
150 template<class N
> struct get_builtin_type
{ typedef N type
; } ;
151 template<> struct get_builtin_type
<MyInt
> { typedef int type
; } ;
152 template<> struct get_builtin_type
<MyFloat
> { typedef double type
; } ;
154 // U extract_builtin ( T s ) returns 's' converted to the corresponding built-in type U.
157 struct extract_builtin
159 static N
apply ( N n
) { return n
; }
162 struct extract_builtin
<MyInt
>
164 static int apply ( MyInt
const& n
) { return n
.to_builtin() ; }
167 struct extract_builtin
<MyFloat
>
169 static double apply ( MyFloat
const& n
) { return n
.to_builtin() ; }
172 template<class Traits
>
173 struct MyCustomRangeChecker
175 typedef typename
Traits::argument_type argument_type
;
177 // This custom range checker uses the fact that our 'fake' UDT are merely wrappers
178 // around builtin types; so it just forward the logic to the correspoding range
179 // checkers for the wrapped builtin types.
181 typedef typename
Traits::source_type S
;
182 typedef typename
Traits::target_type T
;
184 // NOTE: S and/or T can be either UDT or builtin types.
186 typedef typename get_builtin_type
<S
>::type builtinS
;
187 typedef typename get_builtin_type
<T
>::type builtinT
;
189 // NOTE: The internal range checker used by default is *built* when you instantiate
190 // a converter<> with a given Traits according to the properties of the involved types.
191 // Currently, there is no way to instantiate this range checker as a separate class.
192 // However, you can see it as part of the interface of the converter
193 // (since the converter inherits from it)
194 // Therefore, here we instantiate a converter corresponding to the builtin types to access
195 // their associated builtin range checker.
197 typedef boost::numeric::converter
<builtinT
,builtinS
> InternalConverter
;
199 static range_check_result
out_of_range ( argument_type s
)
201 return InternalConverter::out_of_range( extract_builtin
<S
>::apply(s
) );
204 static void validate_range ( argument_type s
)
206 return InternalConverter::validate_range( extract_builtin
<S
>::apply(s
) );
223 void test_udt_conversions_with_defaults()
225 cout
<< "Testing UDT conversion with default policies\n" ;
231 TEST_SUCCEEDING_CONVERSION_DEF(MyInt
,int,miv
,mibv
);
232 TEST_SUCCEEDING_CONVERSION_DEF(int,MyInt
,mibv
,miv
);
234 // MyFloat <--> double
236 double mfbv
= static_cast<double>(rand()) / 3.0 ;
238 TEST_SUCCEEDING_CONVERSION_DEF(MyFloat
,double,mfv
,mfbv
);
239 TEST_SUCCEEDING_CONVERSION_DEF(double,MyFloat
,mfbv
,mfv
);
241 // MyInt <--> MyFloat
243 MyInt
miv2 ( static_cast<int>(mfbv
) );
244 MyFloat
miv2F ( static_cast<int>(mfbv
) );
245 MyFloat
mfv2 ( static_cast<double>(mibv
) );
246 MyInt
mfv2I ( static_cast<double>(mibv
) );
247 TEST_SUCCEEDING_CONVERSION_DEF(MyFloat
,MyInt
,miv2F
,miv2
);
248 TEST_SUCCEEDING_CONVERSION_DEF(MyInt
,MyFloat
,mfv2I
,mfv2
);
251 template<class T
, class S
>
252 struct GenerateCustomConverter
254 typedef conversion_traits
<T
,S
> Traits
;
256 typedef def_overflow_handler OverflowHandler
;
257 typedef Trunc
<S
> Float2IntRounder
;
258 typedef raw_converter
<Traits
> RawConverter
;
259 typedef MyCustomRangeChecker
<Traits
> RangeChecker
;
261 typedef converter
<T
,S
,Traits
,OverflowHandler
,Float2IntRounder
,RawConverter
,RangeChecker
> type
;
264 void test_udt_conversions_with_custom_range_checking()
266 cout
<< "Testing UDT conversions with custom range checker\n" ;
269 MyFloat
mfv ( static_cast<double>(mibv
) );
271 typedef GenerateCustomConverter
<MyFloat
,int>::type int_to_MyFloat_Conv
;
273 TEST_SUCCEEDING_CONVERSION( int_to_MyFloat_Conv
, MyFloat
, int, mfv
, mibv
);
277 MyFloat
mfv2 ( static_cast<double>(mibv2
) );
279 typedef GenerateCustomConverter
<MyFloat
,MyInt
>::type MyInt_to_MyFloat_Conv
;
281 TEST_SUCCEEDING_CONVERSION( MyInt_to_MyFloat_Conv
, MyFloat
, MyInt
, mfv2
, miv
);
283 double mfbv
= bounds
<double>::highest();
284 typedef GenerateCustomConverter
<MyInt
,double>::type double_to_MyInt_Conv
;
286 TEST_POS_OVERFLOW_CONVERSION( double_to_MyInt_Conv
, MyInt
, double, mfbv
);
288 MyFloat
mfv3 ( bounds
<double>::lowest() ) ;
289 typedef GenerateCustomConverter
<int,MyFloat
>::type MyFloat_to_int_Conv
;
291 TEST_NEG_OVERFLOW_CONVERSION( MyFloat_to_int_Conv
, int, MyFloat
, mfv3
);
295 int test_main( int, char* [] )
297 cout
<< setprecision( numeric_limits
<long double>::digits10
) ;
299 test_udt_conversions_with_defaults();
300 test_udt_conversions_with_custom_range_checking();