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