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 for most recent version including 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>
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)
128 template <bool is_signed> struct numeric_min_select;
131 struct numeric_min_select<true>
134 struct limits : std::numeric_limits<T>
136 static inline T min()
137 # ifndef __GNUC__ // bug workaround courtesy Jens Maurer
139 return std::numeric_limits<T>::min() >= 0
140 // unary minus causes integral promotion, thus the static_cast<>
141 ? static_cast<T>(-std::numeric_limits<T>::max())
142 : std::numeric_limits<T>::min();
150 # ifdef __GNUC__ // bug workaround courtesy Jens Maurer
151 template<> template<class T>
152 inline T numeric_min_select<true>::limits<T>::min()
154 return std::numeric_limits<T>::min() >= 0
155 // unary minus causes integral promotion, thus the static_cast<>
156 ? static_cast<T>(-std::numeric_limits<T>::max())
157 : std::numeric_limits<T>::min();
162 struct numeric_min_select<false>
165 struct limits : std::numeric_limits<T> {};
168 // Move to namespace boost in utility.hpp?
170 struct fixed_numeric_limits
171 : public numeric_min_select<
172 std::numeric_limits<T>::is_signed
173 >::template limits<T>
176 } // namespace detail
178 // less_than_type_min -
179 // x_is_signed should be numeric_limits<X>::is_signed
180 // y_is_signed should be numeric_limits<Y>::is_signed
181 // y_min should be numeric_limits<Y>::min()
183 // check(x, y_min) returns true iff x < y_min without invoking comparisons
184 // between signed and unsigned values.
186 // "poor man's partial specialization" is in use here.
187 template <bool x_is_signed, bool y_is_signed>
188 struct less_than_type_min
190 template <class X, class Y>
191 static bool check(X x, Y y_min)
192 { return x < y_min; }
196 struct less_than_type_min<false, true>
198 template <class X, class Y>
199 static bool check(X, Y)
204 struct less_than_type_min<true, false>
206 template <class X, class Y>
207 static bool check(X x, Y)
211 // greater_than_type_max -
212 // same_sign should be:
213 // numeric_limits<X>::is_signed == numeric_limits<Y>::is_signed
214 // y_max should be numeric_limits<Y>::max()
216 // check(x, y_max) returns true iff x > y_max without invoking comparisons
217 // between signed and unsigned values.
219 // "poor man's partial specialization" is in use here.
220 template <bool same_sign, bool x_is_signed>
221 struct greater_than_type_max;
224 struct greater_than_type_max<true, true>
226 template <class X, class Y>
227 static inline bool check(X x, Y y_max)
228 { return x > y_max; }
232 struct greater_than_type_max<false, true>
234 // What does the standard say about this? I think it's right, and it
235 // will work with every compiler I know of.
236 template <class X, class Y>
237 static inline bool check(X x, Y)
238 { return x >= 0 && static_cast<X>(static_cast<Y>(x)) != x; }
242 struct greater_than_type_max<true, false>
244 template <class X, class Y>
245 static inline bool check(X x, Y y_max)
246 { return x > y_max; }
250 struct greater_than_type_max<false, false>
252 // What does the standard say about this? I think it's right, and it
253 // will work with every compiler I know of.
254 template <class X, class Y>
255 static inline bool check(X x, Y)
256 { return static_cast<X>(static_cast<Y>(x)) != x; }
259 #else // use #pragma hacks if available
264 # pragma warning(push)
265 # pragma warning(disable : 4018)
266 # pragma warning(disable : 4146)
267 #elif defined(__BORLANDC__)
268 # pragma option push -w-8041
271 // Move to namespace boost in utility.hpp?
273 struct fixed_numeric_limits : public std::numeric_limits<T>
275 static inline T min()
277 return std::numeric_limits<T>::is_signed && std::numeric_limits<T>::min() >= 0
278 ? T(-std::numeric_limits<T>::max()) : std::numeric_limits<T>::min();
283 # pragma warning(pop)
284 #elif defined(__BORLANDC__)
287 } // namespace detail
291 template<typename Target, typename Source>
292 inline Target numeric_cast(Source arg BOOST_EXPLICIT_DEFAULT_TARGET)
294 // typedefs abbreviating respective trait classes
295 typedef std::numeric_limits<Source> arg_traits;
296 typedef detail::fixed_numeric_limits<Target> result_traits;
298 #if !defined(BOOST_NO_LIMITS_COMPILE_TIME_CONSTANTS) || defined(BOOST_SGI_CPP_LIMITS)
299 // typedefs that act as compile time assertions
300 // (to be replaced by boost compile time assertions
301 // as and when they become available and are stable)
302 typedef bool argument_must_be_numeric[arg_traits::is_specialized];
303 typedef bool result_must_be_numeric[result_traits::is_specialized];
305 const bool arg_is_signed = arg_traits::is_signed;
306 const bool result_is_signed = result_traits::is_signed;
307 const bool same_sign = arg_is_signed == result_is_signed;
309 if (less_than_type_min<arg_is_signed, result_is_signed>::check(arg, result_traits::min())
310 || greater_than_type_max<same_sign, arg_is_signed>::check(arg, result_traits::max())
313 #else // We need to use #pragma hacks if available
316 # pragma warning(push)
317 # pragma warning(disable : 4018)
318 #elif defined(__BORLANDC__)
319 #pragma option push -w-8012
321 if ((arg < 0 && !result_traits::is_signed) // loss of negative range
322 || (arg_traits::is_signed && arg < result_traits::min()) // underflow
323 || arg > result_traits::max()) // overflow
325 # pragma warning(pop)
326 #elif defined(__BORLANDC__)
331 throw bad_numeric_cast();
333 return static_cast<Target>(arg);
336 # undef BOOST_EXPLICIT_DEFAULT_TARGET
340 #endif // BOOST_CAST_HPP