mirror of
https://github.com/fmtlib/fmt.git
synced 2025-07-30 02:37:36 +02:00
Implement empty format specs
This commit is contained in:
@ -29,6 +29,8 @@ TEST(scan_test, read_int) {
|
|||||||
EXPECT_EQ(n, 42);
|
EXPECT_EQ(n, 42);
|
||||||
fmt::scan("-42", "{}", n);
|
fmt::scan("-42", "{}", n);
|
||||||
EXPECT_EQ(n, -42);
|
EXPECT_EQ(n, -42);
|
||||||
|
fmt::scan("42", "{:}", n);
|
||||||
|
EXPECT_EQ(n, 42);
|
||||||
EXPECT_THROW_MSG(fmt::scan(std::to_string(INT_MAX + 1u), "{}", n),
|
EXPECT_THROW_MSG(fmt::scan(std::to_string(INT_MAX + 1u), "{}", n),
|
||||||
fmt::format_error, "number is too big");
|
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 {
|
class string_scan_buffer : public scan_buffer {
|
||||||
private:
|
private:
|
||||||
void consume() override {}
|
void consume() override {}
|
||||||
@ -330,7 +333,7 @@ template <typename Context> class basic_scan_arg {
|
|||||||
long long* long_long_value_;
|
long long* long_long_value_;
|
||||||
unsigned long long* ulong_long_value_;
|
unsigned long long* ulong_long_value_;
|
||||||
std::string* string_;
|
std::string* string_;
|
||||||
fmt::string_view* string_view_;
|
string_view* string_view_;
|
||||||
detail::custom_scan_arg<Context> custom_;
|
detail::custom_scan_arg<Context> custom_;
|
||||||
// TODO: more types
|
// TODO: more types
|
||||||
};
|
};
|
||||||
@ -356,7 +359,7 @@ template <typename Context> class basic_scan_arg {
|
|||||||
: type_(scan_type::ulong_long_type), ulong_long_value_(&value) {}
|
: type_(scan_type::ulong_long_type), ulong_long_value_(&value) {}
|
||||||
FMT_CONSTEXPR basic_scan_arg(std::string& value)
|
FMT_CONSTEXPR basic_scan_arg(std::string& value)
|
||||||
: type_(scan_type::string_type), 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) {}
|
: type_(scan_type::string_view_type), string_view_(&value) {}
|
||||||
template <typename T>
|
template <typename T>
|
||||||
FMT_CONSTEXPR basic_scan_arg(T& value) : type_(scan_type::custom_type) {
|
FMT_CONSTEXPR basic_scan_arg(T& value) : type_(scan_type::custom_type) {
|
||||||
@ -423,8 +426,8 @@ class scan_context {
|
|||||||
scan_args args_;
|
scan_args args_;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
using iterator = detail::scan_buffer::iterator;
|
using iterator = detail::scan_iterator;
|
||||||
using sentinel = detail::scan_buffer::sentinel;
|
using sentinel = detail::scan_sentinel;
|
||||||
|
|
||||||
explicit FMT_CONSTEXPR scan_context(detail::scan_buffer& buf, scan_args args)
|
explicit FMT_CONSTEXPR scan_context(detail::scan_buffer& buf, scan_args args)
|
||||||
: buf_(buf), args_(args) {}
|
: buf_(buf), args_(args) {}
|
||||||
@ -456,109 +459,93 @@ const char* parse_scan_specs(const char* begin, const char* end,
|
|||||||
return begin;
|
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 {
|
struct default_arg_scanner {
|
||||||
using iterator = scan_buffer::iterator;
|
scan_iterator it;
|
||||||
using sentinel = scan_buffer::sentinel;
|
|
||||||
|
|
||||||
iterator begin;
|
template <typename T>
|
||||||
|
FMT_INLINE auto operator()(T&& value) -> scan_iterator {
|
||||||
template <typename T = unsigned>
|
return read(it, value);
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// An argument scanner with format specifiers.
|
||||||
struct arg_scanner {
|
struct arg_scanner {
|
||||||
using iterator = scan_buffer::iterator;
|
scan_iterator it;
|
||||||
using sentinel = scan_buffer::sentinel;
|
|
||||||
|
|
||||||
iterator begin;
|
|
||||||
const format_specs<>& specs;
|
const format_specs<>& specs;
|
||||||
|
|
||||||
template <typename T, FMT_ENABLE_IF(std::is_integral<T>::value)>
|
template <typename T>
|
||||||
auto operator()(T&&) -> iterator {
|
auto operator()(T&& value) -> scan_iterator {
|
||||||
// TODO: implement
|
// TODO: handle specs
|
||||||
return begin;
|
return read(it, value);
|
||||||
}
|
|
||||||
|
|
||||||
template <typename T, FMT_ENABLE_IF(!std::is_integral<T>::value)>
|
|
||||||
auto operator()(T&&) -> iterator {
|
|
||||||
// TODO: implement
|
|
||||||
return begin;
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -611,8 +598,7 @@ struct scan_handler : error_handler {
|
|||||||
auto specs = format_specs<>();
|
auto specs = format_specs<>();
|
||||||
begin = parse_scan_specs(begin, end, specs, arg.type());
|
begin = parse_scan_specs(begin, end, specs, arg.type());
|
||||||
if (begin == end || *begin != '}') on_error("missing '}' in format string");
|
if (begin == end || *begin != '}') on_error("missing '}' in format string");
|
||||||
auto s = arg_scanner{scan_ctx_.begin(), specs};
|
scan_ctx_.advance_to(arg.visit(arg_scanner{scan_ctx_.begin(), specs}));
|
||||||
scan_ctx_.advance_to(arg.visit(s));
|
|
||||||
return begin;
|
return begin;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user