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