udt_example_0.cpp 7.2 KB

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