diff options
Diffstat (limited to 'src/boost/libs/detail/test/numeric_traits_test.cpp')
-rw-r--r-- | src/boost/libs/detail/test/numeric_traits_test.cpp | 416 |
1 files changed, 416 insertions, 0 deletions
diff --git a/src/boost/libs/detail/test/numeric_traits_test.cpp b/src/boost/libs/detail/test/numeric_traits_test.cpp new file mode 100644 index 00000000..c22cc6f3 --- /dev/null +++ b/src/boost/libs/detail/test/numeric_traits_test.cpp @@ -0,0 +1,416 @@ +// (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; +} |