]>
Commit | Line | Data |
---|---|---|
92f5a8d4 TL |
1 | /*============================================================================= |
2 | Copyright (c) 2018 Nikita Kniazev | |
3 | ||
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 | =============================================================================*/ | |
7 | ||
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 | |
12 | #include <iosfwd> | |
13 | #include <limits> | |
14 | #include <sstream> | |
15 | ||
16 | #ifdef _MSC_VER | |
17 | # pragma warning(disable: 4127) // conditional expression is constant | |
18 | #endif | |
19 | ||
20 | template <int Min, int Max> | |
21 | struct custom_int | |
22 | { | |
23 | BOOST_DEFAULTED_FUNCTION(custom_int(), {}) | |
24 | BOOST_CONSTEXPR custom_int(int value) : value_(value) {} | |
25 | ||
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_; } | |
30 | ||
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_--; } | |
39 | ||
40 | custom_int operator+() { return +value_; } | |
41 | custom_int operator-() { return -value_; } | |
42 | ||
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_; } | |
49 | ||
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_; | |
54 | } | |
55 | ||
56 | BOOST_STATIC_CONSTEXPR int max = Max; | |
57 | BOOST_STATIC_CONSTEXPR int min = Min; | |
58 | ||
59 | private: | |
60 | int value_; | |
61 | }; | |
62 | ||
63 | namespace utils { | |
64 | ||
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; }; | |
69 | ||
70 | } | |
71 | ||
72 | namespace std { | |
73 | ||
74 | template <int Min, int Max> | |
75 | class numeric_limits<custom_int<Min, Max> > : public numeric_limits<int> | |
76 | { | |
77 | public: | |
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; | |
84 | }; | |
85 | ||
86 | } | |
87 | ||
88 | namespace qi = boost::spirit::qi; | |
89 | ||
90 | template <typename T, int Base, int MaxDigits> | |
91 | void test_overflow_handling(char const* begin, char const* end, int i) | |
92 | { | |
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, | |
96 | "test prerequest"); | |
97 | int initial = Base - i % Base; // just a 'random' non-equal to i number | |
98 | T x(initial); | |
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) { | |
102 | BOOST_TEST(r); | |
103 | BOOST_TEST(it == end); | |
104 | BOOST_TEST_EQ(x, i); | |
105 | } | |
106 | else { | |
107 | BOOST_TEST(!r); | |
108 | BOOST_TEST(it == begin); | |
109 | } | |
110 | } | |
111 | ||
112 | template <typename T, int Base> | |
113 | void test_unparsed_digits_are_not_consumed(char const* it, char const* end, int i) | |
114 | { | |
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 | |
121 | T x(initial); | |
122 | bool r = qi::extract_int<T, Base, 1, 1>::call(it, end, x); | |
123 | BOOST_TEST(r); | |
124 | if (-Base < i && i < Base) { | |
125 | BOOST_TEST(it == end); | |
126 | BOOST_TEST_EQ(x, i); | |
127 | } | |
128 | else { | |
129 | BOOST_TEST_EQ(end - it, (end - begin) - 1 - has_sign); | |
130 | BOOST_TEST_EQ(x, i / Base); | |
131 | } | |
132 | } | |
133 | ||
134 | template <typename T, int Base> | |
135 | void test_ignore_overflow_digits(char const* it, char const* end, int i) | |
136 | { | |
137 | // TODO: Check accumulating too? | |
138 | if (i < 0) return; // extract_int does not support IgnoreOverflowDigits | |
139 | ||
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 | |
143 | T x(initial); | |
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); | |
147 | BOOST_TEST_EQ(x, i); | |
148 | } | |
149 | else { | |
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); | |
153 | if (Base == 10) | |
154 | BOOST_TEST_EQ(it - begin, std::numeric_limits<T>::digits10 + has_sign); | |
155 | int expected = i; | |
156 | for (char const* p = it; p < end; ++p) expected /= Base; | |
157 | BOOST_TEST_EQ(x, expected); | |
158 | } | |
159 | } | |
160 | ||
161 | template <typename T, int Base> | |
162 | void run_tests(char const* begin, char const* end, int i) | |
163 | { | |
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); | |
172 | } | |
173 | ||
174 | int main() | |
175 | { | |
176 | for (int i = -30; i <= 30; ++i) { | |
177 | std::ostringstream oss; | |
178 | oss << i; | |
179 | std::string s = oss.str(); | |
180 | char const* begin = s.data(), *const end = begin + s.size(); | |
181 | ||
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); | |
188 | } | |
189 | ||
190 | return boost::report_errors(); | |
191 | } |