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 | 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. |
---|
82 | template <bool is_signed> struct complement_base; |
---|
83 | |
---|
84 | template <> 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 | |
---|
94 | template <> 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. |
---|
105 | template <> |
---|
106 | struct 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. |
---|
122 | template <class Number> |
---|
123 | struct 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 | // |
---|
138 | template <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. |
---|
144 | template <> struct stream_as<char> { |
---|
145 | typedef unsigned char t1; |
---|
146 | typedef unsigned t2; |
---|
147 | }; |
---|
148 | template <> struct stream_as<unsigned char> { |
---|
149 | typedef unsigned char t1; typedef unsigned t2; |
---|
150 | }; |
---|
151 | template <> 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 |
---|
158 | template <> struct stream_as<boost::uintmax_t> { |
---|
159 | typedef std::string t1; |
---|
160 | typedef std::string t2; |
---|
161 | }; |
---|
162 | |
---|
163 | template <> 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 |
---|
170 | template <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. |
---|
182 | template <> 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 | }; |
---|
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"); |
---|
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. |
---|
206 | template <class T> |
---|
207 | typename 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 |
---|
218 | struct unsigned_tag {}; |
---|
219 | struct signed_tag {}; |
---|
220 | |
---|
221 | // Tests for unsigned numbers. The extra default Number parameter works around |
---|
222 | // an MSVC bug. |
---|
223 | template <class Number> |
---|
224 | void 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. |
---|
266 | struct out_of_range_tag {}; |
---|
267 | struct in_range_tag {}; |
---|
268 | |
---|
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*) |
---|
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. |
---|
292 | template <class Number> |
---|
293 | void 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 | |
---|
316 | template <class Number> |
---|
317 | void 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. |
---|
355 | template <class Number> |
---|
356 | void 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 | |
---|
383 | int 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 | } |
---|