1 // Copyright (C) 2005, Fernando Luis Cacciola Carballal.
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)
8 #include "boost/config.hpp"
9 #include "boost/utility.hpp"
10 #include "boost/limits.hpp"
11 #include "boost/utility.hpp"
19 #include "boost/test/included/test_exec_monitor.hpp"
21 #include "boost/numeric/conversion/cast.hpp"
24 using namespace boost
;
25 using namespace numeric
;
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:
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)
40 // Let's start with the simpliest case of an UDT which supports standard conversions
44 Double( double v
) : mV(v
) {}
46 operator double() const { return mV
; }
51 double dv
= (numeric_limits
<double>::max
)() ;
52 double fv
= (numeric_limits
<float >::max
)() ;
59 // conversion_traits<>::udt_builtin_mixture works out of the box as long as boost::is_arithmetic<UDT> yields false
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
) ) ;
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 ) ;
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.
75 BOOST_CHECK( numeric_cast
<double>(Dv
) == static_cast<double>(Dv
) ) ;
76 BOOST_CHECK( numeric_cast
<Double
>(dv
) == static_cast<Double
>(dv
) ) ;
78 BOOST_CHECK( numeric_cast
<float> (Dv
) == static_cast<float> (Dv
) ) ;
79 BOOST_CHECK( numeric_cast
<Double
>(fv
) == static_cast<Double
>(fv
) ) ;
83 // Range checking is disabled by default if an UDT is either the source or target of the conversion.
85 BOOST_CHECK( (converter
<float,double>::out_of_range(dv
) == cPosOverflow
) );
86 BOOST_CHECK( (converter
<float,Double
>::out_of_range(Dv
) == cInRange
) );
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
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
105 // If your UDT doesn't supply floor/ceil, conversions to integer types
106 // won't compile unless a custom Float2IntRounder is used.
108 Double
floor ( Double v
) { return Double(std::floor(v
.mV
)) ; }
109 Double
ceil ( Double v
) { return Double(std::ceil (v
.mV
)) ; }
113 BOOST_CHECK( numeric_cast
<int>(Dv
) == static_cast<int>(Dv
) ) ;
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)
123 static Double
nearbyint ( Double
const& s
) { return Double(static_cast<int>(s
)); }
125 typedef mpl::integral_c
< std::float_round_style
, std::round_toward_zero
> round_style
;
128 void custom_rounding()
130 typedef converter
<int
132 ,conversion_traits
<int,Double
>
133 ,void // By default UDT disable range checking so this won't be used
136 DoubleToIntConverter
;
138 BOOST_CHECK( DoubleToIntConverter::convert(Dv
) == static_cast<int>(Dv
) ) ;
142 // In the next Level of complexity, your UDTs might not support conversion operators
146 Float( float v
) : mV(v
) {}
153 Int( int v
) : mV(v
) {}
158 typedef conversion_traits
<Int
,Float
> Float2IntTraits
;
159 typedef conversion_traits
<Float
,Int
> Int2FloatTraits
;
161 namespace boost
{ namespace numeric
164 // Though static_cast<> won't work with them you can still use numeric_cast<> by specializing
165 // raw_converter as follows:
167 template<> struct raw_converter
<Float2IntTraits
>
169 typedef Float2IntTraits::result_type result_type
;
170 typedef Float2IntTraits::argument_type argument_type
;
172 static result_type
low_level_convert ( argument_type s
) { return Int((int)s
.mV
); }
174 template<> struct raw_converter
<Int2FloatTraits
>
176 typedef Int2FloatTraits::result_type result_type
;
177 typedef Int2FloatTraits::argument_type argument_type
;
179 static result_type
low_level_convert ( argument_type s
) { return Float(s
.mV
); }
184 void custom_raw_converter()
190 BOOST_CHECK(numeric_cast
<Int
> (f
).mV
== i
.mV
) ;
191 BOOST_CHECK(numeric_cast
<Float
>(i
).mV
== fi
.mV
) ;
195 // Alterntively, the custom raw_converter classes can be defined non-instrusively
196 // (not as specializations) and passed along as policies
198 struct Float2IntRawConverter
200 static Int
low_level_convert ( Float
const& s
) { return Int((int)s
.mV
); }
202 struct Int2FloatRawConverter
204 static Float
low_level_convert ( Int
const& s
) { return Float(s
.mV
); }
207 void custom_raw_converter2()
213 typedef converter
<Int
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
222 BOOST_CHECK(Float2IntConverter::convert(f
).mV
== i
.mV
) ;
225 int test_main( int, char* [] )
227 cout
<< setprecision( numeric_limits
<long double>::digits10
) ;
232 custom_raw_converter();
233 custom_raw_converter2();