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 // Avoid left shifting negative integers, use multiplication instead
71 BOOST_STATIC_CONSTANT(Number, shift = 1u << CHAR_BIT);
72 BOOST_STATIC_CONSTANT(Number, max =
73 Number(Number(prev::max) * shift)
74 + Number(UCHAR_MAX));
75 BOOST_STATIC_CONSTANT(Number, min = Number(Number(prev::min) * shift));
76#endif
77 };
78};
79
80// Template class complement_base<> -- defines values for min and max for
81// complement<1>, at the deepest level of recursion. Uses "poor man's partial
82// specialization" again.
83template <bool is_signed> struct complement_base;
84
85template <> struct complement_base<false>
86{
87 template <class Number>
88 struct values
89 {
90 BOOST_STATIC_CONSTANT(Number, min = 0);
91 BOOST_STATIC_CONSTANT(Number, max = UCHAR_MAX);
92 };
93};
94
95template <> struct complement_base<true>
96{
97 template <class Number>
98 struct values
99 {
100 BOOST_STATIC_CONSTANT(Number, min = SCHAR_MIN);
101 BOOST_STATIC_CONSTANT(Number, max = SCHAR_MAX);
102 };
103};
104
105// Base specialization of complement, puts an end to the recursion.
106template <>
107struct complement<1>
108{
109 template <class Number>
110 struct traits
111 {
112 BOOST_STATIC_CONSTANT(bool, is_signed = boost::detail::is_signed<Number>::value);
113 BOOST_STATIC_CONSTANT(Number, min =
114 complement_base<is_signed>::template values<Number>::min);
115 BOOST_STATIC_CONSTANT(Number, max =
116 complement_base<is_signed>::template values<Number>::max);
117 };
118};
119
120// Now here's the "pretty" template you're intended to actually use.
121// complement_traits<Number>::min, complement_traits<Number>::max are the
122// minimum and maximum values of Number if Number is a built-in integer type.
123template <class Number>
124struct complement_traits
125{
126 BOOST_STATIC_CONSTANT(Number, max = (complement_traits_aux<Number, sizeof(Number)>::max));
127 BOOST_STATIC_CONSTANT(Number, min = (complement_traits_aux<Number, sizeof(Number)>::min));
128};
129
130// =================================================================================
131
132// Support for streaming various numeric types in exactly the format I want. I
133// needed this in addition to all the assertions so that I could see exactly
134// what was going on.
135//
136// Numbers go through a 2-stage conversion process (by default, though, no real
137// conversion).
138//
139template <class T> struct stream_as {
140 typedef T t1;
141 typedef T t2;
142};
143
144// char types first get converted to unsigned char, then to unsigned.
145template <> struct stream_as<char> {
146 typedef unsigned char t1;
147 typedef unsigned t2;
148};
149template <> struct stream_as<unsigned char> {
150 typedef unsigned char t1; typedef unsigned t2;
151};
152template <> struct stream_as<signed char> {
153 typedef unsigned char t1; typedef unsigned t2;
154};
155
156#if defined(BOOST_MSVC_STD_ITERATOR) // No intmax streaming built-in
157
158// With this library implementation, __int64 and __uint64 get streamed as strings
159template <> struct stream_as<boost::uintmax_t> {
160 typedef std::string t1;
161 typedef std::string t2;
162};
163
164template <> struct stream_as<boost::intmax_t> {
165 typedef std::string t1;
166 typedef std::string t2;
167};
168#endif
169
170// Standard promotion process for streaming
171template <class T> struct promote
172{
173 static typename stream_as<T>::t1 from(T x) {
174 typedef typename stream_as<T>::t1 t1;
175 return t1(x);
176 }
177};
178
179#if defined(BOOST_MSVC_STD_ITERATOR) // No intmax streaming built-in
180
181// On this platform, stream them as long/unsigned long if they fit.
182// Otherwise, write a string.
183template <> struct promote<boost::uintmax_t> {
184 std::string static from(const boost::uintmax_t x) {
185 if (x > ULONG_MAX)
186 return std::string("large unsigned value");
187 else
188 return boost::lexical_cast<std::string>((unsigned long)x);
189 }
190};
191template <> struct promote<boost::intmax_t> {
192 std::string static from(const boost::intmax_t x) {
193 if (x > boost::intmax_t(ULONG_MAX))
194 return std::string("large positive signed value");
195 else if (x >= 0)
196 return boost::lexical_cast<std::string>((unsigned long)x);
197
198 if (x < boost::intmax_t(LONG_MIN))
199 return std::string("large negative signed value");
200 else
201 return boost::lexical_cast<std::string>((long)x);
202 }
203};
204#endif
205
206// This is the function which converts types to the form I want to stream them in.
207template <class T>
208typename stream_as<T>::t2 stream_number(T x)
209{
210 return promote<T>::from(x);
211}
212// =================================================================================
213
214//
215// Tests for built-in signed and unsigned types
216//
217
218// Tag types for selecting tests
219struct unsigned_tag {};
220struct signed_tag {};
221
222// Tests for unsigned numbers. The extra default Number parameter works around
223// an MSVC bug.
224template <class Number>
225void test_aux(unsigned_tag, Number*)
226{
227 typedef typename boost::detail::numeric_traits<Number>::difference_type difference_type;
228 BOOST_STATIC_ASSERT(!boost::detail::is_signed<Number>::value);
229 BOOST_STATIC_ASSERT(
230 (sizeof(Number) < sizeof(boost::intmax_t))
231 | (boost::is_same<difference_type, boost::intmax_t>::value));
232
233#if defined(__GNUC__) && __GNUC__ == 4 && __GNUC_MINOR__ == 0 && __GNUC_PATCHLEVEL__ == 2
234 // GCC 4.0.2 ICEs on this C-style cases
235 BOOST_STATIC_ASSERT((complement_traits<Number>::max) > Number(0));
236 BOOST_STATIC_ASSERT((complement_traits<Number>::min) == Number(0));
237#else
238 // Force casting to Number here to work around the fact that it's an enum on MSVC
239 BOOST_STATIC_ASSERT(Number(complement_traits<Number>::max) > Number(0));
240 BOOST_STATIC_ASSERT(Number(complement_traits<Number>::min) == Number(0));
241#endif
242
243 const Number max = complement_traits<Number>::max;
244 const Number min = complement_traits<Number>::min;
245
246 const Number test_max = (sizeof(Number) < sizeof(boost::intmax_t))
247 ? max
248 : max / 2 - 1;
249
250 std::cout << std::hex << "(unsigned) min = " << stream_number(min) << ", max = "
251 << stream_number(max) << "..." << std::flush;
252 std::cout << "difference_type = " << typeid(difference_type).name() << "..."
253 << std::flush;
254
255 difference_type d1 = boost::detail::numeric_distance(Number(0), test_max);
256 difference_type d2 = boost::detail::numeric_distance(test_max, Number(0));
257
258 std::cout << "0->" << stream_number(test_max) << "==" << std::dec << stream_number(d1) << "; "
259 << std::hex << stream_number(test_max) << "->0==" << std::dec << stream_number(d2) << "..." << std::flush;
260
261 assert(d1 == difference_type(test_max));
262 assert(d2 == -difference_type(test_max));
263}
264
265// Tests for signed numbers. The extra default Number parameter works around an
266// MSVC bug.
267struct out_of_range_tag {};
268struct in_range_tag {};
269
270// This test morsel gets executed for numbers whose difference will always be
271// representable in intmax_t
272template <class Number>
273void signed_test(in_range_tag, Number*)
274{
275 BOOST_STATIC_ASSERT(boost::detail::is_signed<Number>::value);
276 typedef typename boost::detail::numeric_traits<Number>::difference_type difference_type;
277 const Number max = complement_traits<Number>::max;
278 const Number min = complement_traits<Number>::min;
279
280 difference_type d1 = boost::detail::numeric_distance(min, max);
281 difference_type d2 = boost::detail::numeric_distance(max, min);
282
283 std::cout << stream_number(min) << "->" << stream_number(max) << "==";
284 std::cout << std::dec << stream_number(d1) << "; ";
285 std::cout << std::hex << stream_number(max) << "->" << stream_number(min)
286 << "==" << std::dec << stream_number(d2) << "..." << std::flush;
287 assert(d1 == difference_type(max) - difference_type(min));
288 assert(d2 == difference_type(min) - difference_type(max));
289}
290
291// This test morsel gets executed for numbers whose difference may exceed the
292// capacity of intmax_t.
293template <class Number>
294void signed_test(out_of_range_tag, Number*)
295{
296 BOOST_STATIC_ASSERT(boost::detail::is_signed<Number>::value);
297 typedef typename boost::detail::numeric_traits<Number>::difference_type difference_type;
298 const Number max = complement_traits<Number>::max;
299 const Number min = complement_traits<Number>::min;
300
301 difference_type min_distance = complement_traits<difference_type>::min;
302 difference_type max_distance = complement_traits<difference_type>::max;
303
304 const Number n1 = Number(min + max_distance);
305 const Number n2 = Number(max + min_distance);
306 difference_type d1 = boost::detail::numeric_distance(min, n1);
307 difference_type d2 = boost::detail::numeric_distance(max, n2);
308
309 std::cout << stream_number(min) << "->" << stream_number(n1) << "==";
310 std::cout << std::dec << stream_number(d1) << "; ";
311 std::cout << std::hex << stream_number(max) << "->" << stream_number(n2)
312 << "==" << std::dec << stream_number(d2) << "..." << std::flush;
313 assert(d1 == max_distance);
314 assert(d2 == min_distance);
315}
316
317template <class Number>
318void test_aux(signed_tag, Number*)
319{
320 typedef typename boost::detail::numeric_traits<Number>::difference_type difference_type;
321 BOOST_STATIC_ASSERT(boost::detail::is_signed<Number>::value);
322 BOOST_STATIC_ASSERT(
323 (sizeof(Number) < sizeof(boost::intmax_t))
324 | (boost::is_same<difference_type, Number>::value));
325
326#if defined(__GNUC__) && __GNUC__ == 4 && __GNUC_MINOR__ == 0 && __GNUC_PATCHLEVEL__ == 2
327 // GCC 4.0.2 ICEs on this cast
328 BOOST_STATIC_ASSERT((complement_traits<Number>::max) > Number(0));
329 BOOST_STATIC_ASSERT((complement_traits<Number>::min) < Number(0));
330#else
331 // Force casting to Number here to work around the fact that it's an enum on MSVC
332 BOOST_STATIC_ASSERT(Number(complement_traits<Number>::max) > Number(0));
333 BOOST_STATIC_ASSERT(Number(complement_traits<Number>::min) < Number(0));
334#endif
335 const Number max = complement_traits<Number>::max;
336 const Number min = complement_traits<Number>::min;
337
338 std::cout << std::hex << "min = " << stream_number(min) << ", max = "
339 << stream_number(max) << "..." << std::flush;
340 std::cout << "difference_type = " << typeid(difference_type).name() << "..."
341 << std::flush;
342
343 typedef typename boost::detail::if_true<
344 (sizeof(Number) < sizeof(boost::intmax_t))>
345 ::template then<
346 in_range_tag,
347 out_of_range_tag
348 >::type
349 range_tag;
350 signed_test<Number>(range_tag(), 0);
351}
352
353
354// Test for all numbers. The extra default Number parameter works around an MSVC
355// bug.
356template <class Number>
357void test(Number* = 0)
358{
359 std::cout << "testing " << typeid(Number).name() << ":\n"
360#ifndef BOOST_NO_LIMITS_COMPILE_TIME_CONSTANTS
361 << "is_signed: " << (std::numeric_limits<Number>::is_signed ? "true\n" : "false\n")
362 << "is_bounded: " << (std::numeric_limits<Number>::is_bounded ? "true\n" : "false\n")
363 << "digits: " << std::numeric_limits<Number>::digits << "\n"
364#endif
365 << "..." << std::flush;
366
367 // factoring out difference_type for the assert below confused Borland :(
368 typedef boost::detail::is_signed<
369#if !defined(BOOST_MSVC) || BOOST_MSVC > 1300
370 typename
371#endif
372 boost::detail::numeric_traits<Number>::difference_type
373 > is_signed;
374 BOOST_STATIC_ASSERT(is_signed::value);
375
376 typedef typename boost::detail::if_true<
377 boost::detail::is_signed<Number>::value
378 >::template then<signed_tag, unsigned_tag>::type signedness;
379
380 test_aux<Number>(signedness(), 0);
381 std::cout << "passed" << std::endl;
382}
383
384int main()
385{
386 test<char>();
387 test<unsigned char>();
388 test<signed char>();
389 test<wchar_t>();
390 test<short>();
391 test<unsigned short>();
392 test<int>();
393 test<unsigned int>();
394 test<long>();
395 test<unsigned long>();
396#if defined(BOOST_HAS_LONG_LONG) && !defined(BOOST_NO_INTEGRAL_INT64_T)
397 test< ::boost::long_long_type>();
398 test< ::boost::ulong_long_type>();
399#elif defined(BOOST_MSVC)
400 // The problem of not having compile-time static class constants other than
401 // enums prevents this from working, since values get truncated.
402 // test<boost::uintmax_t>();
403 // test<boost::intmax_t>();
404#endif
405 return 0;
406}
407

source code of boost/libs/utility/numeric_traits_test.cpp