Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

source: downloads/boost_1_34_1/libs/utility/numeric_traits_test.cpp @ 47

Last change on this file since 47 was 29, checked in by landauf, 17 years ago

updated boost from 1_33_1 to 1_34_1

File size: 14.6 KB
Line 
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
41template <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.
46template <class Number, unsigned size>
47struct 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
53template <unsigned size>
54struct 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.
82template <bool is_signed> struct complement_base;
83
84template <> 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
94template <> 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.
105template <>
106struct 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.
122template <class Number>
123struct 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//
138template <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.
144template <> struct stream_as<char> {
145    typedef unsigned char t1;
146    typedef unsigned t2;
147};
148template <> struct stream_as<unsigned char> {
149    typedef unsigned char t1; typedef unsigned t2;
150};
151template <> 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
158template <> struct stream_as<boost::uintmax_t> {
159    typedef std::string t1;
160    typedef std::string t2;
161};
162
163template <> 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
170template <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.
182template <> 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};
190template <> 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.
206template <class T>
207typename 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
218struct unsigned_tag {};
219struct signed_tag {};
220
221// Tests for unsigned numbers. The extra default Number parameter works around
222// an MSVC bug.
223template <class Number>
224void 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.
266struct out_of_range_tag {};
267struct in_range_tag {};
268
269// This test morsel gets executed for numbers whose difference will always be
270// representable in intmax_t
271template <class Number>
272void 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.
292template <class Number>
293void 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
316template <class Number>
317void 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.
355template <class Number>
356void 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
383int 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}
Note: See TracBrowser for help on using the repository browser.