udt_support_test.cpp 8.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309
  1. // (C) Copyright 2003, 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<iostream>
  9. #include<iomanip>
  10. #include<string>
  11. #include<typeinfo>
  12. #include<vector>
  13. #include<algorithm>
  14. #include "boost/numeric/conversion/converter.hpp"
  15. #ifdef __BORLANDC__
  16. #pragma hdrstop
  17. #endif
  18. #include "test_helpers.cpp"
  19. #include "test_helpers2.cpp"
  20. #include "test_helpers3.cpp"
  21. using namespace std ;
  22. using namespace boost ;
  23. using namespace numeric ;
  24. using namespace MyUDT ;
  25. //-------------------------------------------------------------------------
  26. // These are the typical steps that are required to install support for
  27. // conversions from/to UDT which need special treatment.
  28. //-------------------------------------------------------------------------
  29. //
  30. // (1) Instantiate specific convesions traits.
  31. // This step is only for convenience.
  32. // These traits instances are required in order to define the specializations
  33. // that follow (and which *are required* to make the library work with MyInt and MyFloat)
  34. //
  35. namespace MyUDT {
  36. typedef conversion_traits<double , MyFloat> MyFloat_to_double_Traits;
  37. typedef conversion_traits<int , MyFloat> MyFloat_to_int_Traits;
  38. typedef conversion_traits<MyInt , MyFloat> MyFloat_to_MyInt_Traits;
  39. typedef conversion_traits<int , MyInt > MyInt_to_int_Traits;
  40. typedef conversion_traits<MyFloat, MyInt > MyInt_to_MyFloat_Traits;
  41. typedef conversion_traits<MyInt , double > double_to_MyInt_Traits;
  42. } // namespace MyUDT
  43. //
  44. // (2) Define suitable raw converters.
  45. //
  46. // Our sample UDTs don't support implicit conversions.
  47. // Therefore, the default raw_converter<> doesn't work,
  48. // and we need to define our own.
  49. //
  50. // There are two ways of doing this:
  51. //
  52. // (a) One is to simply specialize boost::numeric::raw_converter<> directly.
  53. // This way, the default converter will work out of the box, which means, for instance,
  54. // that numeric_cast<> can be used with these UDTs.
  55. //
  56. // (b) Define a user class with the appropriate interface and supply it explicitely
  57. // as a policy to a converter instance.
  58. //
  59. // This test uses chice (a).
  60. //
  61. namespace boost {
  62. namespace numeric {
  63. template<>
  64. struct raw_converter<MyUDT::MyFloat_to_double_Traits>
  65. {
  66. static double low_level_convert ( MyUDT::MyFloat const& s )
  67. { return s.to_builtin() ; }
  68. } ;
  69. template<>
  70. struct raw_converter<MyUDT::MyFloat_to_int_Traits>
  71. {
  72. static int low_level_convert ( MyUDT::MyFloat const& s )
  73. { return static_cast<int>( s.to_builtin() ) ; }
  74. } ;
  75. template<>
  76. struct raw_converter<MyUDT::MyFloat_to_MyInt_Traits>
  77. {
  78. static MyUDT::MyInt low_level_convert ( MyUDT::MyFloat const& s )
  79. { return MyUDT::MyInt( static_cast<int>(s.to_builtin()) ) ; }
  80. } ;
  81. template<>
  82. struct raw_converter<MyUDT::MyInt_to_int_Traits>
  83. {
  84. static int low_level_convert ( MyUDT::MyInt const& s ) { return s.to_builtin() ; }
  85. } ;
  86. template<>
  87. struct raw_converter<MyUDT::MyInt_to_MyFloat_Traits>
  88. {
  89. static MyUDT::MyFloat low_level_convert ( MyUDT::MyInt const& s )
  90. {
  91. return MyUDT::MyFloat( static_cast<double>(s.to_builtin()) ) ;
  92. }
  93. } ;
  94. template<>
  95. struct raw_converter<MyUDT::double_to_MyInt_Traits>
  96. {
  97. static MyUDT::MyInt low_level_convert ( double s )
  98. { return MyUDT::MyInt( static_cast<int>(s) ) ; }
  99. } ;
  100. } // namespace numeric
  101. } // namespace boost
  102. //
  103. // (3) Define suitable range checkers
  104. //
  105. // By default, if a UDT is involved in a conversion, internal range checking is disabled.
  106. // This is so because a UDT type can have any sort of range, even unbounded, thus
  107. // the library doesn't attempt to automatically figure out the appropriate range checking logic.
  108. // (as it does when builtin types are involved)
  109. // However, this situation is a bit unsufficient in practice, specially from doing narrowing (subranged)
  110. // conversions from UDTs.
  111. // The library provides a rudimentary hook to help this out: The user can plug in his own
  112. // range checker to the converter instance.
  113. //
  114. // This test shows how to define and use a custom range checker.
  115. //
  116. namespace MyUDT {
  117. //
  118. // The following are metaprogramming tools to allow us the implement the
  119. // MyCustomRangeChecker generically, for either builtin or UDT types.
  120. //
  121. // get_builtin_type<N>::type extracts the built-in type of our UDT's
  122. //
  123. template<class N> struct get_builtin_type { typedef N type ; } ;
  124. template<> struct get_builtin_type<MyInt> { typedef int type ; } ;
  125. template<> struct get_builtin_type<MyFloat> { typedef double type ; } ;
  126. // U extract_builtin ( T s ) returns 's' converted to the corresponding built-in type U.
  127. //
  128. template<class N>
  129. struct extract_builtin
  130. {
  131. static N apply ( N n ) { return n ; }
  132. } ;
  133. template<>
  134. struct extract_builtin<MyInt>
  135. {
  136. static int apply ( MyInt const& n ) { return n.to_builtin() ; }
  137. } ;
  138. template<>
  139. struct extract_builtin<MyFloat>
  140. {
  141. static double apply ( MyFloat const& n ) { return n.to_builtin() ; }
  142. } ;
  143. template<class Traits>
  144. struct MyCustomRangeChecker
  145. {
  146. typedef typename Traits::argument_type argument_type ;
  147. // This custom range checker uses the fact that our 'fake' UDT are merely wrappers
  148. // around builtin types; so it just forward the logic to the correspoding range
  149. // checkers for the wrapped builtin types.
  150. //
  151. typedef typename Traits::source_type S ;
  152. typedef typename Traits::target_type T ;
  153. // NOTE: S and/or T can be either UDT or builtin types.
  154. typedef typename get_builtin_type<S>::type builtinS ;
  155. typedef typename get_builtin_type<T>::type builtinT ;
  156. // NOTE: The internal range checker used by default is *built* when you instantiate
  157. // a converter<> with a given Traits according to the properties of the involved types.
  158. // Currently, there is no way to instantiate this range checker as a separate class.
  159. // However, you can see it as part of the interface of the converter
  160. // (since the converter inherits from it)
  161. // Therefore, here we instantiate a converter corresponding to the builtin types to access
  162. // their associated builtin range checker.
  163. //
  164. typedef boost::numeric::converter<builtinT,builtinS> InternalConverter ;
  165. static range_check_result out_of_range ( argument_type s )
  166. {
  167. return InternalConverter::out_of_range( extract_builtin<S>::apply(s) );
  168. }
  169. static void validate_range ( argument_type s )
  170. {
  171. return InternalConverter::validate_range( extract_builtin<S>::apply(s) );
  172. }
  173. } ;
  174. } // namespace MyUDT
  175. //
  176. // Test here
  177. //
  178. void test_udt_conversions_with_defaults()
  179. {
  180. cout << "Testing UDT conversion with default policies\n" ;
  181. // MyInt <--> int
  182. int mibv = rand();
  183. MyInt miv(mibv);
  184. TEST_SUCCEEDING_CONVERSION_DEF(MyInt,int,miv,mibv);
  185. TEST_SUCCEEDING_CONVERSION_DEF(int,MyInt,mibv,miv);
  186. // MyFloat <--> double
  187. double mfbv = static_cast<double>(rand()) / 3.0 ;
  188. MyFloat mfv (mfbv);
  189. TEST_SUCCEEDING_CONVERSION_DEF(MyFloat,double,mfv,mfbv);
  190. TEST_SUCCEEDING_CONVERSION_DEF(double,MyFloat,mfbv,mfv);
  191. // MyInt <--> MyFloat
  192. MyInt miv2 ( static_cast<int>(mfbv) );
  193. MyFloat miv2F ( static_cast<int>(mfbv) );
  194. MyFloat mfv2 ( static_cast<double>(mibv) );
  195. MyInt mfv2I ( static_cast<double>(mibv) );
  196. TEST_SUCCEEDING_CONVERSION_DEF(MyFloat,MyInt,miv2F,miv2);
  197. TEST_SUCCEEDING_CONVERSION_DEF(MyInt,MyFloat,mfv2I,mfv2);
  198. }
  199. template<class T, class S>
  200. struct GenerateCustomConverter
  201. {
  202. typedef conversion_traits<T,S> Traits;
  203. typedef def_overflow_handler OverflowHandler ;
  204. typedef Trunc<S> Float2IntRounder ;
  205. typedef raw_converter<Traits> RawConverter ;
  206. typedef MyCustomRangeChecker<Traits> RangeChecker ;
  207. typedef converter<T,S,Traits,OverflowHandler,Float2IntRounder,RawConverter,RangeChecker> type ;
  208. } ;
  209. void test_udt_conversions_with_custom_range_checking()
  210. {
  211. cout << "Testing UDT conversions with custom range checker\n" ;
  212. int mibv = rand();
  213. MyFloat mfv ( static_cast<double>(mibv) );
  214. typedef GenerateCustomConverter<MyFloat,int>::type int_to_MyFloat_Conv ;
  215. TEST_SUCCEEDING_CONVERSION( int_to_MyFloat_Conv, MyFloat, int, mfv, mibv );
  216. int mibv2 = rand();
  217. MyInt miv (mibv2);
  218. MyFloat mfv2 ( static_cast<double>(mibv2) );
  219. typedef GenerateCustomConverter<MyFloat,MyInt>::type MyInt_to_MyFloat_Conv ;
  220. TEST_SUCCEEDING_CONVERSION( MyInt_to_MyFloat_Conv, MyFloat, MyInt, mfv2, miv );
  221. double mfbv = bounds<double>::highest();
  222. typedef GenerateCustomConverter<MyInt,double>::type double_to_MyInt_Conv ;
  223. TEST_POS_OVERFLOW_CONVERSION( double_to_MyInt_Conv, MyInt, double, mfbv );
  224. MyFloat mfv3 ( bounds<double>::lowest() ) ;
  225. typedef GenerateCustomConverter<int,MyFloat>::type MyFloat_to_int_Conv ;
  226. TEST_NEG_OVERFLOW_CONVERSION( MyFloat_to_int_Conv, int, MyFloat, mfv3 );
  227. }
  228. int test_main( int, char* [] )
  229. {
  230. cout << setprecision( numeric_limits<long double>::digits10 ) ;
  231. test_udt_conversions_with_defaults();
  232. test_udt_conversions_with_custom_range_checking();
  233. return 0;
  234. }