1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163
|
/*=============================================================================
Copyright (c) 2018 Nikita Kniazev
Distributed under the Boost Software License, Version 1.0. (See accompanying
file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
=============================================================================*/
#include <boost/spirit/home/x3/support/numeric_utils/extract_int.hpp>
#include <boost/core/lightweight_test.hpp>
#include <cmath> // for std::pow
#include <cstdio>
#include <iosfwd>
#include <limits>
#ifdef _MSC_VER
# pragma warning(disable: 4127) // conditional expression is constant
#endif
template <int Min, int Max>
struct custom_int
{
custom_int() = default;
constexpr custom_int(int value) : value_{value} {}
custom_int operator+(custom_int x) const { return value_ + x.value_; }
custom_int operator-(custom_int x) const { return value_ - x.value_; }
custom_int operator*(custom_int x) const { return value_ * x.value_; }
custom_int operator/(custom_int x) const { return value_ / x.value_; }
custom_int& operator+=(custom_int x) { value_ += x.value_; return *this; }
custom_int& operator-=(custom_int x) { value_ -= x.value_; return *this; }
custom_int& operator*=(custom_int x) { value_ *= x.value_; return *this; }
custom_int& operator/=(custom_int x) { value_ /= x.value_; return *this; }
custom_int& operator++() { ++value_; return *this; }
custom_int& operator--() { --value_; return *this; }
custom_int operator++(int) { return value_++; }
custom_int operator--(int) { return value_--; }
custom_int operator+() { return +value_; }
custom_int operator-() { return -value_; }
bool operator< (custom_int x) const { return value_ < x.value_; }
bool operator> (custom_int x) const { return value_ > x.value_; }
bool operator<=(custom_int x) const { return value_ <= x.value_; }
bool operator>=(custom_int x) const { return value_ >= x.value_; }
bool operator==(custom_int x) const { return value_ == x.value_; }
bool operator!=(custom_int x) const { return value_ != x.value_; }
template <typename Char, typename Traits>
friend std::basic_ostream<Char, Traits>&
operator<<(std::basic_ostream<Char, Traits>& os, custom_int x) {
return os << x.value_;
}
static constexpr int max = Max;
static constexpr int min = Min;
private:
int value_;
};
namespace utils {
template <int Min, int Max> struct digits;
template <> struct digits<-9, 9> { static constexpr int r2 = 3, r10 = 1; };
template <> struct digits<-10, 10> { static constexpr int r2 = 3, r10 = 1; };
template <> struct digits<-15, 15> { static constexpr int r2 = 3, r10 = 1; };
}
namespace std {
template <int Min, int Max>
class numeric_limits<custom_int<Min, Max>> : public numeric_limits<int>
{
public:
static constexpr custom_int<Min, Max> max() noexcept { return Max; }
static constexpr custom_int<Min, Max> min() noexcept { return Min; }
static constexpr custom_int<Min, Max> lowest() noexcept { return min(); }
static_assert(numeric_limits<int>::radix == 2, "hardcoded for digits of radix 2");
static constexpr int digits = utils::digits<Min, Max>::r2;
static constexpr int digits10 = utils::digits<Min, Max>::r10;
};
}
namespace x3 = boost::spirit::x3;
template <typename T, int Base, int MaxDigits>
void test_overflow_handling(char const* begin, char const* end, int i)
{
// Check that parser fails on overflow
static_assert(std::numeric_limits<T>::is_bounded, "tests prerequest");
BOOST_ASSERT_MSG(MaxDigits == -1 || static_cast<int>(std::pow(float(Base), MaxDigits)) > T::max,
"test prerequest");
int initial = Base - i % Base; // just a 'random' non-equal to i number
T x { initial };
char const* it = begin;
bool r = x3::extract_int<T, Base, 1, MaxDigits>::call(it, end, x);
if (T::min <= i && i <= T::max) {
BOOST_TEST(r);
BOOST_TEST(it == end);
BOOST_TEST_EQ(x, i);
}
else
if (MaxDigits == -1) // TODO: Looks like a regression. See #430
{
BOOST_TEST(!r);
BOOST_TEST(it == begin);
}
}
template <typename T, int Base>
void test_unparsed_digits_are_not_consumed(char const* it, char const* end, int i)
{
// Check that unparsed digits are not consumed
static_assert(T::min <= -Base+1, "test prerequest");
static_assert(T::max >= Base-1, "test prerequest");
bool has_sign = *it == '+' || *it == '-';
auto len = end - it;
int initial = Base - i % Base; // just a 'random' non-equal to i number
T x { initial };
bool r = x3::extract_int<T, Base, 1, 1>::call(it, end, x);
BOOST_TEST(r);
if (-Base < i && i < Base) {
BOOST_TEST(it == end);
BOOST_TEST_EQ(x, i);
}
else {
BOOST_TEST_EQ(end - it, len - 1 - has_sign);
BOOST_TEST_EQ(x, i / Base);
}
}
template <typename T, int Base>
void run_tests(char const* begin, char const* end, int i)
{
// Check that parser fails on overflow
test_overflow_handling<T, Base, -1>(begin, end, i);
// Check that MaxDigits > digits10 behave like MaxDigits=-1
test_overflow_handling<T, Base, 2>(begin, end, i);
// Check that unparsed digits are not consumed
test_unparsed_digits_are_not_consumed<T, Base>(begin, end, i);
}
int main()
{
for (int i = -30; i <= 30; ++i) {
char s[4];
std::snprintf(s, 4, "%d", i);
auto begin = &s[0], end = begin + std::strlen(begin);
// log(Base, abs(MinOrMax) + 1) == digits
run_tests<custom_int<-9, 9>, 10>(begin, end, i);
// (MinOrMax % Base) == 0
run_tests<custom_int<-10, 10>, 10>(begin, end, i);
// (MinOrMax % Base) != 0
run_tests<custom_int<-15, 15>, 10>(begin, end, i);
}
return boost::report_errors();
}
|