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)
6 // See http://www.boost.org for most recent version including documentation.
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
16 // 21 Jan 2001 Initial version (David Abrahams)
18 #include <boost/detail/numeric_traits.hpp>
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>
29 #ifndef BOOST_NO_LIMITS
33 // =================================================================================
34 // template class complement_traits<Number> --
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!
41 template <unsigned size> struct complement; // forward
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.
46 template <class Number, unsigned size>
47 struct complement_traits_aux
49 BOOST_STATIC_CONSTANT(Number, max = complement<size>::template traits<Number>::max);
50 BOOST_STATIC_CONSTANT(Number, min = complement<size>::template traits<Number>::min);
53 template <unsigned size>
56 template <class Number>
60 // indirection through complement_traits_aux necessary to keep MSVC happy
61 typedef complement_traits_aux<Number, size - 1> prev;
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)
68 BOOST_STATIC_CONSTANT(Number, min = Number((prev::min) << CHAR_BIT));
70 BOOST_STATIC_CONSTANT(Number, max =
71 Number(Number(prev::max) << CHAR_BIT)
73 BOOST_STATIC_CONSTANT(Number, min = Number(Number(prev::min) << CHAR_BIT));
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.
82 template <bool is_signed> struct complement_base;
84 template <> struct complement_base<false>
86 template <class Number>
89 BOOST_STATIC_CONSTANT(Number, min = 0);
90 BOOST_STATIC_CONSTANT(Number, max = UCHAR_MAX);
94 template <> struct complement_base<true>
96 template <class Number>
99 BOOST_STATIC_CONSTANT(Number, min = SCHAR_MIN);
100 BOOST_STATIC_CONSTANT(Number, max = SCHAR_MAX);
104 // Base specialization of complement, puts an end to the recursion.
108 template <class Number>
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);
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.
122 template <class Number>
123 struct complement_traits
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));
129 // =================================================================================
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.
135 // Numbers go through a 2-stage conversion process (by default, though, no real
138 template <class T> struct stream_as {
143 // char types first get converted to unsigned char, then to unsigned.
144 template <> struct stream_as<char> {
145 typedef unsigned char t1;
148 template <> struct stream_as<unsigned char> {
149 typedef unsigned char t1; typedef unsigned t2;
151 template <> struct stream_as<signed char> {
152 typedef unsigned char t1; typedef unsigned t2;
155 #if defined(BOOST_MSVC_STD_ITERATOR) // No intmax streaming built-in
157 // With this library implementation, __int64 and __uint64 get streamed as strings
158 template <> struct stream_as<boost::uintmax_t> {
159 typedef std::string t1;
160 typedef std::string t2;
163 template <> struct stream_as<boost::intmax_t> {
164 typedef std::string t1;
165 typedef std::string t2;
169 // Standard promotion process for streaming
170 template <class T> struct promote
172 static typename stream_as<T>::t1 from(T x) {
173 typedef typename stream_as<T>::t1 t1;
178 #if defined(BOOST_MSVC_STD_ITERATOR) // No intmax streaming built-in
180 // On this platform, stream them as long/unsigned long if they fit.
181 // Otherwise, write a string.
182 template <> struct promote<boost::uintmax_t> {
183 std::string static from(const boost::uintmax_t x) {
185 return std::string("large unsigned value");
187 return boost::lexical_cast<std::string>((unsigned long)x);
190 template <> 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");
195 return boost::lexical_cast<std::string>((unsigned long)x);
197 if (x < boost::intmax_t(LONG_MIN))
198 return std::string("large negative signed value");
200 return boost::lexical_cast<std::string>((long)x);
205 // This is the function which converts types to the form I want to stream them in.
207 typename stream_as<T>::t2 stream_number(T x)
209 return promote<T>::from(x);
211 // =================================================================================
214 // Tests for built-in signed and unsigned types
217 // Tag types for selecting tests
218 struct unsigned_tag {};
219 struct signed_tag {};
221 // Tests for unsigned numbers. The extra default Number parameter works around
223 template <class Number>
224 void test_aux(unsigned_tag, Number*)
226 typedef typename boost::detail::numeric_traits<Number>::difference_type difference_type;
227 BOOST_STATIC_ASSERT(!boost::detail::is_signed<Number>::value);
229 (sizeof(Number) < sizeof(boost::intmax_t))
230 | (boost::is_same<difference_type, boost::intmax_t>::value));
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));
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));
242 const Number max = complement_traits<Number>::max;
243 const Number min = complement_traits<Number>::min;
245 const Number test_max = (sizeof(Number) < sizeof(boost::intmax_t))
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() << "..."
254 difference_type d1 = boost::detail::numeric_distance(Number(0), test_max);
255 difference_type d2 = boost::detail::numeric_distance(test_max, Number(0));
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;
260 assert(d1 == difference_type(test_max));
261 assert(d2 == -difference_type(test_max));
264 // Tests for signed numbers. The extra default Number parameter works around an
266 struct out_of_range_tag {};
267 struct in_range_tag {};
269 // This test morsel gets executed for numbers whose difference will always be
270 // representable in intmax_t
271 template <class Number>
272 void signed_test(in_range_tag, Number*)
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;
279 difference_type d1 = boost::detail::numeric_distance(min, max);
280 difference_type d2 = boost::detail::numeric_distance(max, min);
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));
290 // This test morsel gets executed for numbers whose difference may exceed the
291 // capacity of intmax_t.
292 template <class Number>
293 void signed_test(out_of_range_tag, Number*)
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;
300 difference_type min_distance = complement_traits<difference_type>::min;
301 difference_type max_distance = complement_traits<difference_type>::max;
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);
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);
316 template <class Number>
317 void test_aux(signed_tag, Number*)
319 typedef typename boost::detail::numeric_traits<Number>::difference_type difference_type;
320 BOOST_STATIC_ASSERT(boost::detail::is_signed<Number>::value);
322 (sizeof(Number) < sizeof(boost::intmax_t))
323 | (boost::is_same<difference_type, Number>::value));
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));
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));
334 const Number max = complement_traits<Number>::max;
335 const Number min = complement_traits<Number>::min;
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() << "..."
342 typedef typename boost::detail::if_true<
343 (sizeof(Number) < sizeof(boost::intmax_t))>
349 signed_test<Number>(range_tag(), 0);
353 // Test for all numbers. The extra default Number parameter works around an MSVC
355 template <class Number>
356 void test(Number* = 0)
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"
364 << "..." << std::flush;
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
371 boost::detail::numeric_traits<Number>::difference_type
373 BOOST_STATIC_ASSERT(is_signed::value);
375 typedef typename boost::detail::if_true<
376 boost::detail::is_signed<Number>::value
377 >::template then<signed_tag, unsigned_tag>::type signedness;
379 test_aux<Number>(signedness(), 0);
380 std::cout << "passed" << std::endl;
386 test<unsigned char>();
390 test<unsigned short>();
392 test<unsigned int>();
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>();