1 // boost cast.hpp header file ----------------------------------------------//
3 // (C) Copyright Kevlin Henney and Dave Abrahams 1999.
4 // Distributed under the Boost
5 // Software License, Version 1.0. (See accompanying file
6 // LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
8 // See http://www.boost.org/libs/conversion for Documentation.
11 // 02 Apr 01 Removed BOOST_NO_LIMITS workarounds and included
12 // <boost/limits.hpp> instead (the workaround did not
13 // actually compile when BOOST_NO_LIMITS was defined in
14 // any case, so we loose nothing). (John Maddock)
15 // 21 Jan 01 Undid a bug I introduced yesterday. numeric_cast<> never
16 // worked with stock GCC; trying to get it to do that broke
18 // 20 Jan 01 Moved BOOST_NO_LIMITS_COMPILE_TIME_CONSTANTS to config.hpp.
19 // Removed unused BOOST_EXPLICIT_TARGET macro. Moved
20 // boost::detail::type to boost/type.hpp. Made it compile with
21 // stock gcc again (Dave Abrahams)
22 // 29 Nov 00 Remove nested namespace cast, cleanup spacing before Formal
23 // Review (Beman Dawes)
24 // 19 Oct 00 Fix numeric_cast for floating-point types (Dave Abrahams)
25 // 15 Jul 00 Suppress numeric_cast warnings for GCC, Borland and MSVC
27 // 30 Jun 00 More MSVC6 wordarounds. See comments below. (Dave Abrahams)
28 // 28 Jun 00 Removed implicit_cast<>. See comment below. (Beman Dawes)
29 // 27 Jun 00 More MSVC6 workarounds
30 // 15 Jun 00 Add workarounds for MSVC6
31 // 2 Feb 00 Remove bad_numeric_cast ";" syntax error (Doncho Angelov)
32 // 26 Jan 00 Add missing throw() to bad_numeric_cast::what(0 (Adam Levar)
33 // 29 Dec 99 Change using declarations so usages in other namespaces work
34 // correctly (Dave Abrahams)
35 // 23 Sep 99 Change polymorphic_downcast assert to also detect M.I. errors
36 // as suggested Darin Adler and improved by Valentin Bonnard.
37 // 2 Sep 99 Remove controversial asserts, simplify, rename.
38 // 30 Aug 99 Move to cast.hpp, replace value_cast with numeric_cast,
39 // place in nested namespace.
40 // 3 Aug 99 Initial version
42 #ifndef BOOST_CAST_HPP
43 #define BOOST_CAST_HPP
45 # include <boost/config.hpp>
48 # include <boost/type.hpp>
49 # include <boost/limits.hpp>
50 # include <boost/detail/select_type.hpp>
52 // It has been demonstrated numerous times that MSVC 6.0 fails silently at link
53 // time if you use a template function which has template parameters that don't
54 // appear in the function's argument list.
56 // TODO: Add this to config.hpp?
57 # if defined(BOOST_MSVC) && BOOST_MSVC <= 1200 // 1200 = VC6
58 # define BOOST_EXPLICIT_DEFAULT_TARGET , ::boost::type<Target>* = 0
60 # define BOOST_EXPLICIT_DEFAULT_TARGET
65 // See the documentation for descriptions of how to choose between
66 // static_cast<>, dynamic_cast<>, polymorphic_cast<> and polymorphic_downcast<>
68 // polymorphic_cast --------------------------------------------------------//
70 // Runtime checked polymorphic downcasts and crosscasts.
71 // Suggested in The C++ Programming Language, 3rd Ed, Bjarne Stroustrup,
72 // section 15.8 exercise 1, page 425.
74 template <class Target, class Source>
75 inline Target polymorphic_cast(Source* x BOOST_EXPLICIT_DEFAULT_TARGET)
77 Target tmp = dynamic_cast<Target>(x);
78 if ( tmp == 0 ) throw std::bad_cast();
82 // polymorphic_downcast ----------------------------------------------------//
84 // assert() checked polymorphic downcast. Crosscasts prohibited.
86 // WARNING: Because this cast uses assert(), it violates the One Definition
87 // Rule if NDEBUG is inconsistently defined across translation units.
89 // Contributed by Dave Abrahams
91 template <class Target, class Source>
92 inline Target polymorphic_downcast(Source* x BOOST_EXPLICIT_DEFAULT_TARGET)
94 assert( dynamic_cast<Target>(x) == x ); // detect logic error
95 return static_cast<Target>(x);
98 // implicit_cast -----------------------------------------------------------//
100 // Removed due to uncertain purpose. Use either numeric_cast (see below)
101 // or static_cast according to the need.
103 // numeric_cast and related exception --------------------------------------//
105 // Contributed by Kevlin Henney
107 // bad_numeric_cast --------------------------------------------------------//
109 // exception used to indicate runtime numeric_cast failure
110 class bad_numeric_cast : public std::bad_cast
113 // constructors, destructors and assignment operator defaulted
115 // function inlined for brevity and consistency with rest of library
116 virtual const char *what() const throw()
118 return "bad numeric cast: loss of range in numeric_cast";
122 // numeric_cast ------------------------------------------------------------//
124 #if !defined(BOOST_NO_LIMITS_COMPILE_TIME_CONSTANTS) || defined(BOOST_SGI_CPP_LIMITS)
129 struct signed_numeric_limits : std::numeric_limits<T>
131 static inline T min BOOST_PREVENT_MACRO_SUBSTITUTION ()
133 return (std::numeric_limits<T>::min)() >= 0
134 // unary minus causes integral promotion, thus the static_cast<>
135 ? static_cast<T>(-(std::numeric_limits<T>::max)())
136 : (std::numeric_limits<T>::min)();
140 // Move to namespace boost in utility.hpp?
141 template <class T, bool specialized>
142 struct fixed_numeric_limits_base
143 : public if_true< std::numeric_limits<T>::is_signed >
144 ::BOOST_NESTED_TEMPLATE then< signed_numeric_limits<T>,
145 std::numeric_limits<T>
150 struct fixed_numeric_limits
151 : fixed_numeric_limits_base<T,(std::numeric_limits<T>::is_specialized)>
154 # ifdef BOOST_HAS_LONG_LONG
155 // cover implementations which supply no specialization for long
156 // long / unsigned long long. Not intended to be full
157 // numeric_limits replacements, but good enough for numeric_cast<>
159 struct fixed_numeric_limits_base< ::boost::long_long_type, false>
161 BOOST_STATIC_CONSTANT(bool, is_specialized = true);
162 BOOST_STATIC_CONSTANT(bool, is_signed = true);
163 static ::boost::long_long_type max BOOST_PREVENT_MACRO_SUBSTITUTION ()
168 return 9223372036854775807LL; // hope this is portable
172 static ::boost::long_long_type min BOOST_PREVENT_MACRO_SUBSTITUTION ()
177 return -( 9223372036854775807LL )-1; // hope this is portable
183 struct fixed_numeric_limits_base< ::boost::ulong_long_type, false>
185 BOOST_STATIC_CONSTANT(bool, is_specialized = true);
186 BOOST_STATIC_CONSTANT(bool, is_signed = false);
187 static ::boost::ulong_long_type max BOOST_PREVENT_MACRO_SUBSTITUTION ()
189 # ifdef ULONGLONG_MAX
190 return ULONGLONG_MAX;
192 return 0xffffffffffffffffULL; // hope this is portable
196 static ::boost::ulong_long_type min BOOST_PREVENT_MACRO_SUBSTITUTION () { return 0; }
199 } // namespace detail
201 // less_than_type_min -
202 // x_is_signed should be numeric_limits<X>::is_signed
203 // y_is_signed should be numeric_limits<Y>::is_signed
204 // y_min should be numeric_limits<Y>::min()
206 // check(x, y_min) returns true iff x < y_min without invoking comparisons
207 // between signed and unsigned values.
209 // "poor man's partial specialization" is in use here.
210 template <bool x_is_signed, bool y_is_signed>
211 struct less_than_type_min
213 template <class X, class Y>
214 static bool check(X x, Y y_min)
215 { return x < y_min; }
219 struct less_than_type_min<false, true>
221 template <class X, class Y>
222 static bool check(X, Y)
227 struct less_than_type_min<true, false>
229 template <class X, class Y>
230 static bool check(X x, Y)
234 // greater_than_type_max -
235 // same_sign should be:
236 // numeric_limits<X>::is_signed == numeric_limits<Y>::is_signed
237 // y_max should be numeric_limits<Y>::max()
239 // check(x, y_max) returns true iff x > y_max without invoking comparisons
240 // between signed and unsigned values.
242 // "poor man's partial specialization" is in use here.
243 template <bool same_sign, bool x_is_signed>
244 struct greater_than_type_max;
247 struct greater_than_type_max<true, true>
249 template <class X, class Y>
250 static inline bool check(X x, Y y_max)
251 { return x > y_max; }
255 struct greater_than_type_max<false, true>
257 // What does the standard say about this? I think it's right, and it
258 // will work with every compiler I know of.
259 template <class X, class Y>
260 static inline bool check(X x, Y)
261 { return x >= 0 && static_cast<X>(static_cast<Y>(x)) != x; }
263 # if defined(BOOST_MSVC) && BOOST_MSVC <= 1200
264 // MSVC6 can't static_cast unsigned __int64 -> floating types
265 # define BOOST_UINT64_CAST(src_type) \
266 static inline bool check(src_type x, unsigned __int64) \
268 if (x < 0) return false; \
269 unsigned __int64 y = static_cast<unsigned __int64>(x); \
270 bool odd = y & 0x1; \
271 __int64 div2 = static_cast<__int64>(y >> 1); \
272 return ((static_cast<src_type>(div2) * 2.0) + odd) != x; \
275 BOOST_UINT64_CAST(long double);
276 BOOST_UINT64_CAST(double);
277 BOOST_UINT64_CAST(float);
278 # undef BOOST_UINT64_CAST
283 struct greater_than_type_max<true, false>
285 template <class X, class Y>
286 static inline bool check(X x, Y y_max)
287 { return x > y_max; }
291 struct greater_than_type_max<false, false>
293 // What does the standard say about this? I think it's right, and it
294 // will work with every compiler I know of.
295 template <class X, class Y>
296 static inline bool check(X x, Y)
297 { return static_cast<X>(static_cast<Y>(x)) != x; }
300 #else // use #pragma hacks if available
305 # pragma warning(push)
306 # pragma warning(disable : 4018)
307 # pragma warning(disable : 4146)
308 #elif defined(__BORLANDC__)
309 # pragma option push -w-8041
312 // Move to namespace boost in utility.hpp?
314 struct fixed_numeric_limits : public std::numeric_limits<T>
316 static inline T min BOOST_PREVENT_MACRO_SUBSTITUTION ()
318 return std::numeric_limits<T>::is_signed && (std::numeric_limits<T>::min)() >= 0
319 ? T(-(std::numeric_limits<T>::max)()) : (std::numeric_limits<T>::min)();
324 # pragma warning(pop)
325 #elif defined(__BORLANDC__)
328 } // namespace detail
332 template<typename Target, typename Source>
333 inline Target numeric_cast(Source arg BOOST_EXPLICIT_DEFAULT_TARGET)
335 // typedefs abbreviating respective trait classes
336 typedef detail::fixed_numeric_limits<Source> arg_traits;
337 typedef detail::fixed_numeric_limits<Target> result_traits;
339 #if defined(BOOST_STRICT_CONFIG) \
340 || (!defined(__HP_aCC) || __HP_aCC > 33900) \
341 && (!defined(BOOST_NO_LIMITS_COMPILE_TIME_CONSTANTS) \
342 || defined(BOOST_SGI_CPP_LIMITS))
343 // typedefs that act as compile time assertions
344 // (to be replaced by boost compile time assertions
345 // as and when they become available and are stable)
346 typedef bool argument_must_be_numeric[arg_traits::is_specialized];
347 typedef bool result_must_be_numeric[result_traits::is_specialized];
349 const bool arg_is_signed = arg_traits::is_signed;
350 const bool result_is_signed = result_traits::is_signed;
351 const bool same_sign = arg_is_signed == result_is_signed;
353 if (less_than_type_min<arg_is_signed, result_is_signed>::check(arg, (result_traits::min)())
354 || greater_than_type_max<same_sign, arg_is_signed>::check(arg, (result_traits::max)())
357 #else // We need to use #pragma hacks if available
360 # pragma warning(push)
361 # pragma warning(disable : 4018)
362 #elif defined(__BORLANDC__)
363 #pragma option push -w-8012
365 if ((arg < 0 && !result_traits::is_signed) // loss of negative range
366 || (arg_traits::is_signed && arg < (result_traits::min)()) // underflow
367 || arg > (result_traits::max)()) // overflow
369 # pragma warning(pop)
370 #elif defined(__BORLANDC__)
375 throw bad_numeric_cast();
377 return static_cast<Target>(arg);
380 # undef BOOST_EXPLICIT_DEFAULT_TARGET
384 #endif // BOOST_CAST_HPP