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/x3/support/numeric_utils/extract_int.hpp>
10 #include <cmath> // for std::pow
16 # pragma warning(disable: 4127) // conditional expression is constant
19 template <int Min
, int Max
>
22 custom_int() = default;
23 constexpr custom_int(int value
) : value_
{value
} {}
25 custom_int
operator+(custom_int x
) const { return value_
+ x
.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_
; }
30 custom_int
& operator+=(custom_int x
) { value_
+= x
.value_
; return *this; }
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++() { ++value_
; return *this; }
35 custom_int
& operator--() { --value_
; return *this; }
36 custom_int
operator++(int) { return value_
++; }
37 custom_int
operator--(int) { return value_
--; }
39 custom_int
operator+() { return +value_
; }
40 custom_int
operator-() { return -value_
; }
42 bool operator< (custom_int x
) const { return value_
< x
.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_
; }
49 template <typename Char
, typename Traits
>
50 friend std::basic_ostream
<Char
, Traits
>&
51 operator<<(std::basic_ostream
<Char
, Traits
>& os
, custom_int x
) {
52 return os
<< x
.value_
;
55 static constexpr int max
= Max
;
56 static constexpr int min
= Min
;
64 template <int Min
, int Max
> struct digits
;
65 template <> struct digits
<-9, 9> { static constexpr int r2
= 3, r10
= 1; };
66 template <> struct digits
<-10, 10> { static constexpr int r2
= 3, r10
= 1; };
67 template <> struct digits
<-15, 15> { static constexpr int r2
= 3, r10
= 1; };
73 template <int Min
, int Max
>
74 class numeric_limits
<custom_int
<Min
, Max
>> : public numeric_limits
<int>
77 static constexpr custom_int
<Min
, Max
> max() noexcept
{ return Max
; }
78 static constexpr custom_int
<Min
, Max
> min() noexcept
{ return Min
; }
79 static constexpr custom_int
<Min
, Max
> lowest() noexcept
{ return min(); }
80 static_assert(numeric_limits
<int>::radix
== 2, "hardcoded for digits of radix 2");
81 static constexpr int digits
= utils::digits
<Min
, Max
>::r2
;
82 static constexpr int digits10
= utils::digits
<Min
, Max
>::r10
;
87 namespace x3
= boost::spirit::x3
;
89 template <typename T
, int Base
, int MaxDigits
>
90 void test_overflow_handling(char const* begin
, char const* end
, int i
)
92 // Check that parser fails on overflow
93 static_assert(std::numeric_limits
<T
>::is_bounded
, "tests prerequest");
94 BOOST_ASSERT_MSG(MaxDigits
== -1 || static_cast<int>(std::pow(float(Base
), MaxDigits
)) > T::max
,
96 int initial
= Base
- i
% Base
; // just a 'random' non-equal to i number
98 char const* it
= begin
;
99 bool r
= x3::extract_int
<T
, Base
, 1, MaxDigits
>::call(it
, end
, x
);
100 if (T::min
<= i
&& i
<= T::max
) {
102 BOOST_TEST(it
== end
);
106 if (MaxDigits
== -1) // TODO: Looks like a regression. See #430
109 BOOST_TEST(it
== begin
);
113 template <typename T
, int Base
>
114 void test_unparsed_digits_are_not_consumed(char const* it
, char const* end
, int i
)
116 // Check that unparsed digits are not consumed
117 static_assert(T::min
<= -Base
+1, "test prerequest");
118 static_assert(T::max
>= Base
-1, "test prerequest");
119 bool has_sign
= *it
== '+' || *it
== '-';
121 int initial
= Base
- i
% Base
; // just a 'random' non-equal to i number
123 bool r
= x3::extract_int
<T
, Base
, 1, 1>::call(it
, end
, x
);
125 if (-Base
< i
&& i
< Base
) {
126 BOOST_TEST(it
== end
);
130 BOOST_TEST_EQ(end
- it
, len
- 1 - has_sign
);
131 BOOST_TEST_EQ(x
, i
/ Base
);
135 template <typename T
, int Base
>
136 void run_tests(char const* begin
, char const* end
, int i
)
138 // Check that parser fails on overflow
139 test_overflow_handling
<T
, Base
, -1>(begin
, end
, i
);
140 // Check that MaxDigits > digits10 behave like MaxDigits=-1
141 test_overflow_handling
<T
, Base
, 2>(begin
, end
, i
);
142 // Check that unparsed digits are not consumed
143 test_unparsed_digits_are_not_consumed
<T
, Base
>(begin
, end
, i
);
148 for (int i
= -30; i
<= 30; ++i
) {
150 std::snprintf(s
, 4, "%d", i
);
151 auto begin
= &s
[0], end
= begin
+ std::strlen(begin
);
153 // log(Base, abs(MinOrMax) + 1) == digits
154 run_tests
<custom_int
<-9, 9>, 10>(begin
, end
, i
);
155 // (MinOrMax % Base) == 0
156 run_tests
<custom_int
<-10, 10>, 10>(begin
, end
, i
);
157 // (MinOrMax % Base) != 0
158 run_tests
<custom_int
<-15, 15>, 10>(begin
, end
, i
);
161 return boost::report_errors();