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 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309
|
// (C) Copyright 2003, 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<iostream>
#include<iomanip>
#include<string>
#include<typeinfo>
#include<vector>
#include<algorithm>
#include "boost/numeric/conversion/converter.hpp"
#ifdef __BORLANDC__
#pragma hdrstop
#endif
#include "test_helpers.cpp"
#include "test_helpers2.cpp"
#include "test_helpers3.cpp"
using namespace std ;
using namespace boost ;
using namespace numeric ;
using namespace MyUDT ;
//-------------------------------------------------------------------------
// These are the typical steps that are required to install support for
// conversions from/to UDT which need special treatment.
//-------------------------------------------------------------------------
//
// (1) Instantiate specific convesions traits.
// This step is only for convenience.
// These traits instances are required in order to define the specializations
// that follow (and which *are required* to make the library work with MyInt and MyFloat)
//
namespace MyUDT {
typedef conversion_traits<double , MyFloat> MyFloat_to_double_Traits;
typedef conversion_traits<int , MyFloat> MyFloat_to_int_Traits;
typedef conversion_traits<MyInt , MyFloat> MyFloat_to_MyInt_Traits;
typedef conversion_traits<int , MyInt > MyInt_to_int_Traits;
typedef conversion_traits<MyFloat, MyInt > MyInt_to_MyFloat_Traits;
typedef conversion_traits<MyInt , double > double_to_MyInt_Traits;
} // namespace MyUDT
//
// (2) Define suitable raw converters.
//
// Our sample UDTs don't support implicit conversions.
// Therefore, the default raw_converter<> doesn't work,
// and we need to define our own.
//
// There are two ways of doing this:
//
// (a) One is to simply specialize boost::numeric::raw_converter<> directly.
// This way, the default converter will work out of the box, which means, for instance,
// that numeric_cast<> can be used with these UDTs.
//
// (b) Define a user class with the appropriate interface and supply it explicitely
// as a policy to a converter instance.
//
// This test uses chice (a).
//
namespace boost {
namespace numeric {
template<>
struct raw_converter<MyUDT::MyFloat_to_double_Traits>
{
static double low_level_convert ( MyUDT::MyFloat const& s )
{ return s.to_builtin() ; }
} ;
template<>
struct raw_converter<MyUDT::MyFloat_to_int_Traits>
{
static int low_level_convert ( MyUDT::MyFloat const& s )
{ return static_cast<int>( s.to_builtin() ) ; }
} ;
template<>
struct raw_converter<MyUDT::MyFloat_to_MyInt_Traits>
{
static MyUDT::MyInt low_level_convert ( MyUDT::MyFloat const& s )
{ return MyUDT::MyInt( static_cast<int>(s.to_builtin()) ) ; }
} ;
template<>
struct raw_converter<MyUDT::MyInt_to_int_Traits>
{
static int low_level_convert ( MyUDT::MyInt const& s ) { return s.to_builtin() ; }
} ;
template<>
struct raw_converter<MyUDT::MyInt_to_MyFloat_Traits>
{
static MyUDT::MyFloat low_level_convert ( MyUDT::MyInt const& s )
{
return MyUDT::MyFloat( static_cast<double>(s.to_builtin()) ) ;
}
} ;
template<>
struct raw_converter<MyUDT::double_to_MyInt_Traits>
{
static MyUDT::MyInt low_level_convert ( double s )
{ return MyUDT::MyInt( static_cast<int>(s) ) ; }
} ;
} // namespace numeric
} // namespace boost
//
// (3) Define suitable range checkers
//
// By default, if a UDT is involved in a conversion, internal range checking is disabled.
// This is so because a UDT type can have any sort of range, even unbounded, thus
// the library doesn't attempt to automatically figure out the appropriate range checking logic.
// (as it does when builtin types are involved)
// However, this situation is a bit unsufficient in practice, specially from doing narrowing (subranged)
// conversions from UDTs.
// The library provides a rudimentary hook to help this out: The user can plug in his own
// range checker to the converter instance.
//
// This test shows how to define and use a custom range checker.
//
namespace MyUDT {
//
// The following are metaprogramming tools to allow us the implement the
// MyCustomRangeChecker generically, for either builtin or UDT types.
//
// get_builtin_type<N>::type extracts the built-in type of our UDT's
//
template<class N> struct get_builtin_type { typedef N type ; } ;
template<> struct get_builtin_type<MyInt> { typedef int type ; } ;
template<> struct get_builtin_type<MyFloat> { typedef double type ; } ;
// U extract_builtin ( T s ) returns 's' converted to the corresponding built-in type U.
//
template<class N>
struct extract_builtin
{
static N apply ( N n ) { return n ; }
} ;
template<>
struct extract_builtin<MyInt>
{
static int apply ( MyInt const& n ) { return n.to_builtin() ; }
} ;
template<>
struct extract_builtin<MyFloat>
{
static double apply ( MyFloat const& n ) { return n.to_builtin() ; }
} ;
template<class Traits>
struct MyCustomRangeChecker
{
typedef typename Traits::argument_type argument_type ;
// This custom range checker uses the fact that our 'fake' UDT are merely wrappers
// around builtin types; so it just forward the logic to the correspoding range
// checkers for the wrapped builtin types.
//
typedef typename Traits::source_type S ;
typedef typename Traits::target_type T ;
// NOTE: S and/or T can be either UDT or builtin types.
typedef typename get_builtin_type<S>::type builtinS ;
typedef typename get_builtin_type<T>::type builtinT ;
// NOTE: The internal range checker used by default is *built* when you instantiate
// a converter<> with a given Traits according to the properties of the involved types.
// Currently, there is no way to instantiate this range checker as a separate class.
// However, you can see it as part of the interface of the converter
// (since the converter inherits from it)
// Therefore, here we instantiate a converter corresponding to the builtin types to access
// their associated builtin range checker.
//
typedef boost::numeric::converter<builtinT,builtinS> InternalConverter ;
static range_check_result out_of_range ( argument_type s )
{
return InternalConverter::out_of_range( extract_builtin<S>::apply(s) );
}
static void validate_range ( argument_type s )
{
return InternalConverter::validate_range( extract_builtin<S>::apply(s) );
}
} ;
} // namespace MyUDT
//
// Test here
//
void test_udt_conversions_with_defaults()
{
cout << "Testing UDT conversion with default policies\n" ;
// MyInt <--> int
int mibv = rand();
MyInt miv(mibv);
TEST_SUCCEEDING_CONVERSION_DEF(MyInt,int,miv,mibv);
TEST_SUCCEEDING_CONVERSION_DEF(int,MyInt,mibv,miv);
// MyFloat <--> double
double mfbv = static_cast<double>(rand()) / 3.0 ;
MyFloat mfv (mfbv);
TEST_SUCCEEDING_CONVERSION_DEF(MyFloat,double,mfv,mfbv);
TEST_SUCCEEDING_CONVERSION_DEF(double,MyFloat,mfbv,mfv);
// MyInt <--> MyFloat
MyInt miv2 ( static_cast<int>(mfbv) );
MyFloat miv2F ( static_cast<int>(mfbv) );
MyFloat mfv2 ( static_cast<double>(mibv) );
MyInt mfv2I ( static_cast<double>(mibv) );
TEST_SUCCEEDING_CONVERSION_DEF(MyFloat,MyInt,miv2F,miv2);
TEST_SUCCEEDING_CONVERSION_DEF(MyInt,MyFloat,mfv2I,mfv2);
}
template<class T, class S>
struct GenerateCustomConverter
{
typedef conversion_traits<T,S> Traits;
typedef def_overflow_handler OverflowHandler ;
typedef Trunc<S> Float2IntRounder ;
typedef raw_converter<Traits> RawConverter ;
typedef MyCustomRangeChecker<Traits> RangeChecker ;
typedef converter<T,S,Traits,OverflowHandler,Float2IntRounder,RawConverter,RangeChecker> type ;
} ;
void test_udt_conversions_with_custom_range_checking()
{
cout << "Testing UDT conversions with custom range checker\n" ;
int mibv = rand();
MyFloat mfv ( static_cast<double>(mibv) );
typedef GenerateCustomConverter<MyFloat,int>::type int_to_MyFloat_Conv ;
TEST_SUCCEEDING_CONVERSION( int_to_MyFloat_Conv, MyFloat, int, mfv, mibv );
int mibv2 = rand();
MyInt miv (mibv2);
MyFloat mfv2 ( static_cast<double>(mibv2) );
typedef GenerateCustomConverter<MyFloat,MyInt>::type MyInt_to_MyFloat_Conv ;
TEST_SUCCEEDING_CONVERSION( MyInt_to_MyFloat_Conv, MyFloat, MyInt, mfv2, miv );
double mfbv = bounds<double>::highest();
typedef GenerateCustomConverter<MyInt,double>::type double_to_MyInt_Conv ;
TEST_POS_OVERFLOW_CONVERSION( double_to_MyInt_Conv, MyInt, double, mfbv );
MyFloat mfv3 ( bounds<double>::lowest() ) ;
typedef GenerateCustomConverter<int,MyFloat>::type MyFloat_to_int_Conv ;
TEST_NEG_OVERFLOW_CONVERSION( MyFloat_to_int_Conv, int, MyFloat, mfv3 );
}
int test_main( int, char* [] )
{
cout << setprecision( numeric_limits<long double>::digits10 ) ;
test_udt_conversions_with_defaults();
test_udt_conversions_with_custom_range_checking();
return 0;
}
|