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/core/lightweight_test.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 | { |
44 | Double( double v ) : mV(v) {} |
45 | |
46 | 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 | |
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_TEST( (conversion_traits<double,Double>::udt_builtin_mixture::value == udt_to_builtin) ) ; |
62 | BOOST_TEST( (conversion_traits<Double,double>::udt_builtin_mixture::value == builtin_to_udt) ) ; |
63 | BOOST_TEST( (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_TEST( (conversion_traits<double,Double>::subranged::value) == true ) ; |
67 | BOOST_TEST( (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_TEST( numeric_cast<double>(Dv) == static_cast<double>(Dv) ) ; |
76 | BOOST_TEST( numeric_cast<Double>(dv) == static_cast<Double>(dv) ) ; |
77 | |
78 | BOOST_TEST( numeric_cast<float> (Dv) == static_cast<float> (Dv) ) ; |
79 | BOOST_TEST( 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_TEST( (converter<float,double>::out_of_range(dv) == cPosOverflow) ); |
86 | BOOST_TEST( (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 | |
108 | Double floor ( Double v ) { return Double(std::floor(x: v.mV)) ; } |
109 | Double ceil ( Double v ) { return Double(std::ceil (x: v.mV)) ; } |
110 | |
111 | void rounding() |
112 | { |
113 | BOOST_TEST( 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 | { |
123 | 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 | |
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_TEST( 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 | { |
146 | Float( float v ) : mV(v) {} |
147 | |
148 | float mV ; |
149 | } ; |
150 | |
151 | struct Int |
152 | { |
153 | 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 | |
172 | 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 | |
179 | static result_type low_level_convert ( argument_type s ) { return Float(s.mV); } |
180 | } ; |
181 | |
182 | } } |
183 | |
184 | void custom_raw_converter() |
185 | { |
186 | Float f (12.34); |
187 | Int i (12); |
188 | Float fi(12); |
189 | |
190 | BOOST_TEST(numeric_cast<Int> (f).mV == i .mV ) ; |
191 | BOOST_TEST(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 | { |
200 | static Int low_level_convert ( Float const& s ) { return Int((int)s.mV); } |
201 | } ; |
202 | struct Int2FloatRawConverter |
203 | { |
204 | static Float low_level_convert ( Int const& s ) { return Float(s.mV); } |
205 | } ; |
206 | |
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_TEST(Float2IntConverter::convert(f).mV == i .mV ) ; |
223 | } |
224 | |
225 | int main() |
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 boost::report_errors(); |
236 | } |
237 | |
238 | |
239 | |
240 | |
241 | |
242 | |
243 | |