1 | // |
2 | //! Copyright (c) 2011 |
3 | //! Brandon Kohn |
4 | // |
5 | // Distributed under the Boost Software License, Version 1.0. (See |
6 | // accompanying file LICENSE_1_0.txt or copy at |
7 | // http://www.boost.org/LICENSE_1_0.txt) |
8 | // |
9 | |
10 | #include <boost/operators.hpp> |
11 | #include <boost/numeric/conversion/cast.hpp> |
12 | #include <boost/mpl/for_each.hpp> |
13 | #include <boost/mpl/vector.hpp> |
14 | #include <boost/cstdint.hpp> |
15 | #include <boost/core/lightweight_test.hpp> |
16 | #include <boost/static_assert.hpp> |
17 | |
18 | //! Define a simple custom number |
19 | struct Double |
20 | { |
21 | Double() |
22 | : v(0) |
23 | {} |
24 | |
25 | template <typename T> |
26 | explicit Double( T v ) |
27 | : v(static_cast<double>(v)) |
28 | {} |
29 | |
30 | template <typename T> |
31 | Double& operator= ( T t ) |
32 | { |
33 | v = static_cast<double>(t); |
34 | return *this; |
35 | } |
36 | |
37 | bool operator < ( const Double& rhs ) const |
38 | { |
39 | return v < rhs.v; |
40 | } |
41 | |
42 | template <typename T> |
43 | bool operator < ( T rhs ) const |
44 | { |
45 | return v < static_cast<double>(rhs); |
46 | } |
47 | |
48 | template <typename LHS> |
49 | friend bool operator < ( const LHS& lhs, const Double& rhs ) |
50 | { |
51 | return lhs < rhs.v; |
52 | } |
53 | |
54 | bool operator > ( const Double& rhs ) const |
55 | { |
56 | return v > rhs.v; |
57 | } |
58 | |
59 | template <typename LHS> |
60 | friend bool operator > ( const LHS& lhs, const Double& rhs ) |
61 | { |
62 | return lhs > rhs.v; |
63 | } |
64 | |
65 | template <typename T> |
66 | bool operator > ( T rhs ) const |
67 | { |
68 | return v > static_cast<double>(rhs); |
69 | } |
70 | |
71 | bool operator == ( const Double& rhs ) const |
72 | { |
73 | return v == rhs.v; |
74 | } |
75 | |
76 | template <typename T> |
77 | bool operator == ( T rhs ) const |
78 | { |
79 | return v == static_cast<double>(rhs); |
80 | } |
81 | |
82 | template <typename LHS> |
83 | friend bool operator == ( const LHS& lhs, const Double& rhs ) |
84 | { |
85 | return lhs == rhs.v; |
86 | } |
87 | |
88 | bool operator !() const |
89 | { |
90 | return v == 0; |
91 | } |
92 | |
93 | Double operator -() const |
94 | { |
95 | return Double(-v); |
96 | } |
97 | |
98 | Double& operator +=( const Double& t ) |
99 | { |
100 | v += t.v; |
101 | return *this; |
102 | } |
103 | |
104 | template <typename T> |
105 | Double& operator +=( T t ) |
106 | { |
107 | v += static_cast<double>(t); |
108 | return *this; |
109 | } |
110 | |
111 | Double& operator -=( const Double& t ) |
112 | { |
113 | v -= t.v; |
114 | return *this; |
115 | } |
116 | |
117 | template <typename T> |
118 | Double& operator -=( T t ) |
119 | { |
120 | v -= static_cast<double>(t); |
121 | return *this; |
122 | } |
123 | |
124 | Double& operator *= ( const Double& factor ) |
125 | { |
126 | v *= factor.v; |
127 | return *this; |
128 | } |
129 | |
130 | template <typename T> |
131 | Double& operator *=( T t ) |
132 | { |
133 | v *= static_cast<double>(t); |
134 | return *this; |
135 | } |
136 | |
137 | Double& operator /= (const Double& divisor) |
138 | { |
139 | v /= divisor.v; |
140 | return *this; |
141 | } |
142 | |
143 | template <typename T> |
144 | Double& operator /=( T t ) |
145 | { |
146 | v /= static_cast<double>(t); |
147 | return (*this); |
148 | } |
149 | |
150 | double v; |
151 | }; |
152 | |
153 | //! Define numeric_limits for the custom type. |
154 | namespace std |
155 | { |
156 | template<> |
157 | class numeric_limits< Double > : public numeric_limits<double> |
158 | { |
159 | public: |
160 | |
161 | //! Limit our Double to a range of +/- 100.0 |
162 | static Double (min)() |
163 | { |
164 | return Double(1.e-2); |
165 | } |
166 | |
167 | static Double (max)() |
168 | { |
169 | return Double(1.e2); |
170 | } |
171 | |
172 | static Double epsilon() |
173 | { |
174 | return Double( std::numeric_limits<double>::epsilon() ); |
175 | } |
176 | }; |
177 | } |
178 | |
179 | //! Define range checking and overflow policies. |
180 | namespace custom |
181 | { |
182 | //! Define a custom range checker |
183 | template<typename Traits, typename OverFlowHandler> |
184 | struct range_checker |
185 | { |
186 | typedef typename Traits::argument_type argument_type ; |
187 | typedef typename Traits::source_type S; |
188 | typedef typename Traits::target_type T; |
189 | |
190 | //! Check range of integral types. |
191 | static boost::numeric::range_check_result out_of_range( argument_type s ) |
192 | { |
193 | using namespace boost::numeric; |
194 | if( s > bounds<T>::highest() ) |
195 | return cPosOverflow; |
196 | else if( s < bounds<T>::lowest() ) |
197 | return cNegOverflow; |
198 | else |
199 | return cInRange; |
200 | } |
201 | |
202 | static void validate_range ( argument_type s ) |
203 | { |
204 | BOOST_STATIC_ASSERT( std::numeric_limits<T>::is_bounded ); |
205 | OverFlowHandler()( out_of_range(s) ); |
206 | } |
207 | }; |
208 | |
209 | //! Overflow handler |
210 | struct positive_overflow{}; |
211 | struct negative_overflow{}; |
212 | |
213 | struct overflow_handler |
214 | { |
215 | void operator() ( boost::numeric::range_check_result r ) |
216 | { |
217 | using namespace boost::numeric; |
218 | if( r == cNegOverflow ) |
219 | throw negative_overflow() ; |
220 | else if( r == cPosOverflow ) |
221 | throw positive_overflow() ; |
222 | } |
223 | }; |
224 | |
225 | //! Define a rounding policy and specialize on the custom type. |
226 | template<class S> |
227 | struct Ceil : boost::numeric::Ceil<S>{}; |
228 | |
229 | template<> |
230 | struct Ceil<Double> |
231 | { |
232 | typedef Double source_type; |
233 | |
234 | typedef Double const& argument_type; |
235 | |
236 | static source_type nearbyint ( argument_type s ) |
237 | { |
238 | #if !defined(BOOST_NO_STDC_NAMESPACE) |
239 | using std::ceil ; |
240 | #endif |
241 | return Double( ceil(x: s.v) ); |
242 | } |
243 | |
244 | typedef boost::mpl::integral_c< std::float_round_style, std::round_toward_infinity> round_style; |
245 | }; |
246 | |
247 | //! Define a rounding policy and specialize on the custom type. |
248 | template<class S> |
249 | struct Trunc: boost::numeric::Trunc<S>{}; |
250 | |
251 | template<> |
252 | struct Trunc<Double> |
253 | { |
254 | typedef Double source_type; |
255 | |
256 | typedef Double const& argument_type; |
257 | |
258 | static source_type nearbyint ( argument_type s ) |
259 | { |
260 | #if !defined(BOOST_NO_STDC_NAMESPACE) |
261 | using std::floor; |
262 | #endif |
263 | return Double( floor(x: s.v) ); |
264 | } |
265 | |
266 | typedef boost::mpl::integral_c< std::float_round_style, std::round_toward_zero> round_style; |
267 | }; |
268 | }//namespace custom; |
269 | |
270 | namespace boost { namespace numeric { |
271 | |
272 | //! Define the numeric_cast_traits specializations on the custom type. |
273 | template <typename S> |
274 | struct numeric_cast_traits<Double, S> |
275 | { |
276 | typedef custom::overflow_handler overflow_policy; |
277 | typedef custom::range_checker |
278 | < |
279 | boost::numeric::conversion_traits<Double, S> |
280 | , overflow_policy |
281 | > range_checking_policy; |
282 | typedef boost::numeric::Trunc<S> rounding_policy; |
283 | }; |
284 | |
285 | template <typename T> |
286 | struct numeric_cast_traits<T, Double> |
287 | { |
288 | typedef custom::overflow_handler overflow_policy; |
289 | typedef custom::range_checker |
290 | < |
291 | boost::numeric::conversion_traits<T, Double> |
292 | , overflow_policy |
293 | > range_checking_policy; |
294 | typedef custom::Trunc<Double> rounding_policy; |
295 | }; |
296 | |
297 | //! Define the conversion from the custom type to built-in types and vice-versa. |
298 | template<typename T> |
299 | struct raw_converter< conversion_traits< T, Double > > |
300 | { |
301 | static T low_level_convert ( const Double& n ) |
302 | { |
303 | return static_cast<T>( n.v ); |
304 | } |
305 | }; |
306 | |
307 | template<typename S> |
308 | struct raw_converter< conversion_traits< Double, S > > |
309 | { |
310 | static Double low_level_convert ( const S& n ) |
311 | { |
312 | return Double(n); |
313 | } |
314 | }; |
315 | }}//namespace boost::numeric; |
316 | |
317 | #define BOOST_TEST_CATCH_CUSTOM_POSITIVE_OVERFLOW( CastCode ) \ |
318 | BOOST_TEST_THROWS( CastCode, custom::positive_overflow ) |
319 | /***/ |
320 | |
321 | #define BOOST_TEST_CATCH_CUSTOM_NEGATIVE_OVERFLOW( CastCode ) \ |
322 | BOOST_TEST_THROWS( CastCode, custom::negative_overflow ) |
323 | /***/ |
324 | |
325 | struct test_cast_traits |
326 | { |
327 | template <typename T> |
328 | void operator()(T) const |
329 | { |
330 | Double d = boost::numeric_cast<Double>( static_cast<T>(50) ); |
331 | BOOST_TEST( d.v == 50. ); |
332 | T v = boost::numeric_cast<T>( d ); |
333 | BOOST_TEST( v == 50 ); |
334 | } |
335 | }; |
336 | |
337 | void test_numeric_cast_traits() |
338 | { |
339 | typedef boost::mpl::vector |
340 | < |
341 | boost::int8_t |
342 | , boost::uint8_t |
343 | , boost::int16_t |
344 | , boost::uint16_t |
345 | , boost::int32_t |
346 | , boost::uint32_t |
347 | #if !defined( BOOST_NO_INT64_T ) |
348 | , boost::int64_t |
349 | , boost::uint64_t |
350 | #endif |
351 | , float |
352 | , double |
353 | , long double |
354 | > types; |
355 | boost::mpl::for_each<types>( f: test_cast_traits() ); |
356 | |
357 | //! Check overflow handler. |
358 | Double d( 56.0 ); |
359 | BOOST_TEST_CATCH_CUSTOM_POSITIVE_OVERFLOW( d = boost::numeric_cast<Double>( 101 ) ); |
360 | BOOST_TEST( d.v == 56. ); |
361 | BOOST_TEST_CATCH_CUSTOM_NEGATIVE_OVERFLOW( d = boost::numeric_cast<Double>( -101 ) ); |
362 | BOOST_TEST( d.v == 56.); |
363 | |
364 | //! Check custom round policy. |
365 | d = 5.9; |
366 | int five = boost::numeric_cast<int>( arg: d ); |
367 | BOOST_TEST( five == 5 ); |
368 | } |
369 | |
370 | int main() |
371 | { |
372 | test_numeric_cast_traits(); |
373 | return boost::report_errors(); |
374 | } |
375 | |
376 | #undef BOOST_TEST_CATCH_CUSTOM_POSITIVE_OVERFLOW |
377 | #undef BOOST_TEST_CATCH_CUSTOM_NEGATIVE_OVERFLOW |
378 | |