]> git.proxmox.com Git - ceph.git/blob - ceph/src/boost/libs/detail/test/numeric_traits_test.cpp
import quincy beta 17.1.0
[ceph.git] / ceph / src / boost / libs / detail / test / numeric_traits_test.cpp
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 {
180 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> {
191 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> {
202 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>
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>
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>
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>
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>
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>
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
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 }