1 // boost cast.hpp header file ----------------------------------------------//
3 // (C) Copyright boost.org 1999. Permission to copy, use, modify, sell
4 // and distribute this software is granted provided this copyright
5 // notice appears in all copies. This software is provided "as is" without
6 // express or implied warranty, and with no claim as to its suitability for
9 // See http://www.boost.org/libs/conversion for Documentation.
12 // 02 Apr 01 Removed BOOST_NO_LIMITS workarounds and included
13 // <boost/limits.hpp> instead (the workaround did not
14 // actually compile when BOOST_NO_LIMITS was defined in
15 // any case, so we loose nothing). (John Maddock)
16 // 21 Jan 01 Undid a bug I introduced yesterday. numeric_cast<> never
17 // worked with stock GCC; trying to get it to do that broke
19 // 20 Jan 01 Moved BOOST_NO_LIMITS_COMPILE_TIME_CONSTANTS to config.hpp.
20 // Removed unused BOOST_EXPLICIT_TARGET macro. Moved
21 // boost::detail::type to boost/type.hpp. Made it compile with
22 // stock gcc again (Dave Abrahams)
23 // 29 Nov 00 Remove nested namespace cast, cleanup spacing before Formal
24 // Review (Beman Dawes)
25 // 19 Oct 00 Fix numeric_cast for floating-point types (Dave Abrahams)
26 // 15 Jul 00 Suppress numeric_cast warnings for GCC, Borland and MSVC
28 // 30 Jun 00 More MSVC6 wordarounds. See comments below. (Dave Abrahams)
29 // 28 Jun 00 Removed implicit_cast<>. See comment below. (Beman Dawes)
30 // 27 Jun 00 More MSVC6 workarounds
31 // 15 Jun 00 Add workarounds for MSVC6
32 // 2 Feb 00 Remove bad_numeric_cast ";" syntax error (Doncho Angelov)
33 // 26 Jan 00 Add missing throw() to bad_numeric_cast::what(0 (Adam Levar)
34 // 29 Dec 99 Change using declarations so usages in other namespaces work
35 // correctly (Dave Abrahams)
36 // 23 Sep 99 Change polymorphic_downcast assert to also detect M.I. errors
37 // as suggested Darin Adler and improved by Valentin Bonnard.
38 // 2 Sep 99 Remove controversial asserts, simplify, rename.
39 // 30 Aug 99 Move to cast.hpp, replace value_cast with numeric_cast,
40 // place in nested namespace.
41 // 3 Aug 99 Initial version
43 #ifndef BOOST_CAST_HPP
44 #define BOOST_CAST_HPP
46 # include <boost/config.hpp>
49 # include <boost/type.hpp>
50 # include <boost/limits.hpp>
51 # include <boost/detail/select_type.hpp>
53 // It has been demonstrated numerous times that MSVC 6.0 fails silently at link
54 // time if you use a template function which has template parameters that don't
55 // appear in the function's argument list.
57 // TODO: Add this to config.hpp?
58 # if defined(BOOST_MSVC) && BOOST_MSVC <= 1200 // 1200 = VC6
59 # define BOOST_EXPLICIT_DEFAULT_TARGET , ::boost::type<Target>* = 0
61 # define BOOST_EXPLICIT_DEFAULT_TARGET
66 // See the documentation for descriptions of how to choose between
67 // static_cast<>, dynamic_cast<>, polymorphic_cast<> and polymorphic_downcast<>
69 // polymorphic_cast --------------------------------------------------------//
71 // Runtime checked polymorphic downcasts and crosscasts.
72 // Suggested in The C++ Programming Language, 3rd Ed, Bjarne Stroustrup,
73 // section 15.8 exercise 1, page 425.
75 template <class Target, class Source>
76 inline Target polymorphic_cast(Source* x BOOST_EXPLICIT_DEFAULT_TARGET)
78 Target tmp = dynamic_cast<Target>(x);
79 if ( tmp == 0 ) throw std::bad_cast();
83 // polymorphic_downcast ----------------------------------------------------//
85 // assert() checked polymorphic downcast. Crosscasts prohibited.
87 // WARNING: Because this cast uses assert(), it violates the One Definition
88 // Rule if NDEBUG is inconsistently defined across translation units.
90 // Contributed by Dave Abrahams
92 template <class Target, class Source>
93 inline Target polymorphic_downcast(Source* x BOOST_EXPLICIT_DEFAULT_TARGET)
95 assert( dynamic_cast<Target>(x) == x ); // detect logic error
96 return static_cast<Target>(x);
99 // implicit_cast -----------------------------------------------------------//
101 // Removed due to uncertain purpose. Use either numeric_cast (see below)
102 // or static_cast according to the need.
104 // numeric_cast and related exception --------------------------------------//
106 // Contributed by Kevlin Henney
108 // bad_numeric_cast --------------------------------------------------------//
110 // exception used to indicate runtime numeric_cast failure
111 class bad_numeric_cast : public std::bad_cast
114 // constructors, destructors and assignment operator defaulted
116 // function inlined for brevity and consistency with rest of library
117 virtual const char *what() const throw()
119 return "bad numeric cast: loss of range in numeric_cast";
123 // numeric_cast ------------------------------------------------------------//
125 #if !defined(BOOST_NO_LIMITS_COMPILE_TIME_CONSTANTS) || defined(BOOST_SGI_CPP_LIMITS)
130 struct signed_numeric_limits : std::numeric_limits<T>
132 static inline T min()
134 return std::numeric_limits<T>::min() >= 0
135 // unary minus causes integral promotion, thus the static_cast<>
136 ? static_cast<T>(-std::numeric_limits<T>::max())
137 : std::numeric_limits<T>::min();
141 // Move to namespace boost in utility.hpp?
142 template <class T, bool specialized>
143 struct fixed_numeric_limits_base
144 : public if_true< std::numeric_limits<T>::is_signed >
145 ::BOOST_NESTED_TEMPLATE then< signed_numeric_limits<T>,
146 std::numeric_limits<T>
151 struct fixed_numeric_limits
152 : fixed_numeric_limits_base<T,(std::numeric_limits<T>::is_specialized)>
155 # ifdef BOOST_HAS_LONG_LONG
156 // cover implementations which supply no specialization for long
157 // long / unsigned long long. Not intended to be full
158 // numeric_limits replacements, but good enough for numeric_cast<>
160 struct fixed_numeric_limits_base<long long, false>
162 BOOST_STATIC_CONSTANT(bool, is_specialized = true);
163 BOOST_STATIC_CONSTANT(bool, is_signed = true);
164 static long long max()
169 return 9223372036854775807LL; // hope this is portable
173 static long long min()
178 return -9223372036854775808LL; // hope this is portable
184 struct fixed_numeric_limits_base<unsigned long long, false>
186 BOOST_STATIC_CONSTANT(bool, is_specialized = true);
187 BOOST_STATIC_CONSTANT(bool, is_signed = false);
188 static unsigned long long max()
190 # ifdef ULONGLONG_MAX
191 return ULONGLONG_MAX;
193 return 0xffffffffffffffffULL; // hope this is portable
197 static unsigned long long min() { return 0; }
200 } // namespace detail
202 // less_than_type_min -
203 // x_is_signed should be numeric_limits<X>::is_signed
204 // y_is_signed should be numeric_limits<Y>::is_signed
205 // y_min should be numeric_limits<Y>::min()
207 // check(x, y_min) returns true iff x < y_min without invoking comparisons
208 // between signed and unsigned values.
210 // "poor man's partial specialization" is in use here.
211 template <bool x_is_signed, bool y_is_signed>
212 struct less_than_type_min
214 template <class X, class Y>
215 static bool check(X x, Y y_min)
216 { return x < y_min; }
220 struct less_than_type_min<false, true>
222 template <class X, class Y>
223 static bool check(X, Y)
228 struct less_than_type_min<true, false>
230 template <class X, class Y>
231 static bool check(X x, Y)
235 // greater_than_type_max -
236 // same_sign should be:
237 // numeric_limits<X>::is_signed == numeric_limits<Y>::is_signed
238 // y_max should be numeric_limits<Y>::max()
240 // check(x, y_max) returns true iff x > y_max without invoking comparisons
241 // between signed and unsigned values.
243 // "poor man's partial specialization" is in use here.
244 template <bool same_sign, bool x_is_signed>
245 struct greater_than_type_max;
248 struct greater_than_type_max<true, true>
250 template <class X, class Y>
251 static inline bool check(X x, Y y_max)
252 { return x > y_max; }
256 struct greater_than_type_max<false, true>
258 // What does the standard say about this? I think it's right, and it
259 // will work with every compiler I know of.
260 template <class X, class Y>
261 static inline bool check(X x, Y)
262 { return x >= 0 && static_cast<X>(static_cast<Y>(x)) != x; }
264 # if defined(BOOST_MSVC) && BOOST_MSVC <= 1200
265 // MSVC6 can't static_cast unsigned __int64 -> floating types
266 # define BOOST_UINT64_CAST(src_type) \
267 static inline bool check(src_type x, unsigned __int64) \
269 if (x < 0) return false; \
270 unsigned __int64 y = static_cast<unsigned __int64>(x); \
271 bool odd = y & 0x1; \
272 __int64 div2 = static_cast<__int64>(y >> 1); \
273 return ((static_cast<src_type>(div2) * 2.0) + odd) != x; \
276 BOOST_UINT64_CAST(long double);
277 BOOST_UINT64_CAST(double);
278 BOOST_UINT64_CAST(float);
279 # undef BOOST_UINT64_CAST
284 struct greater_than_type_max<true, false>
286 template <class X, class Y>
287 static inline bool check(X x, Y y_max)
288 { return x > y_max; }
292 struct greater_than_type_max<false, false>
294 // What does the standard say about this? I think it's right, and it
295 // will work with every compiler I know of.
296 template <class X, class Y>
297 static inline bool check(X x, Y)
298 { return static_cast<X>(static_cast<Y>(x)) != x; }
301 #else // use #pragma hacks if available
306 # pragma warning(push)
307 # pragma warning(disable : 4018)
308 # pragma warning(disable : 4146)
309 #elif defined(__BORLANDC__)
310 # pragma option push -w-8041
313 // Move to namespace boost in utility.hpp?
315 struct fixed_numeric_limits : public std::numeric_limits<T>
317 static inline T min()
319 return std::numeric_limits<T>::is_signed && std::numeric_limits<T>::min() >= 0
320 ? T(-std::numeric_limits<T>::max()) : std::numeric_limits<T>::min();
325 # pragma warning(pop)
326 #elif defined(__BORLANDC__)
329 } // namespace detail
333 template<typename Target, typename Source>
334 inline Target numeric_cast(Source arg BOOST_EXPLICIT_DEFAULT_TARGET)
336 // typedefs abbreviating respective trait classes
337 typedef detail::fixed_numeric_limits<Source> arg_traits;
338 typedef detail::fixed_numeric_limits<Target> result_traits;
340 #if defined(BOOST_STRICT_CONFIG) \
341 || (!defined(__HP_aCC) || __HP_aCC > 33900) \
342 && (!defined(BOOST_NO_LIMITS_COMPILE_TIME_CONSTANTS) \
343 || defined(BOOST_SGI_CPP_LIMITS))
344 // typedefs that act as compile time assertions
345 // (to be replaced by boost compile time assertions
346 // as and when they become available and are stable)
347 typedef bool argument_must_be_numeric[arg_traits::is_specialized];
348 typedef bool result_must_be_numeric[result_traits::is_specialized];
350 const bool arg_is_signed = arg_traits::is_signed;
351 const bool result_is_signed = result_traits::is_signed;
352 const bool same_sign = arg_is_signed == result_is_signed;
354 if (less_than_type_min<arg_is_signed, result_is_signed>::check(arg, result_traits::min())
355 || greater_than_type_max<same_sign, arg_is_signed>::check(arg, result_traits::max())
358 #else // We need to use #pragma hacks if available
361 # pragma warning(push)
362 # pragma warning(disable : 4018)
363 #elif defined(__BORLANDC__)
364 #pragma option push -w-8012
366 if ((arg < 0 && !result_traits::is_signed) // loss of negative range
367 || (arg_traits::is_signed && arg < result_traits::min()) // underflow
368 || arg > result_traits::max()) // overflow
370 # pragma warning(pop)
371 #elif defined(__BORLANDC__)
376 throw bad_numeric_cast();
378 return static_cast<Target>(arg);
381 # undef BOOST_EXPLICIT_DEFAULT_TARGET
385 #endif // BOOST_CAST_HPP