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