Improve scan prototype

This commit is contained in:
Victor Zverovich 2024-01-14 08:51:33 -08:00
parent ca37503f93
commit e420a58f24
2 changed files with 59 additions and 23 deletions

View File

@ -18,71 +18,73 @@
TEST(scan_test, read_text) {
fmt::string_view s = "foo";
auto end = fmt::scan(s, "foo");
auto end = fmt::scan_to(s, "foo");
EXPECT_EQ(end, s.end());
EXPECT_THROW_MSG(fmt::scan("fob", "foo"), fmt::format_error, "invalid input");
EXPECT_THROW_MSG(fmt::scan_to("fob", "foo"), fmt::format_error,
"invalid input");
}
TEST(scan_test, read_int) {
int n = 0;
fmt::scan("42", "{}", n);
fmt::scan_to("42", "{}", n);
EXPECT_EQ(n, 42);
fmt::scan("-42", "{}", n);
fmt::scan_to("-42", "{}", n);
EXPECT_EQ(n, -42);
fmt::scan("42", "{:}", n);
fmt::scan_to("42", "{:}", n);
EXPECT_EQ(n, 42);
EXPECT_THROW_MSG(fmt::scan(std::to_string(INT_MAX + 1u), "{}", n),
EXPECT_THROW_MSG(fmt::scan_to(std::to_string(INT_MAX + 1u), "{}", n),
fmt::format_error, "number is too big");
}
TEST(scan_test, read_longlong) {
long long n = 0;
fmt::scan("42", "{}", n);
fmt::scan_to("42", "{}", n);
EXPECT_EQ(n, 42);
fmt::scan("-42", "{}", n);
fmt::scan_to("-42", "{}", n);
EXPECT_EQ(n, -42);
}
TEST(scan_test, read_uint) {
unsigned n = 0;
fmt::scan("42", "{}", n);
fmt::scan_to("42", "{}", n);
EXPECT_EQ(n, 42);
EXPECT_THROW_MSG(fmt::scan("-42", "{}", n), fmt::format_error,
EXPECT_THROW_MSG(fmt::scan_to("-42", "{}", n), fmt::format_error,
"invalid input");
}
TEST(scan_test, read_ulonglong) {
unsigned long long n = 0;
fmt::scan("42", "{}", n);
fmt::scan_to("42", "{}", n);
EXPECT_EQ(n, 42);
EXPECT_THROW_MSG(fmt::scan("-42", "{}", n), fmt::format_error,
EXPECT_THROW_MSG(fmt::scan_to("-42", "{}", n), fmt::format_error,
"invalid input");
}
TEST(scan_test, read_hex) {
unsigned n = 0;
fmt::scan("2a", "{:x}", n);
fmt::scan_to("2a", "{:x}", n);
EXPECT_EQ(n, 42);
auto num_digits = std::numeric_limits<unsigned>::digits / 4;
EXPECT_THROW_MSG(fmt::scan(fmt::format("1{:0{}}", 0, num_digits), "{:x}", n),
fmt::format_error, "number is too big");
EXPECT_THROW_MSG(
fmt::scan_to(fmt::format("1{:0{}}", 0, num_digits), "{:x}", n),
fmt::format_error, "number is too big");
}
TEST(scan_test, read_string) {
std::string s;
fmt::scan("foo", "{}", s);
fmt::scan_to("foo", "{}", s);
EXPECT_EQ(s, "foo");
}
TEST(scan_test, read_string_view) {
fmt::string_view s;
fmt::scan("foo", "{}", s);
fmt::scan_to("foo", "{}", s);
EXPECT_EQ(s, "foo");
}
TEST(scan_test, separator) {
int n1 = 0, n2 = 0;
fmt::scan("10 20", "{} {}", n1, n2);
fmt::scan_to("10 20", "{} {}", n1, n2);
EXPECT_EQ(n1, 10);
EXPECT_EQ(n2, 20);
}
@ -115,28 +117,28 @@ template <> struct scanner<num> {
TEST(scan_test, read_custom) {
auto n = num();
fmt::scan("42", "{}", n);
fmt::scan_to("42", "{}", n);
EXPECT_EQ(n.value, 42);
}
TEST(scan_test, invalid_format) {
EXPECT_THROW_MSG(fmt::scan("", "{}"), fmt::format_error,
EXPECT_THROW_MSG(fmt::scan_to("", "{}"), fmt::format_error,
"argument index out of range");
EXPECT_THROW_MSG(fmt::scan("", "{"), fmt::format_error,
EXPECT_THROW_MSG(fmt::scan_to("", "{"), fmt::format_error,
"invalid format string");
}
TEST(scan_test, example) {
std::string key;
int value = 0;
fmt::scan("answer = 42", "{} = {}", key, value);
fmt::scan_to("answer = 42", "{} = {}", key, value);
EXPECT_EQ(key, "answer");
EXPECT_EQ(value, 42);
}
TEST(scan_test, end_of_input) {
int value = 0;
fmt::scan("", "{}", value);
fmt::scan_to("", "{}", value);
}
#if FMT_USE_FCNTL

View File

@ -650,6 +650,40 @@ auto scan_to(string_view input, string_view fmt, T&... args)
return input.begin() + (buf.begin().base() - input.data());
}
template <typename T>
class scan_value {
private:
T value_;
public:
scan_value(T value) : value_(std::move(value)) {}
const T& value() const {
return value_;
}
};
// A rudimentary version of std::expected for testing the API shape.
template <typename T>
class expected {
private:
T value_;
public:
expected(T value) : value_(std::move(value)) {}
};
template <typename T>
using scan_result = expected<scan_value<T>>;
template <typename T>
auto scan(string_view input, string_view fmt) -> scan_result<T> {
static_assert(std::is_same<remove_cvref_t<T>, T>::value, "");
auto value = T();
scan_to(input, fmt, value);
return scan_value<T>(std::move(value));
}
template <typename InputRange, typename... T,
FMT_ENABLE_IF(!std::is_convertible<InputRange, string_view>::value)>
auto scan(InputRange&& input, string_view fmt, T&... args)