1 /*=============================================================================
2 Copyright (c) 2018 Nikita Kniazev
4 Distributed under the Boost Software License, Version 1.0. (See accompanying
5 file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
6 =============================================================================*/
8 #include <boost/detail/lightweight_test.hpp>
9 #include <boost/spirit/home/qi/numeric/numeric_utils.hpp>
10 #include <boost/static_assert.hpp>
11 #include <cmath> // for std::pow
17 # pragma warning(disable: 4127) // conditional expression is constant
20 template <int Min
, int Max
>
23 BOOST_DEFAULTED_FUNCTION(custom_int(), {})
24 BOOST_CONSTEXPR
custom_int(int value
) : value_(value
) {}
26 custom_int
operator+(custom_int x
) const { return value_
+ x
.value_
; }
27 custom_int
operator-(custom_int x
) const { return value_
- x
.value_
; }
28 custom_int
operator*(custom_int x
) const { return value_
* x
.value_
; }
29 custom_int
operator/(custom_int x
) const { return value_
/ x
.value_
; }
31 custom_int
& operator+=(custom_int x
) { value_
+= x
.value_
; return *this; }
32 custom_int
& operator-=(custom_int x
) { value_
-= x
.value_
; return *this; }
33 custom_int
& operator*=(custom_int x
) { value_
*= x
.value_
; return *this; }
34 custom_int
& operator/=(custom_int x
) { value_
/= x
.value_
; return *this; }
35 custom_int
& operator++() { ++value_
; return *this; }
36 custom_int
& operator--() { --value_
; return *this; }
37 custom_int
operator++(int) { return value_
++; }
38 custom_int
operator--(int) { return value_
--; }
40 custom_int
operator+() { return +value_
; }
41 custom_int
operator-() { return -value_
; }
43 bool operator< (custom_int x
) const { return value_
< x
.value_
; }
44 bool operator> (custom_int x
) const { return value_
> x
.value_
; }
45 bool operator<=(custom_int x
) const { return value_
<= x
.value_
; }
46 bool operator>=(custom_int x
) const { return value_
>= x
.value_
; }
47 bool operator==(custom_int x
) const { return value_
== x
.value_
; }
48 bool operator!=(custom_int x
) const { return value_
!= x
.value_
; }
50 template <typename Char
, typename Traits
>
51 friend std::basic_ostream
<Char
, Traits
>&
52 operator<<(std::basic_ostream
<Char
, Traits
>& os
, custom_int x
) {
53 return os
<< x
.value_
;
56 BOOST_STATIC_CONSTEXPR
int max
= Max
;
57 BOOST_STATIC_CONSTEXPR
int min
= Min
;
65 template <int Min
, int Max
> struct digits
;
66 template <> struct digits
<-9, 9> { BOOST_STATIC_CONSTEXPR
int r2
= 3, r10
= 1; };
67 template <> struct digits
<-10, 10> { BOOST_STATIC_CONSTEXPR
int r2
= 3, r10
= 1; };
68 template <> struct digits
<-15, 15> { BOOST_STATIC_CONSTEXPR
int r2
= 3, r10
= 1; };
74 template <int Min
, int Max
>
75 class numeric_limits
<custom_int
<Min
, Max
> > : public numeric_limits
<int>
78 static BOOST_CONSTEXPR custom_int
<Min
, Max
> max() BOOST_NOEXCEPT_OR_NOTHROW
{ return Max
; }
79 static BOOST_CONSTEXPR custom_int
<Min
, Max
> min() BOOST_NOEXCEPT_OR_NOTHROW
{ return Min
; }
80 static BOOST_CONSTEXPR custom_int
<Min
, Max
> lowest() BOOST_NOEXCEPT_OR_NOTHROW
{ return min(); }
81 BOOST_STATIC_ASSERT_MSG(numeric_limits
<int>::radix
== 2, "hardcoded for digits of radix 2");
82 BOOST_STATIC_CONSTEXPR
int digits
= utils::digits
<Min
, Max
>::r2
;
83 BOOST_STATIC_CONSTEXPR
int digits10
= utils::digits
<Min
, Max
>::r10
;
88 namespace qi
= boost::spirit::qi
;
90 template <typename T
, int Base
, int MaxDigits
>
91 void test_overflow_handling(char const* begin
, char const* end
, int i
)
93 // Check that parser fails on overflow
94 BOOST_STATIC_ASSERT_MSG(std::numeric_limits
<T
>::is_bounded
, "tests prerequest");
95 BOOST_ASSERT_MSG(MaxDigits
== -1 || static_cast<int>(std::pow(float(Base
), MaxDigits
)) > T::max
,
97 int initial
= Base
- i
% Base
; // just a 'random' non-equal to i number
99 char const* it
= begin
;
100 bool r
= qi::extract_int
<T
, Base
, 1, MaxDigits
>::call(it
, end
, x
);
101 if (T::min
<= i
&& i
<= T::max
) {
103 BOOST_TEST(it
== end
);
108 BOOST_TEST(it
== begin
);
112 template <typename T
, int Base
>
113 void test_unparsed_digits_are_not_consumed(char const* it
, char const* end
, int i
)
115 // Check that unparsed digits are not consumed
116 BOOST_STATIC_ASSERT_MSG(T::min
<= -Base
+1, "test prerequest");
117 BOOST_STATIC_ASSERT_MSG(T::max
>= Base
-1, "test prerequest");
118 bool has_sign
= *it
== '+' || *it
== '-';
119 char const* begin
= it
;
120 int initial
= Base
- i
% Base
; // just a 'random' non-equal to i number
122 bool r
= qi::extract_int
<T
, Base
, 1, 1>::call(it
, end
, x
);
124 if (-Base
< i
&& i
< Base
) {
125 BOOST_TEST(it
== end
);
129 BOOST_TEST_EQ(end
- it
, (end
- begin
) - 1 - has_sign
);
130 BOOST_TEST_EQ(x
, i
/ Base
);
134 template <typename T
, int Base
>
135 void test_ignore_overflow_digits(char const* it
, char const* end
, int i
)
137 // TODO: Check accumulating too?
138 if (i
< 0) return; // extract_int does not support IgnoreOverflowDigits
140 bool has_sign
= *it
== '+' || *it
== '-';
141 char const* begin
= it
;
142 int initial
= Base
- i
% Base
; // just a 'random' non-equal to i number
144 BOOST_TEST((qi::extract_uint
<T
, Base
, 1, -1, false, true>::call(it
, end
, x
)));
145 if (T::min
<= i
&& i
<= T::max
) {
146 BOOST_TEST(it
== end
);
150 BOOST_TEST_EQ(it
- begin
, (qi::detail::digits_traits
<T
, Base
>::value
) + has_sign
);
151 if (Base
== std::numeric_limits
<T
>::radix
)
152 BOOST_TEST_EQ(it
- begin
, std::numeric_limits
<T
>::digits
+ has_sign
);
154 BOOST_TEST_EQ(it
- begin
, std::numeric_limits
<T
>::digits10
+ has_sign
);
156 for (char const* p
= it
; p
< end
; ++p
) expected
/= Base
;
157 BOOST_TEST_EQ(x
, expected
);
161 template <typename T
, int Base
>
162 void run_tests(char const* begin
, char const* end
, int i
)
164 // Check that parser fails on overflow
165 test_overflow_handling
<T
, Base
, -1>(begin
, end
, i
);
166 // Check that MaxDigits > digits10 behave like MaxDigits=-1
167 test_overflow_handling
<T
, Base
, 2>(begin
, end
, i
);
168 // Check that unparsed digits are not consumed
169 test_unparsed_digits_are_not_consumed
<T
, Base
>(begin
, end
, i
);
170 // Check that IgnoreOverflowDigits does what we expect
171 test_ignore_overflow_digits
<T
, Base
>(begin
, end
, i
);
176 for (int i
= -30; i
<= 30; ++i
) {
177 std::ostringstream oss
;
179 std::string s
= oss
.str();
180 char const* begin
= s
.data(), *const end
= begin
+ s
.size();
182 // log(Base, abs(MinOrMax) + 1) == digits
183 run_tests
<custom_int
<-9, 9>, 10>(begin
, end
, i
);
184 // (MinOrMax % Base) == 0
185 run_tests
<custom_int
<-10, 10>, 10>(begin
, end
, i
);
186 // (MinOrMax % Base) != 0
187 run_tests
<custom_int
<-15, 15>, 10>(begin
, end
, i
);
190 return boost::report_errors();