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
23using namespace std ;
24using namespace boost;
25using 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//
42struct Double
43{
44 Double( double v ) : mV(v) {}
45
46 operator double() const { return mV ; }
47
48 double mV ;
49} ;
50
51double dv = (numeric_limits<double>::max)() ;
52double fv = (numeric_limits<float >::max)() ;
53Double Dv(dv);
54Double Fv(fv);
55
56void 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
108Double floor ( Double v ) { return Double(std::floor(x: v.mV)) ; }
109Double ceil ( Double v ) { return Double(std::ceil (x: v.mV)) ; }
110
111void 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//
121struct 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
128void 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//
144struct Float
145{
146 Float( float v ) : mV(v) {}
147
148 float mV ;
149} ;
150
151struct Int
152{
153 Int( int v ) : mV(v) {}
154
155 int mV ;
156} ;
157
158typedef conversion_traits<Int,Float> Float2IntTraits ;
159typedef conversion_traits<Float,Int> Int2FloatTraits ;
160
161namespace 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//
167template<> 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} ;
174template<> 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
184void 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//
198struct Float2IntRawConverter
199{
200 static Int low_level_convert ( Float const& s ) { return Int((int)s.mV); }
201} ;
202struct Int2FloatRawConverter
203{
204 static Float low_level_convert ( Int const& s ) { return Float(s.mV); }
205} ;
206
207void 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
225int 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

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