diff --git a/numeric_traits_test.cpp b/numeric_traits_test.cpp new file mode 100644 index 0000000..170f61c --- /dev/null +++ b/numeric_traits_test.cpp @@ -0,0 +1,323 @@ +// (C) Copyright David Abrahams 2001. Permission to copy, use, modify, +// sell and distribute this software is granted provided this +// copyright notice appears in all copies. This software is provided +// "as is" without express or implied warranty, and with no claim as +// to its suitability for any purpose. + +// See http://www.boost.org for most recent version including documentation. + +// Revision History +// 21 Jan 2001 Initial version (David Abrahams) + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifndef BOOST_NO_LIMITS +# include +#endif + +// A macro for declaring class compile-time constants. +#ifndef BOOST_NO_INCLASS_MEMBER_INITIALIZATION +# define DECLARE_CLASS_CONST(type, init) static const type init +#else +# define DECLARE_CLASS_CONST(type, init) enum { init } +#endif + +// template class complement_traits -- +// +// statically computes the max and min for 1s and 2s-complement binary +// numbers. This helps on platforms without support. It also shows +// an example of a recursive template that works with MSVC! +// + +template 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 +struct complement_traits_aux +{ + DECLARE_CLASS_CONST(Number, max = complement::template traits::max); + DECLARE_CLASS_CONST(Number, min = complement::template traits::min); +}; + +template +struct complement +{ + template + struct traits + { + private: + // indirection through complement_traits_aux neccessary to keep MSVC happy + typedef complement_traits_aux prev; + public: + DECLARE_CLASS_CONST(Number, max = + Number(Number(prev::max) << CHAR_BIT) + + Number(UCHAR_MAX)); + + DECLARE_CLASS_CONST(Number, min = Number(Number(prev::min) << CHAR_BIT)); + }; +}; + +// 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 struct complement_base; + +template <> struct complement_base +{ + template + struct values + { + DECLARE_CLASS_CONST(Number, min = 0); + DECLARE_CLASS_CONST(Number, max = UCHAR_MAX); + }; +}; + +template <> struct complement_base +{ + template + struct values + { + DECLARE_CLASS_CONST(Number, min = SCHAR_MIN); + DECLARE_CLASS_CONST(Number, max = SCHAR_MAX); + }; +}; + +// Base specialization of complement, puts an end to the recursion. +template <> +struct complement<1> +{ + template + struct traits + { + DECLARE_CLASS_CONST(bool, is_signed = boost::detail::is_signed::value); + DECLARE_CLASS_CONST(Number, min = + complement_base::template values::min); + DECLARE_CLASS_CONST(Number, max = + complement_base::template values::max); + }; +}; + +// Now here's the "pretty" template you're intended to actually use. +// complement_traits::min, complement_traits::max are the +// minimum and maximum values of Number if Number is a built-in integer type. +template +struct complement_traits +{ + DECLARE_CLASS_CONST(Number, max = (complement_traits_aux::max)); + DECLARE_CLASS_CONST(Number, min = (complement_traits_aux::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 struct stream_as { + typedef T t1; + typedef T t2; +}; + +// char types first get converted to unsigned char, then to unsigned. +template <> struct stream_as { + typedef unsigned char t1; + typedef unsigned t2; +}; +template <> struct stream_as { + typedef unsigned char t1; typedef unsigned t2; +}; +template <> struct stream_as { + typedef unsigned char t1; typedef unsigned t2; +}; + +#if defined(BOOST_MSVC) // No intmax streaming built-in + +// On this platform, __int64 and __uint64 get streamed as strings +template <> struct stream_as { + typedef std::string t1; + typedef std::string t2; +}; + +template <> struct stream_as { + typedef std::string t1; + typedef std::string t2; +}; +#endif + +// Standard promotion process for streaming +template struct promote +{ + typename stream_as::t1 static from(T x) { + typedef typename stream_as::t1 t1; + return t1(x); + } +}; + +#if defined(BOOST_MSVC) // No intmax streaming built-in + +// On this platform, stream them as long/unsigned long if they fit. +// Otherwise, write a string. +template <> struct promote { + std::string static from(const boost::uintmax_t x) { + if (x > ULONG_MAX) + return std::string("large unsigned value"); + else + return boost::lexical_cast((unsigned long)x); + } +}; +template <> struct promote { + 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) + return boost::lexical_cast((unsigned long)x); + + if (x < boost::intmax_t(LONG_MIN)) + return std::string("large negative signed value"); + else + return boost::lexical_cast((long)x); + } +}; +#endif + +// This is the function which converts types to the form I want to stream them in. +template +typename stream_as::t2 stream_number(T x) +{ + return promote::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 +void test_aux(unsigned_tag, Number* = 0) +{ + typedef typename boost::detail::numeric_traits::difference_type difference_type; + BOOST_STATIC_ASSERT(!boost::detail::is_signed::value); + BOOST_STATIC_ASSERT( + (sizeof(Number) < sizeof(boost::intmax_t)) + | (boost::is_same::value)); + + // Force casting to Number here to work around the fact that it's an enum on MSVC + BOOST_STATIC_ASSERT(Number(complement_traits::max) > Number(0)); + BOOST_STATIC_ASSERT(Number(complement_traits::min) == Number(0)); + + const Number max = complement_traits::max; + const Number min = complement_traits::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. +template +void test_aux(signed_tag, Number* = 0) +{ + typedef typename boost::detail::numeric_traits::difference_type difference_type; + BOOST_STATIC_ASSERT(boost::detail::is_signed::value); + BOOST_STATIC_ASSERT( + (sizeof(Number) < sizeof(boost::intmax_t)) + | (boost::is_same::value)); + + // Force casting to Number here to work around the fact that it's an enum on MSVC + BOOST_STATIC_ASSERT(Number(complement_traits::max) > Number(0)); + BOOST_STATIC_ASSERT(Number(complement_traits::min) < Number(0)); + + const Number max = complement_traits::max; + const Number min = complement_traits::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; + assert(min < max); + + difference_type d1 = boost::detail::numeric_distance(min, max); + difference_type d2 = boost::detail::numeric_distance(max, min); + if (sizeof(Number) < sizeof(boost::intmax_t)) + { + 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)); + } +} + + +// Test for all numbers. The extra default Number parameter works around an MSVC +// bug. +template +void test(Number* = 0) +{ + std::cout << "testing " << typeid(Number).name() << "..." << std::flush; + typedef typename boost::detail::numeric_traits::difference_type difference_type; + BOOST_STATIC_ASSERT(boost::detail::is_signed::value); + + typedef typename boost::detail::if_true< + boost::detail::is_signed::value + >::template then::type signedness; + + test_aux(signedness()); + std::cout << "passed" << std::endl; +} + +int main() +{ + test(); + test(); + test(); + test(); + test(); + test(); + test(); + test(); + test(); +#if defined(ULLONG_MAX) || defined(ULONG_LONG_MAX) + test(); + test(); +#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(); + // test(); +#endif + return 0; +}