File: udt_support_test.cpp

package info (click to toggle)
boost1.35 1.35.0-5
  • links: PTS
  • area: main
  • in suites: lenny
  • size: 203,856 kB
  • ctags: 337,867
  • sloc: cpp: 938,683; xml: 56,847; ansic: 41,589; python: 18,999; sh: 11,566; makefile: 664; perl: 494; yacc: 456; asm: 353; csh: 6
file content (309 lines) | stat: -rw-r--r-- 9,049 bytes parent folder | download | duplicates (11)
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;
}