mirror of
https://github.com/fmtlib/fmt.git
synced 2024-11-09 21:00:06 +00:00
Implement empty format specs
This commit is contained in:
parent
c068c7c622
commit
d33731d202
@ -29,6 +29,8 @@ TEST(scan_test, read_int) {
|
||||
EXPECT_EQ(n, 42);
|
||||
fmt::scan("-42", "{}", n);
|
||||
EXPECT_EQ(n, -42);
|
||||
fmt::scan("42", "{:}", n);
|
||||
EXPECT_EQ(n, 42);
|
||||
EXPECT_THROW_MSG(fmt::scan(std::to_string(INT_MAX + 1u), "{}", n),
|
||||
fmt::format_error, "number is too big");
|
||||
}
|
||||
|
188
test/scan.h
188
test/scan.h
@ -147,6 +147,9 @@ class scan_buffer {
|
||||
}
|
||||
};
|
||||
|
||||
using scan_iterator = scan_buffer::iterator;
|
||||
using scan_sentinel = scan_buffer::sentinel;
|
||||
|
||||
class string_scan_buffer : public scan_buffer {
|
||||
private:
|
||||
void consume() override {}
|
||||
@ -330,7 +333,7 @@ template <typename Context> class basic_scan_arg {
|
||||
long long* long_long_value_;
|
||||
unsigned long long* ulong_long_value_;
|
||||
std::string* string_;
|
||||
fmt::string_view* string_view_;
|
||||
string_view* string_view_;
|
||||
detail::custom_scan_arg<Context> custom_;
|
||||
// TODO: more types
|
||||
};
|
||||
@ -356,7 +359,7 @@ template <typename Context> class basic_scan_arg {
|
||||
: type_(scan_type::ulong_long_type), ulong_long_value_(&value) {}
|
||||
FMT_CONSTEXPR basic_scan_arg(std::string& value)
|
||||
: type_(scan_type::string_type), string_(&value) {}
|
||||
FMT_CONSTEXPR basic_scan_arg(fmt::string_view& value)
|
||||
FMT_CONSTEXPR basic_scan_arg(string_view& value)
|
||||
: type_(scan_type::string_view_type), string_view_(&value) {}
|
||||
template <typename T>
|
||||
FMT_CONSTEXPR basic_scan_arg(T& value) : type_(scan_type::custom_type) {
|
||||
@ -423,8 +426,8 @@ class scan_context {
|
||||
scan_args args_;
|
||||
|
||||
public:
|
||||
using iterator = detail::scan_buffer::iterator;
|
||||
using sentinel = detail::scan_buffer::sentinel;
|
||||
using iterator = detail::scan_iterator;
|
||||
using sentinel = detail::scan_sentinel;
|
||||
|
||||
explicit FMT_CONSTEXPR scan_context(detail::scan_buffer& buf, scan_args args)
|
||||
: buf_(buf), args_(args) {}
|
||||
@ -456,109 +459,93 @@ const char* parse_scan_specs(const char* begin, const char* end,
|
||||
return begin;
|
||||
}
|
||||
|
||||
template <typename T, FMT_ENABLE_IF(std::is_unsigned<T>::value)>
|
||||
auto read(scan_iterator it, T& value) -> scan_iterator {
|
||||
if (it == scan_sentinel()) return it;
|
||||
char c = *it;
|
||||
if (c < '0' || c > '9') throw_format_error("invalid input");
|
||||
|
||||
int num_digits = 0;
|
||||
T n = 0, prev = 0;
|
||||
char prev_digit = c;
|
||||
do {
|
||||
prev = n;
|
||||
n = n * 10 + static_cast<unsigned>(c - '0');
|
||||
prev_digit = c;
|
||||
c = *++it;
|
||||
++num_digits;
|
||||
if (c < '0' || c > '9') break;
|
||||
} while (it != scan_sentinel());
|
||||
|
||||
// Check overflow.
|
||||
if (num_digits <= std::numeric_limits<int>::digits10) {
|
||||
value = n;
|
||||
return it;
|
||||
}
|
||||
unsigned max = to_unsigned((std::numeric_limits<int>::max)());
|
||||
if (num_digits == std::numeric_limits<int>::digits10 + 1 &&
|
||||
prev * 10ull + unsigned(prev_digit - '0') <= max) {
|
||||
value = n;
|
||||
} else {
|
||||
throw_format_error("number is too big");
|
||||
}
|
||||
return it;
|
||||
}
|
||||
|
||||
template <typename T, FMT_ENABLE_IF(std::is_signed<T>::value)>
|
||||
auto read(scan_iterator it, T& value) -> scan_iterator {
|
||||
bool negative = it != scan_sentinel() && *it == '-';
|
||||
if (negative) {
|
||||
++it;
|
||||
if (it == scan_sentinel()) throw_format_error("invalid input");
|
||||
}
|
||||
using unsigned_type = typename std::make_unsigned<T>::type;
|
||||
unsigned_type abs_value = 0;
|
||||
it = read(it, abs_value);
|
||||
auto n = static_cast<T>(abs_value);
|
||||
value = negative ? -n : n;
|
||||
return it;
|
||||
}
|
||||
|
||||
auto read(scan_iterator it, std::string& value) -> scan_iterator {
|
||||
while (it != scan_sentinel() && *it != ' ') value.push_back(*it++);
|
||||
return it;
|
||||
}
|
||||
|
||||
auto read(scan_iterator it, string_view& value) -> scan_iterator {
|
||||
auto range = to_contiguous(it);
|
||||
// This could also be checked at compile time in scan.
|
||||
if (!range) throw_format_error("string_view requires contiguous input");
|
||||
auto p = range.begin;
|
||||
while (p != range.end && *p != ' ') ++p;
|
||||
size_t size = to_unsigned(p - range.begin);
|
||||
value = {range.begin, size};
|
||||
return advance(it, size);
|
||||
}
|
||||
|
||||
auto read(scan_iterator it, monostate) -> scan_iterator {
|
||||
return it;
|
||||
}
|
||||
|
||||
// An argument scanner that uses the default format, e.g. decimal for integers.
|
||||
struct default_arg_scanner {
|
||||
using iterator = scan_buffer::iterator;
|
||||
using sentinel = scan_buffer::sentinel;
|
||||
scan_iterator it;
|
||||
|
||||
iterator begin;
|
||||
|
||||
template <typename T = unsigned>
|
||||
auto read_uint(iterator it, T& value) -> iterator {
|
||||
if (it == sentinel()) return it;
|
||||
char c = *it;
|
||||
if (c < '0' || c > '9') throw_format_error("invalid input");
|
||||
|
||||
int num_digits = 0;
|
||||
T n = 0, prev = 0;
|
||||
char prev_digit = c;
|
||||
do {
|
||||
prev = n;
|
||||
n = n * 10 + static_cast<unsigned>(c - '0');
|
||||
prev_digit = c;
|
||||
c = *++it;
|
||||
++num_digits;
|
||||
if (c < '0' || c > '9') break;
|
||||
} while (it != sentinel());
|
||||
|
||||
// Check overflow.
|
||||
if (num_digits <= std::numeric_limits<int>::digits10) {
|
||||
value = n;
|
||||
return it;
|
||||
}
|
||||
unsigned max = to_unsigned((std::numeric_limits<int>::max)());
|
||||
if (num_digits == std::numeric_limits<int>::digits10 + 1 &&
|
||||
prev * 10ull + unsigned(prev_digit - '0') <= max) {
|
||||
value = n;
|
||||
} else {
|
||||
throw_format_error("number is too big");
|
||||
}
|
||||
return it;
|
||||
}
|
||||
|
||||
template <typename T = int>
|
||||
auto read_int(iterator it, T& value) -> iterator {
|
||||
bool negative = it != sentinel() && *it == '-';
|
||||
if (negative) {
|
||||
++it;
|
||||
if (it == sentinel()) throw_format_error("invalid input");
|
||||
}
|
||||
using unsigned_type = typename std::make_unsigned<T>::type;
|
||||
unsigned_type abs_value = 0;
|
||||
it = read_uint<unsigned_type>(it, abs_value);
|
||||
auto n = static_cast<T>(abs_value);
|
||||
value = negative ? -n : n;
|
||||
return it;
|
||||
}
|
||||
|
||||
auto operator()(int& value) -> iterator {
|
||||
return read_int(begin, value);
|
||||
}
|
||||
auto operator()(unsigned& value) -> iterator {
|
||||
return read_uint(begin, value);
|
||||
}
|
||||
auto operator()(long long& value) -> iterator {
|
||||
return read_int<long long>(begin, value);
|
||||
}
|
||||
auto operator()(unsigned long long& value) -> iterator {
|
||||
return read_uint<unsigned long long>(begin, value);
|
||||
}
|
||||
auto operator()(std::string& value) -> iterator {
|
||||
iterator it = begin;
|
||||
while (it != sentinel() && *it != ' ') value.push_back(*it++);
|
||||
return it;
|
||||
}
|
||||
auto operator()(fmt::string_view& value) -> iterator {
|
||||
auto range = to_contiguous(begin);
|
||||
// This could also be checked at compile time in scan.
|
||||
if (!range) throw_format_error("string_view requires contiguous input");
|
||||
auto p = range.begin;
|
||||
while (p != range.end && *p != ' ') ++p;
|
||||
size_t size = to_unsigned(p - range.begin);
|
||||
value = {range.begin, size};
|
||||
return advance(begin, size);
|
||||
}
|
||||
auto operator()(monostate) -> iterator {
|
||||
return begin;
|
||||
template <typename T>
|
||||
FMT_INLINE auto operator()(T&& value) -> scan_iterator {
|
||||
return read(it, value);
|
||||
}
|
||||
};
|
||||
|
||||
// An argument scanner with format specifiers.
|
||||
struct arg_scanner {
|
||||
using iterator = scan_buffer::iterator;
|
||||
using sentinel = scan_buffer::sentinel;
|
||||
|
||||
iterator begin;
|
||||
scan_iterator it;
|
||||
const format_specs<>& specs;
|
||||
|
||||
template <typename T, FMT_ENABLE_IF(std::is_integral<T>::value)>
|
||||
auto operator()(T&&) -> iterator {
|
||||
// TODO: implement
|
||||
return begin;
|
||||
}
|
||||
|
||||
template <typename T, FMT_ENABLE_IF(!std::is_integral<T>::value)>
|
||||
auto operator()(T&&) -> iterator {
|
||||
// TODO: implement
|
||||
return begin;
|
||||
template <typename T>
|
||||
auto operator()(T&& value) -> scan_iterator {
|
||||
// TODO: handle specs
|
||||
return read(it, value);
|
||||
}
|
||||
};
|
||||
|
||||
@ -611,8 +598,7 @@ struct scan_handler : error_handler {
|
||||
auto specs = format_specs<>();
|
||||
begin = parse_scan_specs(begin, end, specs, arg.type());
|
||||
if (begin == end || *begin != '}') on_error("missing '}' in format string");
|
||||
auto s = arg_scanner{scan_ctx_.begin(), specs};
|
||||
scan_ctx_.advance_to(arg.visit(s));
|
||||
scan_ctx_.advance_to(arg.visit(arg_scanner{scan_ctx_.begin(), specs}));
|
||||
return begin;
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user