123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416 |
- // (C) Copyright David Abrahams 2001.
- // Distributed under the Boost Software License, Version 1.0. (See
- // accompanying file LICENSE_1_0.txt or copy at
- // http://www.boost.org/LICENSE_1_0.txt)
- // See http://www.boost.org for most recent version including documentation.
- // Revision History
- // 1 Apr 2001 Fixes for ICL; use BOOST_STATIC_CONSTANT
- // 11 Feb 2001 Fixes for Borland (David Abrahams)
- // 23 Jan 2001 Added test for wchar_t (David Abrahams)
- // 23 Jan 2001 Now statically selecting a test for signed numbers to avoid
- // warnings with fancy compilers. Added commentary and
- // additional dumping of traits data for tested types (David
- // Abrahams).
- // 21 Jan 2001 Initial version (David Abrahams)
- #include <boost/detail/numeric_traits.hpp>
- #include <cassert>
- #include <boost/type_traits/conditional.hpp>
- #include <boost/type_traits/is_signed.hpp>
- #include <boost/type_traits/is_same.hpp>
- #include <boost/static_assert.hpp>
- #include <boost/cstdint.hpp>
- #include <climits>
- #include <typeinfo>
- #include <iostream>
- #include <sstream>
- #include <string>
- #ifndef BOOST_NO_LIMITS
- #include <limits>
- #endif
- // =================================================================================
- // template class complement_traits<Number> --
- //
- // statically computes the max and min for 1s and 2s-complement binary
- // numbers. This helps on platforms without <limits> support. It also shows
- // an example of a recursive template that works with MSVC!
- //
- template <unsigned size> struct complement; // forward
- // The template complement, below, does all the real work, using "poor man's
- // partial specialization". We need complement_traits_aux<> so that MSVC doesn't
- // complain about undefined min/max as we're trying to recursively define them.
- template <class Number, unsigned size>
- struct complement_traits_aux
- {
- BOOST_STATIC_CONSTANT(Number, max = complement<size>::template traits<Number>::max);
- BOOST_STATIC_CONSTANT(Number, min = complement<size>::template traits<Number>::min);
- };
- template <unsigned size>
- struct complement
- {
- template <class Number>
- struct traits
- {
- private:
- // indirection through complement_traits_aux necessary to keep MSVC happy
- typedef complement_traits_aux<Number, size - 1> prev;
- public:
- #if defined(__GNUC__) && __GNUC__ == 4 && __GNUC_MINOR__ == 0 && __GNUC_PATCHLEVEL__ == 2
- // GCC 4.0.2 ICEs on these C-style casts
- BOOST_STATIC_CONSTANT(Number, max =
- Number((prev::max) << CHAR_BIT)
- + Number(UCHAR_MAX));
- BOOST_STATIC_CONSTANT(Number, min = Number((prev::min) << CHAR_BIT));
- #else
- // Avoid left shifting negative integers, use multiplication instead
- BOOST_STATIC_CONSTANT(Number, shift = 1u << CHAR_BIT);
- BOOST_STATIC_CONSTANT(Number, max =
- Number(Number(prev::max) * shift)
- + Number(UCHAR_MAX));
- BOOST_STATIC_CONSTANT(Number, min = Number(Number(prev::min) * shift));
- #endif
- };
- };
- // Template class complement_base<> -- defines values for min and max for
- // complement<1>, at the deepest level of recursion. Uses "poor man's partial
- // specialization" again.
- template <bool is_signed> struct complement_base;
- template <> struct complement_base<false>
- {
- template <class Number>
- struct values
- {
- BOOST_STATIC_CONSTANT(Number, min = 0);
- BOOST_STATIC_CONSTANT(Number, max = UCHAR_MAX);
- };
- };
- template <> struct complement_base<true>
- {
- template <class Number>
- struct values
- {
- BOOST_STATIC_CONSTANT(Number, min = SCHAR_MIN);
- BOOST_STATIC_CONSTANT(Number, max = SCHAR_MAX);
- };
- };
- // Base specialization of complement, puts an end to the recursion.
- template <>
- struct complement<1>
- {
- template <class Number>
- struct traits
- {
- BOOST_STATIC_CONSTANT(bool, is_signed = boost::is_signed<Number>::value);
- BOOST_STATIC_CONSTANT(Number, min =
- complement_base<is_signed>::template values<Number>::min);
- BOOST_STATIC_CONSTANT(Number, max =
- complement_base<is_signed>::template values<Number>::max);
- };
- };
- // Now here's the "pretty" template you're intended to actually use.
- // complement_traits<Number>::min, complement_traits<Number>::max are the
- // minimum and maximum values of Number if Number is a built-in integer type.
- template <class Number>
- struct complement_traits
- {
- BOOST_STATIC_CONSTANT(Number, max = (complement_traits_aux<Number, sizeof(Number)>::max));
- BOOST_STATIC_CONSTANT(Number, min = (complement_traits_aux<Number, sizeof(Number)>::min));
- };
- // =================================================================================
- // Support for streaming various numeric types in exactly the format I want. I
- // needed this in addition to all the assertions so that I could see exactly
- // what was going on.
- //
- // Numbers go through a 2-stage conversion process (by default, though, no real
- // conversion).
- //
- template <class T> struct stream_as {
- typedef T t1;
- typedef T t2;
- };
- // char types first get converted to unsigned char, then to unsigned.
- template <> struct stream_as<char> {
- typedef unsigned char t1;
- typedef unsigned t2;
- };
- template <> struct stream_as<unsigned char> {
- typedef unsigned char t1; typedef unsigned t2;
- };
- template <> struct stream_as<signed char> {
- typedef unsigned char t1; typedef unsigned t2;
- };
- #if defined(BOOST_MSVC_STD_ITERATOR) // No intmax streaming built-in
- // With this library implementation, __int64 and __uint64 get streamed as strings
- template <> struct stream_as<boost::uintmax_t> {
- typedef std::string t1;
- typedef std::string t2;
- };
- template <> struct stream_as<boost::intmax_t> {
- typedef std::string t1;
- typedef std::string t2;
- };
- #endif
- // Standard promotion process for streaming
- template <class T> struct promote
- {
- static typename stream_as<T>::t1 from(T x) {
- typedef typename stream_as<T>::t1 t1;
- return t1(x);
- }
- };
- #if defined(BOOST_MSVC_STD_ITERATOR) // No intmax streaming built-in
- // On this platform, stream them as long/unsigned long if they fit.
- // Otherwise, write a string.
- template <> struct promote<boost::uintmax_t> {
- std::string static from(const boost::uintmax_t x) {
- if (x > ULONG_MAX)
- return std::string("large unsigned value");
- else {
- std::ostringstream strm;
- strm << (unsigned long)x;
- return strm.str();
- }
- }
- };
- template <> struct promote<boost::intmax_t> {
- std::string static from(const boost::intmax_t x) {
- if (x > boost::intmax_t(ULONG_MAX))
- return std::string("large positive signed value");
- else if (x >= 0) {
- std::ostringstream strm;
- strm << (unsigned long)x;
- return strm.str();
- }
- if (x < boost::intmax_t(LONG_MIN))
- return std::string("large negative signed value");
- else {
- std::ostringstream strm;
- strm << (long)x;
- return strm.str();
- }
- }
- };
- #endif
- // This is the function which converts types to the form I want to stream them in.
- template <class T>
- typename stream_as<T>::t2 stream_number(T x)
- {
- return promote<T>::from(x);
- }
- // =================================================================================
- //
- // Tests for built-in signed and unsigned types
- //
- // Tag types for selecting tests
- struct unsigned_tag {};
- struct signed_tag {};
- // Tests for unsigned numbers. The extra default Number parameter works around
- // an MSVC bug.
- template <class Number>
- void test_aux(unsigned_tag, Number*)
- {
- typedef typename boost::detail::numeric_traits<Number>::difference_type difference_type;
- BOOST_STATIC_ASSERT(!boost::is_signed<Number>::value);
- BOOST_STATIC_ASSERT(
- (sizeof(Number) < sizeof(boost::intmax_t))
- | (boost::is_same<difference_type, boost::intmax_t>::value));
- #if defined(__GNUC__) && __GNUC__ == 4 && __GNUC_MINOR__ == 0 && __GNUC_PATCHLEVEL__ == 2
- // GCC 4.0.2 ICEs on this C-style cases
- BOOST_STATIC_ASSERT((complement_traits<Number>::max) > Number(0));
- BOOST_STATIC_ASSERT((complement_traits<Number>::min) == Number(0));
- #else
- // Force casting to Number here to work around the fact that it's an enum on MSVC
- BOOST_STATIC_ASSERT(Number(complement_traits<Number>::max) > Number(0));
- BOOST_STATIC_ASSERT(Number(complement_traits<Number>::min) == Number(0));
- #endif
- const Number max = complement_traits<Number>::max;
- const Number min = complement_traits<Number>::min;
- const Number test_max = (sizeof(Number) < sizeof(boost::intmax_t))
- ? max
- : max / 2 - 1;
- std::cout << std::hex << "(unsigned) min = " << stream_number(min) << ", max = "
- << stream_number(max) << "..." << std::flush;
- std::cout << "difference_type = " << typeid(difference_type).name() << "..."
- << std::flush;
- difference_type d1 = boost::detail::numeric_distance(Number(0), test_max);
- difference_type d2 = boost::detail::numeric_distance(test_max, Number(0));
- std::cout << "0->" << stream_number(test_max) << "==" << std::dec << stream_number(d1) << "; "
- << std::hex << stream_number(test_max) << "->0==" << std::dec << stream_number(d2) << "..." << std::flush;
- assert(d1 == difference_type(test_max));
- assert(d2 == -difference_type(test_max));
- }
- // Tests for signed numbers. The extra default Number parameter works around an
- // MSVC bug.
- struct out_of_range_tag {};
- struct in_range_tag {};
- // This test morsel gets executed for numbers whose difference will always be
- // representable in intmax_t
- template <class Number>
- void signed_test(in_range_tag, Number*)
- {
- BOOST_STATIC_ASSERT(boost::is_signed<Number>::value);
- typedef typename boost::detail::numeric_traits<Number>::difference_type difference_type;
- const Number max = complement_traits<Number>::max;
- const Number min = complement_traits<Number>::min;
- difference_type d1 = boost::detail::numeric_distance(min, max);
- difference_type d2 = boost::detail::numeric_distance(max, min);
- std::cout << stream_number(min) << "->" << stream_number(max) << "==";
- std::cout << std::dec << stream_number(d1) << "; ";
- std::cout << std::hex << stream_number(max) << "->" << stream_number(min)
- << "==" << std::dec << stream_number(d2) << "..." << std::flush;
- assert(d1 == difference_type(max) - difference_type(min));
- assert(d2 == difference_type(min) - difference_type(max));
- }
- // This test morsel gets executed for numbers whose difference may exceed the
- // capacity of intmax_t.
- template <class Number>
- void signed_test(out_of_range_tag, Number*)
- {
- BOOST_STATIC_ASSERT(boost::is_signed<Number>::value);
- typedef typename boost::detail::numeric_traits<Number>::difference_type difference_type;
- const Number max = complement_traits<Number>::max;
- const Number min = complement_traits<Number>::min;
- difference_type min_distance = complement_traits<difference_type>::min;
- difference_type max_distance = complement_traits<difference_type>::max;
- const Number n1 = Number(min + max_distance);
- const Number n2 = Number(max + min_distance);
- difference_type d1 = boost::detail::numeric_distance(min, n1);
- difference_type d2 = boost::detail::numeric_distance(max, n2);
- std::cout << stream_number(min) << "->" << stream_number(n1) << "==";
- std::cout << std::dec << stream_number(d1) << "; ";
- std::cout << std::hex << stream_number(max) << "->" << stream_number(n2)
- << "==" << std::dec << stream_number(d2) << "..." << std::flush;
- assert(d1 == max_distance);
- assert(d2 == min_distance);
- }
- template <class Number>
- void test_aux(signed_tag, Number*)
- {
- typedef typename boost::detail::numeric_traits<Number>::difference_type difference_type;
- BOOST_STATIC_ASSERT(boost::is_signed<Number>::value);
- BOOST_STATIC_ASSERT(
- (sizeof(Number) < sizeof(boost::intmax_t))
- | (boost::is_same<difference_type, Number>::value));
- #if defined(__GNUC__) && __GNUC__ == 4 && __GNUC_MINOR__ == 0 && __GNUC_PATCHLEVEL__ == 2
- // GCC 4.0.2 ICEs on this cast
- BOOST_STATIC_ASSERT((complement_traits<Number>::max) > Number(0));
- BOOST_STATIC_ASSERT((complement_traits<Number>::min) < Number(0));
- #else
- // Force casting to Number here to work around the fact that it's an enum on MSVC
- BOOST_STATIC_ASSERT(Number(complement_traits<Number>::max) > Number(0));
- BOOST_STATIC_ASSERT(Number(complement_traits<Number>::min) < Number(0));
- #endif
- const Number max = complement_traits<Number>::max;
- const Number min = complement_traits<Number>::min;
- std::cout << std::hex << "min = " << stream_number(min) << ", max = "
- << stream_number(max) << "..." << std::flush;
- std::cout << "difference_type = " << typeid(difference_type).name() << "..."
- << std::flush;
- typedef typename boost::conditional<
- (sizeof(Number) < sizeof(boost::intmax_t)),
- in_range_tag,
- out_of_range_tag
- >::type range_tag;
- signed_test<Number>(range_tag(), 0);
- }
- // Test for all numbers. The extra default Number parameter works around an MSVC
- // bug.
- template <class Number>
- void test(Number* = 0)
- {
- std::cout << "testing " << typeid(Number).name() << ":\n"
- #ifndef BOOST_NO_LIMITS_COMPILE_TIME_CONSTANTS
- << "is_signed: " << (std::numeric_limits<Number>::is_signed ? "true\n" : "false\n")
- << "is_bounded: " << (std::numeric_limits<Number>::is_bounded ? "true\n" : "false\n")
- << "digits: " << std::numeric_limits<Number>::digits << "\n"
- #endif
- << "..." << std::flush;
- // factoring out difference_type for the assert below confused Borland :(
- typedef boost::is_signed<
- #if !defined(BOOST_MSVC) || BOOST_MSVC > 1300
- typename
- #endif
- boost::detail::numeric_traits<Number>::difference_type
- > is_signed;
- BOOST_STATIC_ASSERT(is_signed::value);
- typedef typename boost::conditional<
- boost::is_signed<Number>::value,
- signed_tag,
- unsigned_tag
- >::type signedness;
- test_aux<Number>(signedness(), 0);
- std::cout << "passed" << std::endl;
- }
- int main()
- {
- test<char>();
- test<unsigned char>();
- test<signed char>();
- test<wchar_t>();
- test<short>();
- test<unsigned short>();
- test<int>();
- test<unsigned int>();
- test<long>();
- test<unsigned long>();
- #if defined(BOOST_HAS_LONG_LONG) && !defined(BOOST_NO_INTEGRAL_INT64_T)
- test< ::boost::long_long_type>();
- test< ::boost::ulong_long_type>();
- #elif defined(BOOST_MSVC)
- // The problem of not having compile-time static class constants other than
- // enums prevents this from working, since values get truncated.
- // test<boost::uintmax_t>();
- // test<boost::intmax_t>();
- #endif
- return 0;
- }
|