| 1 | // (C) Copyright David Abrahams 2001. |
|---|
| 2 | // Distributed under the Boost Software License, Version 1.0. (See |
|---|
| 3 | // accompanying file LICENSE_1_0.txt or copy at |
|---|
| 4 | // http://www.boost.org/LICENSE_1_0.txt) |
|---|
| 5 | |
|---|
| 6 | // See http://www.boost.org for most recent version including documentation. |
|---|
| 7 | |
|---|
| 8 | // Revision History |
|---|
| 9 | // 1 Apr 2001 Fixes for ICL; use BOOST_STATIC_CONSTANT |
|---|
| 10 | // 11 Feb 2001 Fixes for Borland (David Abrahams) |
|---|
| 11 | // 23 Jan 2001 Added test for wchar_t (David Abrahams) |
|---|
| 12 | // 23 Jan 2001 Now statically selecting a test for signed numbers to avoid |
|---|
| 13 | // warnings with fancy compilers. Added commentary and |
|---|
| 14 | // additional dumping of traits data for tested types (David |
|---|
| 15 | // Abrahams). |
|---|
| 16 | // 21 Jan 2001 Initial version (David Abrahams) |
|---|
| 17 | |
|---|
| 18 | #include <boost/detail/numeric_traits.hpp> |
|---|
| 19 | #include <cassert> |
|---|
| 20 | #include <boost/type_traits.hpp> |
|---|
| 21 | #include <boost/static_assert.hpp> |
|---|
| 22 | #include <boost/cstdint.hpp> |
|---|
| 23 | #include <boost/utility.hpp> |
|---|
| 24 | #include <boost/lexical_cast.hpp> |
|---|
| 25 | #include <climits> |
|---|
| 26 | #include <typeinfo> |
|---|
| 27 | #include <iostream> |
|---|
| 28 | #include <string> |
|---|
| 29 | #ifndef BOOST_NO_LIMITS |
|---|
| 30 | # include <limits> |
|---|
| 31 | #endif |
|---|
| 32 | |
|---|
| 33 | // ================================================================================= |
|---|
| 34 | // template class complement_traits<Number> -- |
|---|
| 35 | // |
|---|
| 36 | // statically computes the max and min for 1s and 2s-complement binary |
|---|
| 37 | // numbers. This helps on platforms without <limits> support. It also shows |
|---|
| 38 | // an example of a recursive template that works with MSVC! |
|---|
| 39 | // |
|---|
| 40 | |
|---|
| 41 | template <unsigned size> struct complement; // forward |
|---|
| 42 | |
|---|
| 43 | // The template complement, below, does all the real work, using "poor man's |
|---|
| 44 | // partial specialization". We need complement_traits_aux<> so that MSVC doesn't |
|---|
| 45 | // complain about undefined min/max as we're trying to recursively define them. |
|---|
| 46 | template <class Number, unsigned size> |
|---|
| 47 | struct complement_traits_aux |
|---|
| 48 | { |
|---|
| 49 | BOOST_STATIC_CONSTANT(Number, max = complement<size>::template traits<Number>::max); |
|---|
| 50 | BOOST_STATIC_CONSTANT(Number, min = complement<size>::template traits<Number>::min); |
|---|
| 51 | }; |
|---|
| 52 | |
|---|
| 53 | template <unsigned size> |
|---|
| 54 | struct complement |
|---|
| 55 | { |
|---|
| 56 | template <class Number> |
|---|
| 57 | struct traits |
|---|
| 58 | { |
|---|
| 59 | private: |
|---|
| 60 | // indirection through complement_traits_aux necessary to keep MSVC happy |
|---|
| 61 | typedef complement_traits_aux<Number, size - 1> prev; |
|---|
| 62 | public: |
|---|
| 63 | #if defined(__GNUC__) && __GNUC__ == 4 && __GNUC_MINOR__ == 0 && __GNUC_PATCHLEVEL__ == 2 |
|---|
| 64 | // GCC 4.0.2 ICEs on these C-style casts |
|---|
| 65 | BOOST_STATIC_CONSTANT(Number, max = |
|---|
| 66 | Number((prev::max) << CHAR_BIT) |
|---|
| 67 | + Number(UCHAR_MAX)); |
|---|
| 68 | BOOST_STATIC_CONSTANT(Number, min = Number((prev::min) << CHAR_BIT)); |
|---|
| 69 | #else |
|---|
| 70 | BOOST_STATIC_CONSTANT(Number, max = |
|---|
| 71 | Number(Number(prev::max) << CHAR_BIT) |
|---|
| 72 | + Number(UCHAR_MAX)); |
|---|
| 73 | BOOST_STATIC_CONSTANT(Number, min = Number(Number(prev::min) << CHAR_BIT)); |
|---|
| 74 | #endif |
|---|
| 75 | |
|---|
| 76 | }; |
|---|
| 77 | }; |
|---|
| 78 | |
|---|
| 79 | // Template class complement_base<> -- defines values for min and max for |
|---|
| 80 | // complement<1>, at the deepest level of recursion. Uses "poor man's partial |
|---|
| 81 | // specialization" again. |
|---|
| 82 | template <bool is_signed> struct complement_base; |
|---|
| 83 | |
|---|
| 84 | template <> struct complement_base<false> |
|---|
| 85 | { |
|---|
| 86 | template <class Number> |
|---|
| 87 | struct values |
|---|
| 88 | { |
|---|
| 89 | BOOST_STATIC_CONSTANT(Number, min = 0); |
|---|
| 90 | BOOST_STATIC_CONSTANT(Number, max = UCHAR_MAX); |
|---|
| 91 | }; |
|---|
| 92 | }; |
|---|
| 93 | |
|---|
| 94 | template <> struct complement_base<true> |
|---|
| 95 | { |
|---|
| 96 | template <class Number> |
|---|
| 97 | struct values |
|---|
| 98 | { |
|---|
| 99 | BOOST_STATIC_CONSTANT(Number, min = SCHAR_MIN); |
|---|
| 100 | BOOST_STATIC_CONSTANT(Number, max = SCHAR_MAX); |
|---|
| 101 | }; |
|---|
| 102 | }; |
|---|
| 103 | |
|---|
| 104 | // Base specialization of complement, puts an end to the recursion. |
|---|
| 105 | template <> |
|---|
| 106 | struct complement<1> |
|---|
| 107 | { |
|---|
| 108 | template <class Number> |
|---|
| 109 | struct traits |
|---|
| 110 | { |
|---|
| 111 | BOOST_STATIC_CONSTANT(bool, is_signed = boost::detail::is_signed<Number>::value); |
|---|
| 112 | BOOST_STATIC_CONSTANT(Number, min = |
|---|
| 113 | complement_base<is_signed>::template values<Number>::min); |
|---|
| 114 | BOOST_STATIC_CONSTANT(Number, max = |
|---|
| 115 | complement_base<is_signed>::template values<Number>::max); |
|---|
| 116 | }; |
|---|
| 117 | }; |
|---|
| 118 | |
|---|
| 119 | // Now here's the "pretty" template you're intended to actually use. |
|---|
| 120 | // complement_traits<Number>::min, complement_traits<Number>::max are the |
|---|
| 121 | // minimum and maximum values of Number if Number is a built-in integer type. |
|---|
| 122 | template <class Number> |
|---|
| 123 | struct complement_traits |
|---|
| 124 | { |
|---|
| 125 | BOOST_STATIC_CONSTANT(Number, max = (complement_traits_aux<Number, sizeof(Number)>::max)); |
|---|
| 126 | BOOST_STATIC_CONSTANT(Number, min = (complement_traits_aux<Number, sizeof(Number)>::min)); |
|---|
| 127 | }; |
|---|
| 128 | |
|---|
| 129 | // ================================================================================= |
|---|
| 130 | |
|---|
| 131 | // Support for streaming various numeric types in exactly the format I want. I |
|---|
| 132 | // needed this in addition to all the assertions so that I could see exactly |
|---|
| 133 | // what was going on. |
|---|
| 134 | // |
|---|
| 135 | // Numbers go through a 2-stage conversion process (by default, though, no real |
|---|
| 136 | // conversion). |
|---|
| 137 | // |
|---|
| 138 | template <class T> struct stream_as { |
|---|
| 139 | typedef T t1; |
|---|
| 140 | typedef T t2; |
|---|
| 141 | }; |
|---|
| 142 | |
|---|
| 143 | // char types first get converted to unsigned char, then to unsigned. |
|---|
| 144 | template <> struct stream_as<char> { |
|---|
| 145 | typedef unsigned char t1; |
|---|
| 146 | typedef unsigned t2; |
|---|
| 147 | }; |
|---|
| 148 | template <> struct stream_as<unsigned char> { |
|---|
| 149 | typedef unsigned char t1; typedef unsigned t2; |
|---|
| 150 | }; |
|---|
| 151 | template <> struct stream_as<signed char> { |
|---|
| 152 | typedef unsigned char t1; typedef unsigned t2; |
|---|
| 153 | }; |
|---|
| 154 | |
|---|
| 155 | #if defined(BOOST_MSVC_STD_ITERATOR) // No intmax streaming built-in |
|---|
| 156 | |
|---|
| 157 | // With this library implementation, __int64 and __uint64 get streamed as strings |
|---|
| 158 | template <> struct stream_as<boost::uintmax_t> { |
|---|
| 159 | typedef std::string t1; |
|---|
| 160 | typedef std::string t2; |
|---|
| 161 | }; |
|---|
| 162 | |
|---|
| 163 | template <> struct stream_as<boost::intmax_t> { |
|---|
| 164 | typedef std::string t1; |
|---|
| 165 | typedef std::string t2; |
|---|
| 166 | }; |
|---|
| 167 | #endif |
|---|
| 168 | |
|---|
| 169 | // Standard promotion process for streaming |
|---|
| 170 | template <class T> struct promote |
|---|
| 171 | { |
|---|
| 172 | static typename stream_as<T>::t1 from(T x) { |
|---|
| 173 | typedef typename stream_as<T>::t1 t1; |
|---|
| 174 | return t1(x); |
|---|
| 175 | } |
|---|
| 176 | }; |
|---|
| 177 | |
|---|
| 178 | #if defined(BOOST_MSVC_STD_ITERATOR) // No intmax streaming built-in |
|---|
| 179 | |
|---|
| 180 | // On this platform, stream them as long/unsigned long if they fit. |
|---|
| 181 | // Otherwise, write a string. |
|---|
| 182 | template <> struct promote<boost::uintmax_t> { |
|---|
| 183 | std::string static from(const boost::uintmax_t x) { |
|---|
| 184 | if (x > ULONG_MAX) |
|---|
| 185 | return std::string("large unsigned value"); |
|---|
| 186 | else |
|---|
| 187 | return boost::lexical_cast<std::string>((unsigned long)x); |
|---|
| 188 | } |
|---|
| 189 | }; |
|---|
| 190 | template <> struct promote<boost::intmax_t> { |
|---|
| 191 | std::string static from(const boost::intmax_t x) { |
|---|
| 192 | if (x > boost::intmax_t(ULONG_MAX)) |
|---|
| 193 | return std::string("large positive signed value"); |
|---|
| 194 | else if (x >= 0) |
|---|
| 195 | return boost::lexical_cast<std::string>((unsigned long)x); |
|---|
| 196 | |
|---|
| 197 | if (x < boost::intmax_t(LONG_MIN)) |
|---|
| 198 | return std::string("large negative signed value"); |
|---|
| 199 | else |
|---|
| 200 | return boost::lexical_cast<std::string>((long)x); |
|---|
| 201 | } |
|---|
| 202 | }; |
|---|
| 203 | #endif |
|---|
| 204 | |
|---|
| 205 | // This is the function which converts types to the form I want to stream them in. |
|---|
| 206 | template <class T> |
|---|
| 207 | typename stream_as<T>::t2 stream_number(T x) |
|---|
| 208 | { |
|---|
| 209 | return promote<T>::from(x); |
|---|
| 210 | } |
|---|
| 211 | // ================================================================================= |
|---|
| 212 | |
|---|
| 213 | // |
|---|
| 214 | // Tests for built-in signed and unsigned types |
|---|
| 215 | // |
|---|
| 216 | |
|---|
| 217 | // Tag types for selecting tests |
|---|
| 218 | struct unsigned_tag {}; |
|---|
| 219 | struct signed_tag {}; |
|---|
| 220 | |
|---|
| 221 | // Tests for unsigned numbers. The extra default Number parameter works around |
|---|
| 222 | // an MSVC bug. |
|---|
| 223 | template <class Number> |
|---|
| 224 | void test_aux(unsigned_tag, Number*) |
|---|
| 225 | { |
|---|
| 226 | typedef typename boost::detail::numeric_traits<Number>::difference_type difference_type; |
|---|
| 227 | BOOST_STATIC_ASSERT(!boost::detail::is_signed<Number>::value); |
|---|
| 228 | BOOST_STATIC_ASSERT( |
|---|
| 229 | (sizeof(Number) < sizeof(boost::intmax_t)) |
|---|
| 230 | | (boost::is_same<difference_type, boost::intmax_t>::value)); |
|---|
| 231 | |
|---|
| 232 | #if defined(__GNUC__) && __GNUC__ == 4 && __GNUC_MINOR__ == 0 && __GNUC_PATCHLEVEL__ == 2 |
|---|
| 233 | // GCC 4.0.2 ICEs on this C-style cases |
|---|
| 234 | BOOST_STATIC_ASSERT((complement_traits<Number>::max) > Number(0)); |
|---|
| 235 | BOOST_STATIC_ASSERT((complement_traits<Number>::min) == Number(0)); |
|---|
| 236 | #else |
|---|
| 237 | // Force casting to Number here to work around the fact that it's an enum on MSVC |
|---|
| 238 | BOOST_STATIC_ASSERT(Number(complement_traits<Number>::max) > Number(0)); |
|---|
| 239 | BOOST_STATIC_ASSERT(Number(complement_traits<Number>::min) == Number(0)); |
|---|
| 240 | #endif |
|---|
| 241 | |
|---|
| 242 | const Number max = complement_traits<Number>::max; |
|---|
| 243 | const Number min = complement_traits<Number>::min; |
|---|
| 244 | |
|---|
| 245 | const Number test_max = (sizeof(Number) < sizeof(boost::intmax_t)) |
|---|
| 246 | ? max |
|---|
| 247 | : max / 2 - 1; |
|---|
| 248 | |
|---|
| 249 | std::cout << std::hex << "(unsigned) min = " << stream_number(min) << ", max = " |
|---|
| 250 | << stream_number(max) << "..." << std::flush; |
|---|
| 251 | std::cout << "difference_type = " << typeid(difference_type).name() << "..." |
|---|
| 252 | << std::flush; |
|---|
| 253 | |
|---|
| 254 | difference_type d1 = boost::detail::numeric_distance(Number(0), test_max); |
|---|
| 255 | difference_type d2 = boost::detail::numeric_distance(test_max, Number(0)); |
|---|
| 256 | |
|---|
| 257 | std::cout << "0->" << stream_number(test_max) << "==" << std::dec << stream_number(d1) << "; " |
|---|
| 258 | << std::hex << stream_number(test_max) << "->0==" << std::dec << stream_number(d2) << "..." << std::flush; |
|---|
| 259 | |
|---|
| 260 | assert(d1 == difference_type(test_max)); |
|---|
| 261 | assert(d2 == -difference_type(test_max)); |
|---|
| 262 | } |
|---|
| 263 | |
|---|
| 264 | // Tests for signed numbers. The extra default Number parameter works around an |
|---|
| 265 | // MSVC bug. |
|---|
| 266 | struct out_of_range_tag {}; |
|---|
| 267 | struct in_range_tag {}; |
|---|
| 268 | |
|---|
| 269 | // This test morsel gets executed for numbers whose difference will always be |
|---|
| 270 | // representable in intmax_t |
|---|
| 271 | template <class Number> |
|---|
| 272 | void signed_test(in_range_tag, Number*) |
|---|
| 273 | { |
|---|
| 274 | BOOST_STATIC_ASSERT(boost::detail::is_signed<Number>::value); |
|---|
| 275 | typedef typename boost::detail::numeric_traits<Number>::difference_type difference_type; |
|---|
| 276 | const Number max = complement_traits<Number>::max; |
|---|
| 277 | const Number min = complement_traits<Number>::min; |
|---|
| 278 | |
|---|
| 279 | difference_type d1 = boost::detail::numeric_distance(min, max); |
|---|
| 280 | difference_type d2 = boost::detail::numeric_distance(max, min); |
|---|
| 281 | |
|---|
| 282 | std::cout << stream_number(min) << "->" << stream_number(max) << "=="; |
|---|
| 283 | std::cout << std::dec << stream_number(d1) << "; "; |
|---|
| 284 | std::cout << std::hex << stream_number(max) << "->" << stream_number(min) |
|---|
| 285 | << "==" << std::dec << stream_number(d2) << "..." << std::flush; |
|---|
| 286 | assert(d1 == difference_type(max) - difference_type(min)); |
|---|
| 287 | assert(d2 == difference_type(min) - difference_type(max)); |
|---|
| 288 | } |
|---|
| 289 | |
|---|
| 290 | // This test morsel gets executed for numbers whose difference may exceed the |
|---|
| 291 | // capacity of intmax_t. |
|---|
| 292 | template <class Number> |
|---|
| 293 | void signed_test(out_of_range_tag, Number*) |
|---|
| 294 | { |
|---|
| 295 | BOOST_STATIC_ASSERT(boost::detail::is_signed<Number>::value); |
|---|
| 296 | typedef typename boost::detail::numeric_traits<Number>::difference_type difference_type; |
|---|
| 297 | const Number max = complement_traits<Number>::max; |
|---|
| 298 | const Number min = complement_traits<Number>::min; |
|---|
| 299 | |
|---|
| 300 | difference_type min_distance = complement_traits<difference_type>::min; |
|---|
| 301 | difference_type max_distance = complement_traits<difference_type>::max; |
|---|
| 302 | |
|---|
| 303 | const Number n1 = Number(min + max_distance); |
|---|
| 304 | const Number n2 = Number(max + min_distance); |
|---|
| 305 | difference_type d1 = boost::detail::numeric_distance(min, n1); |
|---|
| 306 | difference_type d2 = boost::detail::numeric_distance(max, n2); |
|---|
| 307 | |
|---|
| 308 | std::cout << stream_number(min) << "->" << stream_number(n1) << "=="; |
|---|
| 309 | std::cout << std::dec << stream_number(d1) << "; "; |
|---|
| 310 | std::cout << std::hex << stream_number(max) << "->" << stream_number(n2) |
|---|
| 311 | << "==" << std::dec << stream_number(d2) << "..." << std::flush; |
|---|
| 312 | assert(d1 == max_distance); |
|---|
| 313 | assert(d2 == min_distance); |
|---|
| 314 | } |
|---|
| 315 | |
|---|
| 316 | template <class Number> |
|---|
| 317 | void test_aux(signed_tag, Number*) |
|---|
| 318 | { |
|---|
| 319 | typedef typename boost::detail::numeric_traits<Number>::difference_type difference_type; |
|---|
| 320 | BOOST_STATIC_ASSERT(boost::detail::is_signed<Number>::value); |
|---|
| 321 | BOOST_STATIC_ASSERT( |
|---|
| 322 | (sizeof(Number) < sizeof(boost::intmax_t)) |
|---|
| 323 | | (boost::is_same<difference_type, Number>::value)); |
|---|
| 324 | |
|---|
| 325 | #if defined(__GNUC__) && __GNUC__ == 4 && __GNUC_MINOR__ == 0 && __GNUC_PATCHLEVEL__ == 2 |
|---|
| 326 | // GCC 4.0.2 ICEs on this cast |
|---|
| 327 | BOOST_STATIC_ASSERT((complement_traits<Number>::max) > Number(0)); |
|---|
| 328 | BOOST_STATIC_ASSERT((complement_traits<Number>::min) < Number(0)); |
|---|
| 329 | #else |
|---|
| 330 | // Force casting to Number here to work around the fact that it's an enum on MSVC |
|---|
| 331 | BOOST_STATIC_ASSERT(Number(complement_traits<Number>::max) > Number(0)); |
|---|
| 332 | BOOST_STATIC_ASSERT(Number(complement_traits<Number>::min) < Number(0)); |
|---|
| 333 | #endif |
|---|
| 334 | const Number max = complement_traits<Number>::max; |
|---|
| 335 | const Number min = complement_traits<Number>::min; |
|---|
| 336 | |
|---|
| 337 | std::cout << std::hex << "min = " << stream_number(min) << ", max = " |
|---|
| 338 | << stream_number(max) << "..." << std::flush; |
|---|
| 339 | std::cout << "difference_type = " << typeid(difference_type).name() << "..." |
|---|
| 340 | << std::flush; |
|---|
| 341 | |
|---|
| 342 | typedef typename boost::detail::if_true< |
|---|
| 343 | (sizeof(Number) < sizeof(boost::intmax_t))> |
|---|
| 344 | ::template then< |
|---|
| 345 | in_range_tag, |
|---|
| 346 | out_of_range_tag |
|---|
| 347 | >::type |
|---|
| 348 | range_tag; |
|---|
| 349 | signed_test<Number>(range_tag(), 0); |
|---|
| 350 | } |
|---|
| 351 | |
|---|
| 352 | |
|---|
| 353 | // Test for all numbers. The extra default Number parameter works around an MSVC |
|---|
| 354 | // bug. |
|---|
| 355 | template <class Number> |
|---|
| 356 | void test(Number* = 0) |
|---|
| 357 | { |
|---|
| 358 | std::cout << "testing " << typeid(Number).name() << ":\n" |
|---|
| 359 | #ifndef BOOST_NO_LIMITS_COMPILE_TIME_CONSTANTS |
|---|
| 360 | << "is_signed: " << (std::numeric_limits<Number>::is_signed ? "true\n" : "false\n") |
|---|
| 361 | << "is_bounded: " << (std::numeric_limits<Number>::is_bounded ? "true\n" : "false\n") |
|---|
| 362 | << "digits: " << std::numeric_limits<Number>::digits << "\n" |
|---|
| 363 | #endif |
|---|
| 364 | << "..." << std::flush; |
|---|
| 365 | |
|---|
| 366 | // factoring out difference_type for the assert below confused Borland :( |
|---|
| 367 | typedef boost::detail::is_signed< |
|---|
| 368 | #if !defined(BOOST_MSVC) || BOOST_MSVC > 1300 |
|---|
| 369 | typename |
|---|
| 370 | #endif |
|---|
| 371 | boost::detail::numeric_traits<Number>::difference_type |
|---|
| 372 | > is_signed; |
|---|
| 373 | BOOST_STATIC_ASSERT(is_signed::value); |
|---|
| 374 | |
|---|
| 375 | typedef typename boost::detail::if_true< |
|---|
| 376 | boost::detail::is_signed<Number>::value |
|---|
| 377 | >::template then<signed_tag, unsigned_tag>::type signedness; |
|---|
| 378 | |
|---|
| 379 | test_aux<Number>(signedness(), 0); |
|---|
| 380 | std::cout << "passed" << std::endl; |
|---|
| 381 | } |
|---|
| 382 | |
|---|
| 383 | int main() |
|---|
| 384 | { |
|---|
| 385 | test<char>(); |
|---|
| 386 | test<unsigned char>(); |
|---|
| 387 | test<signed char>(); |
|---|
| 388 | test<wchar_t>(); |
|---|
| 389 | test<short>(); |
|---|
| 390 | test<unsigned short>(); |
|---|
| 391 | test<int>(); |
|---|
| 392 | test<unsigned int>(); |
|---|
| 393 | test<long>(); |
|---|
| 394 | test<unsigned long>(); |
|---|
| 395 | #if defined(BOOST_HAS_LONG_LONG) && !defined(BOOST_NO_INTEGRAL_INT64_T) |
|---|
| 396 | test< ::boost::long_long_type>(); |
|---|
| 397 | test< ::boost::ulong_long_type>(); |
|---|
| 398 | #elif defined(BOOST_MSVC) |
|---|
| 399 | // The problem of not having compile-time static class constants other than |
|---|
| 400 | // enums prevents this from working, since values get truncated. |
|---|
| 401 | // test<boost::uintmax_t>(); |
|---|
| 402 | // test<boost::intmax_t>(); |
|---|
| 403 | #endif |
|---|
| 404 | return 0; |
|---|
| 405 | } |
|---|