1 | // (C) Copyright 2003, Fernando Luis Cacciola Carballal. |
2 | // |
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) |
6 | // |
7 | // |
8 | #include<iostream> |
9 | #include<iomanip> |
10 | #include<string> |
11 | #include<typeinfo> |
12 | #include<vector> |
13 | #include<algorithm> |
14 | |
15 | #include "boost/numeric/conversion/converter.hpp" |
16 | |
17 | #ifdef BOOST_BORLANDC |
18 | #pragma hdrstop |
19 | #endif |
20 | |
21 | #include "test_helpers.cpp" |
22 | #include "test_helpers2.cpp" |
23 | #include "test_helpers3.cpp" |
24 | |
25 | using namespace std ; |
26 | using namespace boost ; |
27 | using namespace numeric ; |
28 | using namespace MyUDT ; |
29 | |
30 | //------------------------------------------------------------------------- |
31 | // These are the typical steps that are required to install support for |
32 | // conversions from/to UDT which need special treatment. |
33 | //------------------------------------------------------------------------- |
34 | |
35 | |
36 | |
37 | // |
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) |
42 | // |
43 | namespace MyUDT { |
44 | |
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; |
51 | |
52 | } // namespace MyUDT |
53 | |
54 | |
55 | // |
56 | // (2) Define suitable raw converters. |
57 | // |
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. |
61 | // |
62 | // There are two ways of doing this: |
63 | // |
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. |
67 | // |
68 | // (b) Define a user class with the appropriate interface and supply it explicitely |
69 | // as a policy to a converter instance. |
70 | // |
71 | // This test uses chice (a). |
72 | // |
73 | namespace boost { |
74 | |
75 | namespace numeric { |
76 | |
77 | template<> |
78 | struct raw_converter<MyUDT::MyFloat_to_double_Traits> |
79 | { |
80 | static double low_level_convert ( MyUDT::MyFloat const& s ) |
81 | { return s.to_builtin() ; } |
82 | } ; |
83 | |
84 | template<> |
85 | struct raw_converter<MyUDT::MyFloat_to_int_Traits> |
86 | { |
87 | static int low_level_convert ( MyUDT::MyFloat const& s ) |
88 | { return static_cast<int>( s.to_builtin() ) ; } |
89 | } ; |
90 | |
91 | template<> |
92 | struct raw_converter<MyUDT::MyFloat_to_MyInt_Traits> |
93 | { |
94 | static MyUDT::MyInt low_level_convert ( MyUDT::MyFloat const& s ) |
95 | { return MyUDT::MyInt( static_cast<int>(s.to_builtin()) ) ; } |
96 | } ; |
97 | |
98 | template<> |
99 | struct raw_converter<MyUDT::MyInt_to_int_Traits> |
100 | { |
101 | static int low_level_convert ( MyUDT::MyInt const& s ) { return s.to_builtin() ; } |
102 | } ; |
103 | |
104 | template<> |
105 | struct raw_converter<MyUDT::MyInt_to_MyFloat_Traits> |
106 | { |
107 | static MyUDT::MyFloat low_level_convert ( MyUDT::MyInt const& s ) |
108 | { |
109 | return MyUDT::MyFloat( static_cast<double>(s.to_builtin()) ) ; |
110 | } |
111 | } ; |
112 | |
113 | template<> |
114 | struct raw_converter<MyUDT::double_to_MyInt_Traits> |
115 | { |
116 | static MyUDT::MyInt low_level_convert ( double s ) |
117 | { return MyUDT::MyInt( static_cast<int>(s) ) ; } |
118 | } ; |
119 | |
120 | } // namespace numeric |
121 | |
122 | } // namespace boost |
123 | |
124 | |
125 | |
126 | // |
127 | // (3) Define suitable range checkers |
128 | // |
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. |
137 | // |
138 | // This test shows how to define and use a custom range checker. |
139 | // |
140 | |
141 | namespace MyUDT { |
142 | |
143 | // |
144 | // The following are metaprogramming tools to allow us the implement the |
145 | // MyCustomRangeChecker generically, for either builtin or UDT types. |
146 | // |
147 | |
148 | // get_builtin_type<N>::type extracts the built-in type of our UDT's |
149 | // |
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 ; } ; |
153 | |
154 | // U extract_builtin ( T s ) returns 's' converted to the corresponding built-in type U. |
155 | // |
156 | template<class N> |
157 | struct |
158 | { |
159 | static N ( N n ) { return n ; } |
160 | } ; |
161 | template<> |
162 | struct <MyInt> |
163 | { |
164 | static int ( MyInt const& n ) { return n.to_builtin() ; } |
165 | } ; |
166 | template<> |
167 | struct <MyFloat> |
168 | { |
169 | static double ( MyFloat const& n ) { return n.to_builtin() ; } |
170 | } ; |
171 | |
172 | template<class Traits> |
173 | struct MyCustomRangeChecker |
174 | { |
175 | typedef typename Traits::argument_type argument_type ; |
176 | |
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. |
180 | // |
181 | typedef typename Traits::source_type S ; |
182 | typedef typename Traits::target_type T ; |
183 | |
184 | // NOTE: S and/or T can be either UDT or builtin types. |
185 | |
186 | typedef typename get_builtin_type<S>::type builtinS ; |
187 | typedef typename get_builtin_type<T>::type builtinT ; |
188 | |
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. |
196 | // |
197 | typedef boost::numeric::converter<builtinT,builtinS> InternalConverter ; |
198 | |
199 | static range_check_result out_of_range ( argument_type s ) |
200 | { |
201 | return InternalConverter::out_of_range( extract_builtin<S>::apply(s) ); |
202 | } |
203 | |
204 | static void validate_range ( argument_type s ) |
205 | { |
206 | return InternalConverter::validate_range( extract_builtin<S>::apply(s) ); |
207 | } |
208 | } ; |
209 | |
210 | } // namespace MyUDT |
211 | |
212 | |
213 | |
214 | |
215 | |
216 | |
217 | |
218 | |
219 | // |
220 | // Test here |
221 | // |
222 | |
223 | void test_udt_conversions_with_defaults() |
224 | { |
225 | cout << "Testing UDT conversion with default policies\n" ; |
226 | |
227 | // MyInt <--> int |
228 | |
229 | int mibv = rand(); |
230 | MyInt miv(mibv); |
231 | TEST_SUCCEEDING_CONVERSION_DEF(MyInt,int,miv,mibv); |
232 | TEST_SUCCEEDING_CONVERSION_DEF(int,MyInt,mibv,miv); |
233 | |
234 | // MyFloat <--> double |
235 | |
236 | double mfbv = static_cast<double>(rand()) / 3.0 ; |
237 | MyFloat mfv (mfbv); |
238 | TEST_SUCCEEDING_CONVERSION_DEF(MyFloat,double,mfv,mfbv); |
239 | TEST_SUCCEEDING_CONVERSION_DEF(double,MyFloat,mfbv,mfv); |
240 | |
241 | // MyInt <--> MyFloat |
242 | |
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); |
249 | } |
250 | |
251 | template<class T, class S> |
252 | struct GenerateCustomConverter |
253 | { |
254 | typedef conversion_traits<T,S> Traits; |
255 | |
256 | typedef def_overflow_handler OverflowHandler ; |
257 | typedef Trunc<S> Float2IntRounder ; |
258 | typedef raw_converter<Traits> RawConverter ; |
259 | typedef MyCustomRangeChecker<Traits> RangeChecker ; |
260 | |
261 | typedef converter<T,S,Traits,OverflowHandler,Float2IntRounder,RawConverter,RangeChecker> type ; |
262 | } ; |
263 | |
264 | void test_udt_conversions_with_custom_range_checking() |
265 | { |
266 | cout << "Testing UDT conversions with custom range checker\n" ; |
267 | |
268 | int mibv = rand(); |
269 | MyFloat mfv ( static_cast<double>(mibv) ); |
270 | |
271 | typedef GenerateCustomConverter<MyFloat,int>::type int_to_MyFloat_Conv ; |
272 | |
273 | TEST_SUCCEEDING_CONVERSION( int_to_MyFloat_Conv, MyFloat, int, mfv, mibv ); |
274 | |
275 | int mibv2 = rand(); |
276 | MyInt miv (mibv2); |
277 | MyFloat mfv2 ( static_cast<double>(mibv2) ); |
278 | |
279 | typedef GenerateCustomConverter<MyFloat,MyInt>::type MyInt_to_MyFloat_Conv ; |
280 | |
281 | TEST_SUCCEEDING_CONVERSION( MyInt_to_MyFloat_Conv, MyFloat, MyInt, mfv2, miv ); |
282 | |
283 | double mfbv = bounds<double>::highest(); |
284 | typedef GenerateCustomConverter<MyInt,double>::type double_to_MyInt_Conv ; |
285 | |
286 | TEST_POS_OVERFLOW_CONVERSION( double_to_MyInt_Conv, MyInt, double, mfbv ); |
287 | |
288 | MyFloat mfv3 ( bounds<double>::lowest() ) ; |
289 | typedef GenerateCustomConverter<int,MyFloat>::type MyFloat_to_int_Conv ; |
290 | |
291 | TEST_NEG_OVERFLOW_CONVERSION( MyFloat_to_int_Conv, int, MyFloat, mfv3 ); |
292 | } |
293 | |
294 | |
295 | int main() |
296 | { |
297 | cout << setprecision( numeric_limits<long double>::digits10 ) ; |
298 | |
299 | test_udt_conversions_with_defaults(); |
300 | test_udt_conversions_with_custom_range_checking(); |
301 | |
302 | return boost::report_errors(); |
303 | } |
304 | |
305 | |
306 | |
307 | |
308 | |
309 | |
310 | |