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
19struct 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.
154namespace 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.
180namespace 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
270namespace 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
325struct 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
337void 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
370int 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

source code of boost/libs/numeric/conversion/test/numeric_cast_traits_test.cpp