• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright (C) 2005, 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 "boost/config.hpp"
9 #include "boost/utility.hpp"
10 #include "boost/limits.hpp"
11 #include "boost/utility.hpp"
12 
13 #include<iostream>
14 #include<iomanip>
15 #include<string>
16 #include<cmath>
17 
18 
19 #include "boost/test/included/test_exec_monitor.hpp"
20 
21 #include "boost/numeric/conversion/cast.hpp"
22 
23 using namespace std ;
24 using namespace boost;
25 using namespace numeric;
26 
27 //
28 // This example illustrates how to add support for user defined types (UDTs)
29 // to the Boost Numeric Conversion Library.
30 // It is assumed that you are familiar with the following documentation:
31 //
32 //
33 
34 //
35 // The minimum requirement is that boost::is_arithmetic<UDT> evaluates to false
36 // (Otherwise the converter code will try to examine the UDT as a built-in type)
37 //
38 
39 //
40 // Let's start with the simpliest case of an UDT which supports standard conversions
41 //
42 struct Double
43 {
DoubleDouble44   Double( double v ) : mV(v) {}
45 
operator doubleDouble46   operator double() const { return mV ; }
47 
48   double mV ;
49 } ;
50 
51 double dv = (numeric_limits<double>::max)() ;
52 double fv = (numeric_limits<float >::max)() ;
53 Double Dv(dv);
54 Double Fv(fv);
55 
simplest_case()56 void simplest_case()
57 {
58   //
59   // conversion_traits<>::udt_builtin_mixture works out of the box as long as boost::is_arithmetic<UDT> yields false
60   //
61   BOOST_CHECK( (conversion_traits<double,Double>::udt_builtin_mixture::value == udt_to_builtin) ) ;
62   BOOST_CHECK( (conversion_traits<Double,double>::udt_builtin_mixture::value == builtin_to_udt) ) ;
63   BOOST_CHECK( (conversion_traits<Double,Double>::udt_builtin_mixture::value == udt_to_udt    ) ) ;
64 
65   // BY DEFINITION, a conversion from UDT to Builtin is subranged. No attempt is made to actually compare ranges.
66   BOOST_CHECK( (conversion_traits<double,Double>::subranged::value) == true  ) ;
67   BOOST_CHECK( (conversion_traits<Double,double>::subranged::value) == false ) ;
68 
69 
70 
71   //
72   // Conversions to/from FLOATING types, if already supported by an UDT
73   // are also supported out-of-the-box by converter<> in its default configuration.
74   //
75   BOOST_CHECK( numeric_cast<double>(Dv) == static_cast<double>(Dv) ) ;
76   BOOST_CHECK( numeric_cast<Double>(dv) == static_cast<Double>(dv) ) ;
77 
78   BOOST_CHECK( numeric_cast<float> (Dv) == static_cast<float> (Dv) ) ;
79   BOOST_CHECK( numeric_cast<Double>(fv) == static_cast<Double>(fv) ) ;
80 
81 
82   //
83   // Range checking is disabled by default if an UDT is either the source or target of the conversion.
84   //
85   BOOST_CHECK( (converter<float,double>::out_of_range(dv) == cPosOverflow) );
86   BOOST_CHECK( (converter<float,Double>::out_of_range(Dv) == cInRange) );
87 
88 }
89 
90 //
91 // The conversion_traits<> class and therefore the converter<> class looks at
92 // numeric_limits<UDT>::is_integer/is_signed to generate the proper float_in and sign mixtures.
93 // In most implementations, is_integer/is_signed are both false for UDTs if there is no explicit specialization for it.
94 // Therefore, the converter<> will see any UDT for which numeric_limits<> is not specialized as Float AND unsigned.
95 // Signess is used in the converter<> for range checking, but range checking is disabled by default for UDTs, so,
96 // normally, signess is mostly irrelevant as far as the library is concerned, except for the numeric_traits<>::sign_mixture
97 // entry.
98 // is_integer, however, is relevant in that if the conversion is from a float type to an integer type, the conversion is
99 // "rounding" and the rounder policies will participate.
100 // ALL implemented rounder policies require proper definitions for floor(udt) and ceil(udt).
101 // These names will be searched for using ADL, so, if you need to convert TO integral types from a UDT,
102 // you need to supply those functions along with the UDT in right namespace (that is, any namespace that allows
103 // ADL to find them)
104 
105 // If your UDT doesn't supply floor/ceil, conversions to integer types
106 // won't compile unless a custom Float2IntRounder is used.
107 
floor(Double v)108 Double floor ( Double v ) { return Double(std::floor(v.mV)) ; }
ceil(Double v)109 Double ceil  ( Double v ) { return Double(std::ceil (v.mV)) ; }
110 
rounding()111 void rounding()
112 {
113   BOOST_CHECK( numeric_cast<int>(Dv) == static_cast<int>(Dv) ) ;
114 }
115 
116 
117 //
118 // If your UDT can't or won't provide floor/ceil you can set-up and use your own
119 // Float2IntRounder policy (though doing this is not always required as shown so far)
120 //
121 struct DoubleToInt
122 {
nearbyintDoubleToInt123   static Double nearbyint ( Double const& s ) { return Double(static_cast<int>(s)); }
124 
125   typedef mpl::integral_c< std::float_round_style, std::round_toward_zero> round_style ;
126 } ;
127 
custom_rounding()128 void custom_rounding()
129 {
130   typedef converter<int
131                    ,Double
132                    ,conversion_traits<int,Double>
133                    ,void // By default UDT disable range checking so this won't be used
134                    ,DoubleToInt
135                    >
136                    DoubleToIntConverter ;
137 
138    BOOST_CHECK( DoubleToIntConverter::convert(Dv) == static_cast<int>(Dv) ) ;
139 }
140 
141 //
142 // In the next Level of complexity, your UDTs might not support conversion operators
143 //
144 struct Float
145 {
FloatFloat146   Float( float v ) : mV(v) {}
147 
148   float mV ;
149 } ;
150 
151 struct Int
152 {
IntInt153   Int( int v ) : mV(v) {}
154 
155   int mV ;
156 } ;
157 
158 typedef conversion_traits<Int,Float> Float2IntTraits ;
159 typedef conversion_traits<Float,Int> Int2FloatTraits ;
160 
161 namespace boost { namespace numeric
162 {
163 //
164 // Though static_cast<> won't work with them you can still use numeric_cast<> by specializing
165 // raw_converter as follows:
166 //
167 template<> struct raw_converter<Float2IntTraits>
168 {
169   typedef Float2IntTraits::result_type   result_type   ;
170   typedef Float2IntTraits::argument_type argument_type ;
171 
low_level_convertboost::numeric::raw_converter172   static result_type low_level_convert ( argument_type s ) { return Int((int)s.mV); }
173 } ;
174 template<> struct raw_converter<Int2FloatTraits>
175 {
176   typedef Int2FloatTraits::result_type   result_type   ;
177   typedef Int2FloatTraits::argument_type argument_type ;
178 
low_level_convertboost::numeric::raw_converter179   static result_type low_level_convert ( argument_type s ) { return Float(s.mV); }
180 } ;
181 
182 } }
183 
custom_raw_converter()184 void custom_raw_converter()
185 {
186   Float f (12.34);
187   Int   i (12);
188   Float fi(12);
189 
190   BOOST_CHECK(numeric_cast<Int>  (f).mV == i .mV ) ;
191   BOOST_CHECK(numeric_cast<Float>(i).mV == fi.mV ) ;
192 }
193 
194 //
195 // Alterntively, the custom raw_converter classes can be defined non-instrusively
196 // (not as specializations) and passed along as policies
197 //
198 struct Float2IntRawConverter
199 {
low_level_convertFloat2IntRawConverter200   static Int low_level_convert ( Float const& s ) { return Int((int)s.mV); }
201 } ;
202 struct Int2FloatRawConverter
203 {
low_level_convertInt2FloatRawConverter204   static Float low_level_convert ( Int const& s ) { return Float(s.mV); }
205 } ;
206 
custom_raw_converter2()207 void custom_raw_converter2()
208 {
209   Float f (12.34);
210   Int   i (12);
211   Float fi(12);
212 
213   typedef converter<Int
214                    ,Float
215                    ,Float2IntTraits
216                    ,void  // By default UDT disable range checking so this won't be used
217                    ,void  // Float2Int Rounder won't be used if Int isn't marked as integer via numeric_limits<>
218                    ,Float2IntRawConverter
219                    >
220                    Float2IntConverter ;
221 
222   BOOST_CHECK(Float2IntConverter::convert(f).mV == i .mV ) ;
223 }
224 
test_main(int,char * [])225 int test_main( int, char* [] )
226 {
227   cout << setprecision( numeric_limits<long double>::digits10 ) ;
228 
229   simplest_case();
230   rounding();
231   custom_rounding();
232   custom_raw_converter();
233   custom_raw_converter2();
234 
235   return 0;
236 }
237 
238 
239 
240 
241 
242 
243