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 | |
41 | template <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. |
46 | template <class Number, unsigned size> |
47 | struct 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 | |
53 | template <unsigned size> |
54 | struct 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. |
83 | template <bool is_signed> struct complement_base; |
84 | |
85 | template <> 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 | |
95 | template <> 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. |
106 | template <> |
107 | struct 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. |
123 | template <class Number> |
124 | struct 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 | // |
139 | template <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. |
145 | template <> struct stream_as<char> { |
146 | typedef unsigned char t1; |
147 | typedef unsigned t2; |
148 | }; |
149 | template <> struct stream_as<unsigned char> { |
150 | typedef unsigned char t1; typedef unsigned t2; |
151 | }; |
152 | template <> 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 |
159 | template <> struct stream_as<boost::uintmax_t> { |
160 | typedef std::string t1; |
161 | typedef std::string t2; |
162 | }; |
163 | |
164 | template <> 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 |
171 | template <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. |
183 | template <> 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 | }; |
191 | template <> 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. |
207 | template <class T> |
208 | typename 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 |
219 | struct unsigned_tag {}; |
220 | struct signed_tag {}; |
221 | |
222 | // Tests for unsigned numbers. The extra default Number parameter works around |
223 | // an MSVC bug. |
224 | template <class Number> |
225 | void 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. |
267 | struct out_of_range_tag {}; |
268 | struct in_range_tag {}; |
269 | |
270 | // This test morsel gets executed for numbers whose difference will always be |
271 | // representable in intmax_t |
272 | template <class Number> |
273 | void 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. |
293 | template <class Number> |
294 | void 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 | |
317 | template <class Number> |
318 | void 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. |
356 | template <class Number> |
357 | void 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 | |
384 | int 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 | |