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/spirit/home/x3/support/numeric_utils/extract_int.hpp>
10 #include <boost/core/lightweight_test.hpp>
11 #include <cmath> // for std::pow
17 # pragma warning(disable: 4127) // conditional expression is constant
20 template <int Min
, int Max
>
23 custom_int() = default;
24 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 static constexpr int max
= Max
;
57 static constexpr int min
= Min
;
65 template <int Min
, int Max
> struct digits
;
66 template <> struct digits
<-9, 9> { static constexpr int r2
= 3, r10
= 1; };
67 template <> struct digits
<-10, 10> { static constexpr int r2
= 3, r10
= 1; };
68 template <> struct digits
<-15, 15> { 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 constexpr custom_int
<Min
, Max
> max() noexcept
{ return Max
; }
79 static constexpr custom_int
<Min
, Max
> min() noexcept
{ return Min
; }
80 static constexpr custom_int
<Min
, Max
> lowest() noexcept
{ return min(); }
81 static_assert(numeric_limits
<int>::radix
== 2, "hardcoded for digits of radix 2");
82 static constexpr int digits
= utils::digits
<Min
, Max
>::r2
;
83 static constexpr int digits10
= utils::digits
<Min
, Max
>::r10
;
88 namespace x3
= boost::spirit::x3
;
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 static_assert(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
= x3::extract_int
<T
, Base
, 1, MaxDigits
>::call(it
, end
, x
);
101 if (T::min
<= i
&& i
<= T::max
) {
103 BOOST_TEST(it
== end
);
107 if (MaxDigits
== -1) // TODO: Looks like a regression. See #430
110 BOOST_TEST(it
== begin
);
114 template <typename T
, int Base
>
115 void test_unparsed_digits_are_not_consumed(char const* it
, char const* end
, int i
)
117 // Check that unparsed digits are not consumed
118 static_assert(T::min
<= -Base
+1, "test prerequest");
119 static_assert(T::max
>= Base
-1, "test prerequest");
120 bool has_sign
= *it
== '+' || *it
== '-';
122 int initial
= Base
- i
% Base
; // just a 'random' non-equal to i number
124 bool r
= x3::extract_int
<T
, Base
, 1, 1>::call(it
, end
, x
);
126 if (-Base
< i
&& i
< Base
) {
127 BOOST_TEST(it
== end
);
131 BOOST_TEST_EQ(end
- it
, len
- 1 - has_sign
);
132 BOOST_TEST_EQ(x
, i
/ Base
);
136 template <typename T
, int Base
>
137 void run_tests(char const* begin
, char const* end
, int i
)
139 // Check that parser fails on overflow
140 test_overflow_handling
<T
, Base
, -1>(begin
, end
, i
);
141 // Check that MaxDigits > digits10 behave like MaxDigits=-1
142 test_overflow_handling
<T
, Base
, 2>(begin
, end
, i
);
143 // Check that unparsed digits are not consumed
144 test_unparsed_digits_are_not_consumed
<T
, Base
>(begin
, end
, i
);
149 for (int i
= -30; i
<= 30; ++i
) {
151 std::snprintf(s
, 4, "%d", i
);
152 auto begin
= &s
[0], end
= begin
+ std::strlen(begin
);
154 // log(Base, abs(MinOrMax) + 1) == digits
155 run_tests
<custom_int
<-9, 9>, 10>(begin
, end
, i
);
156 // (MinOrMax % Base) == 0
157 run_tests
<custom_int
<-10, 10>, 10>(begin
, end
, i
);
158 // (MinOrMax % Base) != 0
159 run_tests
<custom_int
<-15, 15>, 10>(begin
, end
, i
);
162 return boost::report_errors();