]>
Commit | Line | Data |
---|---|---|
7c673cae FG |
1 | [/ |
2 | Boost.Optional | |
3 | ||
4 | Copyright (c) 2003-2007 Fernando Luis Cacciola Carballal | |
5 | ||
6 | Distributed under the Boost Software License, Version 1.0. | |
7 | (See accompanying file LICENSE_1_0.txt or copy at | |
8 | http://www.boost.org/LICENSE_1_0.txt) | |
9 | ] | |
10 | ||
11 | [section Type Requirements and User-defined-types support] | |
12 | ||
13 | [section Type Requirements] | |
14 | ||
15 | Both arithmetic (built-in) and user-defined numeric types require proper | |
16 | specialization of `std::numeric_limits<>` (that is, with (in-class) integral | |
17 | constants). | |
18 | ||
19 | The library uses `std::numeric_limits<T>::is_specialized` to detect whether | |
20 | the type is builtin or user defined, and `std::numeric_limits<T>::is_integer`, | |
21 | `std::numeric_limits<T>::is_signed` to detect whether the type is integer | |
22 | or floating point; and whether it is signed/unsigned. | |
23 | ||
24 | The default `Float2IntRounder` policies uses unqualified calls to functions | |
25 | `floor()` and `ceil()`; but the standard functions are introduced in scope | |
26 | by a using directive: | |
27 | ||
28 | using std::floor ; return floor(s); | |
29 | ||
30 | Therefore, for builtin arithmetic types, the std functions will be used. | |
31 | User defined types should provide overloaded versions of these functions in | |
32 | order to use the default rounder policies. If these overloads are defined | |
33 | within a user namespace argument dependent lookup (ADL) should find them, | |
34 | but if your compiler has a weak ADL you might need to put these functions | |
35 | some place else or write your own rounder policy. | |
36 | ||
37 | The default `Trunc<>` rounder policy needs to determine if the source value | |
38 | is positive or not, and for this it evaluates the expression | |
39 | `s < static_cast<S>(0)`. Therefore, user defined types require a visible | |
40 | `operator<` in order to use the `Trunc<>` policy (the default). | |
41 | ||
42 | ||
43 | [endsect] | |
44 | ||
45 | [section UDT's special semantics] | |
46 | ||
47 | [heading Conversion Traits] | |
48 | ||
49 | If a User Defined Type is involved in a conversion, it is ['assumed] that | |
50 | the UDT has [link boost_numericconversion.definitions.range_and_precision wider range] | |
51 | than any built-in type, and consequently the values | |
52 | of some `converter_traits<>` members are hardwired regardless of the reality. | |
53 | The following table summarizes this: | |
54 | ||
55 | * `Target=`['UDT] and `Source=`['built-in] | |
56 | * `subranged=false` | |
57 | * `supertype=Target` | |
58 | * `subtype=Source` | |
59 | * `Target=`['built-in] and `Source=`['UDT] | |
60 | * `subranged=true` | |
61 | * `supertype=Source` | |
62 | * `subtype=Target` | |
63 | ||
64 | * `Target=`['UDT] and `Source=`['UDT] | |
65 | * `subranged=false` | |
66 | * `supertype=Target` | |
67 | * `subtype=Source` | |
68 | ||
69 | ||
70 | The `Traits` member `udt_mixture` can be used to detect whether a UDT is involved | |
71 | and to infer the validity of the other members as shown above. | |
72 | ||
73 | [heading Range Checking] | |
74 | ||
75 | Because User Defined Numeric Types might have peculiar ranges (such as an | |
76 | unbounded range), this library does not attempt to supply a meaningful range | |
77 | checking logic when UDTs are involved in a conversion. Therefore, if either | |
78 | Target or Source are not built-in types, the bundled range checking of the | |
79 | `converter<>` function object is automatically disabled. However, it is possible | |
80 | to supply a user-defined range-checker. See | |
81 | [link boost_numericconversion.type_requirements_and_user_defined_types_support.special_policies Special Policies] | |
82 | ||
83 | [endsect] | |
84 | ||
85 | [section Special Policies] | |
86 | ||
87 | There are two components of the `converter<>` class that might require special | |
88 | behavior if User Defined Numeric Types are involved: the Range Checking and the | |
89 | Raw Conversion. | |
90 | ||
91 | When both Target and Source are built-in types, the converter class uses an internal | |
92 | range checking logic which is optimized and customized for the combined properties | |
93 | of the types. | |
94 | ||
95 | However, this internal logic is disabled when either type is User Defined. | |
96 | In this case, the user can specify an ['external] range checking policy which will be | |
97 | used in place of the internal code. See | |
98 | [link boost_numericconversion.type_requirements_and_user_defined_types_support.udts_with_numeric_cast numeric_cast_traits] | |
99 | for details on using UDTs with `numeric_cast`. | |
100 | ||
101 | The converter class performs the actual conversion using a Raw Converter policy. | |
102 | The default raw converter simply performs a `static_cast<Target>(source)`. | |
103 | ||
104 | However, if the a UDT is involved, the `static_cast` might not work. In this case, | |
105 | the user can implement and pass a different raw converter policy. | |
106 | See [link boost_numericconversion.numeric_converter_policy_classes.policy_rawconverter RawConverter] policy for details. | |
107 | ||
108 | [endsect] | |
109 | ||
110 | [section UDTs with numeric_cast] | |
111 | ||
112 | In order to employ UDTs with `numeric_cast`, the user should define | |
113 | a `numeric_cast_traits` specialization on the UDT for each conversion. | |
114 | Here is an example of specializations for converting between the UDT | |
115 | and any other type: | |
116 | ||
117 | namespace boost { namespace numeric { | |
118 | template <typename Source> | |
119 | struct numeric_cast_traits<UDT, Source> | |
120 | { | |
121 | typedef conversion_traits<UDT, Source> conv_traits; | |
122 | ||
123 | //! The following are required: | |
124 | typedef YourOverflowHandlerPolicy overflow_policy; | |
125 | typedef YourRangeCheckerPolicy<conv_traits> range_checking_policy; | |
126 | typedef YourFloat2IntRounderPolicy<Source> rounding_policy; | |
127 | }; | |
128 | template <typename Target> | |
129 | struct numeric_cast_traits<Target, UDT> | |
130 | { | |
131 | typedef conversion_traits<Target, UDT> conv_traits; | |
132 | ||
133 | //! The following are required: | |
134 | typedef YourOverflowHandlerPolicy overflow_policy; | |
135 | typedef YourRangeCheckerPolicy<conv_traits> range_checking_policy; | |
136 | typedef YourFloat2IntRounderPolicy<UDT> rounding_policy; | |
137 | }; | |
138 | }}//namespace boost::numeric; | |
139 | ||
140 | These specializations are already defined with default values for the built-in | |
141 | numeric types. It is possible to disable the generation of specializations for | |
142 | built-in types by defining `BOOST_NUMERIC_CONVERSION_RELAX_BUILT_IN_CAST_TRAITS`. | |
143 | For details on defining custom policies see [link boost_numericconversion.numeric_converter_policy_classes Converter Policies]. | |
144 | ||
145 | Here is a full example of how to define a custom UDT for use with `numeric_cast`: | |
146 | ||
147 | //! Define a simple custom number | |
148 | struct Double | |
149 | : boost::ordered_field_operators | |
150 | < | |
151 | Double | |
152 | , boost::ordered_field_operators2< Double, long double | |
153 | , boost::ordered_field_operators2< Double, double | |
154 | , boost::ordered_field_operators2< Double, float | |
155 | , boost::ordered_field_operators2< Double, int | |
156 | , boost::ordered_field_operators2< Double, unsigned int | |
157 | , boost::ordered_field_operators2< Double, long | |
158 | , boost::ordered_field_operators2< Double, unsigned long | |
159 | , boost::ordered_field_operators2< Double, long long | |
160 | , boost::ordered_field_operators2< Double, unsigned long long | |
161 | , boost::ordered_field_operators2< Double, char | |
162 | , boost::ordered_field_operators2< Double, unsigned char | |
163 | , boost::ordered_field_operators2< Double, short | |
164 | , boost::ordered_field_operators2< Double, unsigned short | |
165 | > > > > > > > > > > > > > > | |
166 | { | |
167 | Double() | |
168 | : v(0) | |
169 | {} | |
170 | ||
171 | template <typename T> | |
172 | explicit Double( T v ) | |
173 | : v(static_cast<double>(v)) | |
174 | {} | |
175 | ||
176 | template <typename T> | |
177 | Double& operator= ( T t ) | |
178 | { | |
179 | v = static_cast<double>(t); | |
180 | return *this; | |
181 | } | |
182 | ||
183 | bool operator < ( const Double& rhs ) const | |
184 | { | |
185 | return v < rhs.v; | |
186 | } | |
187 | ||
188 | template <typename T> | |
189 | bool operator < ( T rhs ) const | |
190 | { | |
191 | return v < static_cast<double>(rhs); | |
192 | } | |
193 | ||
194 | bool operator > ( const Double& rhs ) const | |
195 | { | |
196 | return v > rhs.v; | |
197 | } | |
198 | ||
199 | template <typename T> | |
200 | bool operator > ( T rhs ) const | |
201 | { | |
202 | return v > static_cast<double>(rhs); | |
203 | } | |
204 | ||
205 | bool operator ==( const Double& rhs ) const | |
206 | { | |
207 | return v == rhs.v; | |
208 | } | |
209 | ||
210 | template <typename T> | |
211 | bool operator == ( T rhs ) const | |
212 | { | |
213 | return v == static_cast<double>(rhs); | |
214 | } | |
215 | ||
216 | bool operator !() const | |
217 | { | |
218 | return v == 0; | |
219 | } | |
220 | ||
221 | Double operator -() const | |
222 | { | |
223 | return Double(-v); | |
224 | } | |
225 | ||
226 | Double& operator +=( const Double& t ) | |
227 | { | |
228 | v += t.v; | |
229 | return *this; | |
230 | } | |
231 | ||
232 | template <typename T> | |
233 | Double& operator +=( T t ) | |
234 | { | |
235 | v += static_cast<double>(t); | |
236 | return *this; | |
237 | } | |
238 | ||
239 | Double& operator -=( const Double& t ) | |
240 | { | |
241 | v -= t.v; | |
242 | return *this; | |
243 | } | |
244 | ||
245 | template <typename T> | |
246 | Double& operator -=( T t ) | |
247 | { | |
248 | v -= static_cast<double>(t); | |
249 | return *this; | |
250 | } | |
251 | ||
252 | Double& operator *= ( const Double& factor ) | |
253 | { | |
254 | v *= factor.v; | |
255 | return *this; | |
256 | } | |
257 | ||
258 | template <typename T> | |
259 | Double& operator *=( T t ) | |
260 | { | |
261 | v *= static_cast<double>(t); | |
262 | return *this; | |
263 | } | |
264 | ||
265 | Double& operator /= (const Double& divisor) | |
266 | { | |
267 | v /= divisor.v; | |
268 | return *this; | |
269 | } | |
270 | ||
271 | template <typename T> | |
272 | Double& operator /=( T t ) | |
273 | { | |
274 | v /= static_cast<double>(t); | |
275 | return (*this); | |
276 | } | |
277 | ||
278 | double v; | |
279 | }; | |
280 | ||
281 | //! Define numeric_limits for the custom type. | |
282 | namespace std | |
283 | { | |
284 | template<> | |
285 | class numeric_limits<Double> : public numeric_limits<double> | |
286 | { | |
287 | public: | |
288 | ||
289 | //! Limit our Double to a range of +/- 100.0 | |
290 | static Double (min)() | |
291 | { | |
292 | return Double(1.e-2); | |
293 | } | |
294 | ||
295 | static Double (max)() | |
296 | { | |
297 | return Double(1.e2); | |
298 | } | |
299 | ||
300 | static Double epsilon() | |
301 | { | |
302 | return Double( std::numeric_limits<double>::epsilon() ); | |
303 | } | |
304 | }; | |
305 | } | |
306 | ||
307 | //! Define range checking and overflow policies. | |
308 | namespace custom | |
309 | { | |
310 | //! Define a custom range checker | |
311 | template<typename Traits, typename OverFlowHandler> | |
312 | struct range_checker | |
313 | { | |
314 | typedef typename Traits::argument_type argument_type ; | |
315 | typedef typename Traits::source_type S; | |
316 | typedef typename Traits::target_type T; | |
317 | ||
318 | //! Check range of integral types. | |
319 | static boost::numeric::range_check_result out_of_range( argument_type s ) | |
320 | { | |
321 | using namespace boost::numeric; | |
322 | if( s > bounds<T>::highest() ) | |
323 | return cPosOverflow; | |
324 | else if( s < bounds<T>::lowest() ) | |
325 | return cNegOverflow; | |
326 | else | |
327 | return cInRange; | |
328 | } | |
329 | ||
330 | static void validate_range ( argument_type s ) | |
331 | { | |
332 | BOOST_STATIC_ASSERT( std::numeric_limits<T>::is_bounded ); | |
333 | OverFlowHandler()( out_of_range(s) ); | |
334 | } | |
335 | }; | |
336 | ||
337 | //! Overflow handler | |
338 | struct positive_overflow{}; | |
339 | struct negative_overflow{}; | |
340 | ||
341 | struct overflow_handler | |
342 | { | |
343 | void operator() ( boost::numeric::range_check_result r ) | |
344 | { | |
345 | using namespace boost::numeric; | |
346 | if( r == cNegOverflow ) | |
347 | throw negative_overflow() ; | |
348 | else if( r == cPosOverflow ) | |
349 | throw positive_overflow() ; | |
350 | } | |
351 | }; | |
352 | ||
353 | //! Define a rounding policy and specialize on the custom type. | |
354 | template<class S> | |
355 | struct Ceil : boost::numeric::Ceil<S>{}; | |
356 | ||
357 | template<> | |
358 | struct Ceil<Double> | |
359 | { | |
360 | typedef Double source_type; | |
361 | ||
362 | typedef Double const& argument_type; | |
363 | ||
364 | static source_type nearbyint ( argument_type s ) | |
365 | { | |
366 | #if !defined(BOOST_NO_STDC_NAMESPACE) | |
367 | using std::ceil ; | |
368 | #endif | |
369 | return Double( ceil(s.v) ); | |
370 | } | |
371 | ||
372 | typedef boost::mpl::integral_c< std::float_round_style, std::round_toward_infinity> round_style; | |
373 | }; | |
374 | ||
375 | //! Define a rounding policy and specialize on the custom type. | |
376 | template<class S> | |
377 | struct Trunc: boost::numeric::Trunc<S>{}; | |
378 | ||
379 | template<> | |
380 | struct Trunc<Double> | |
381 | { | |
382 | typedef Double source_type; | |
383 | ||
384 | typedef Double const& argument_type; | |
385 | ||
386 | static source_type nearbyint ( argument_type s ) | |
387 | { | |
388 | #if !defined(BOOST_NO_STDC_NAMESPACE) | |
389 | using std::floor; | |
390 | #endif | |
391 | return Double( floor(s.v) ); | |
392 | } | |
393 | ||
394 | typedef boost::mpl::integral_c< std::float_round_style, std::round_toward_zero> round_style; | |
395 | }; | |
396 | }//namespace custom; | |
397 | ||
398 | namespace boost { namespace numeric { | |
399 | ||
400 | //! Define the numeric_cast_traits specializations on the custom type. | |
401 | template <typename S> | |
402 | struct numeric_cast_traits<Double, S> | |
403 | { | |
404 | typedef custom::overflow_handler overflow_policy; | |
405 | typedef custom::range_checker | |
406 | < | |
407 | boost::numeric::conversion_traits<Double, S> | |
408 | , overflow_policy | |
409 | > range_checking_policy; | |
410 | typedef boost::numeric::Trunc<S> rounding_policy; | |
411 | }; | |
412 | ||
413 | template <typename T> | |
414 | struct numeric_cast_traits<T, Double> | |
415 | { | |
416 | typedef custom::overflow_handler overflow_policy; | |
417 | typedef custom::range_checker | |
418 | < | |
419 | boost::numeric::conversion_traits<T, Double> | |
420 | , overflow_policy | |
421 | > range_checking_policy; | |
422 | typedef custom::Trunc<Double> rounding_policy; | |
423 | }; | |
424 | ||
425 | //! Define the conversion from the custom type to built-in types and vice-versa. | |
426 | template<typename T> | |
427 | struct raw_converter< conversion_traits< T, Double > > | |
428 | { | |
429 | static T low_level_convert ( const Double& n ) | |
430 | { | |
431 | return static_cast<T>( n.v ); | |
432 | } | |
433 | }; | |
434 | ||
435 | template<typename S> | |
436 | struct raw_converter< conversion_traits< Double, S > > | |
437 | { | |
438 | static Double low_level_convert ( const S& n ) | |
439 | { | |
440 | return Double(n); | |
441 | } | |
442 | }; | |
443 | }}//namespace boost::numeric; | |
444 | ||
445 | [endsect] | |
446 | ||
447 | [endsect] | |
448 |