Add default_arg_formatter

This commit is contained in:
Victor Zverovich 2020-06-08 07:23:18 -07:00
parent f2c9cb6244
commit 936a1833c2
4 changed files with 121 additions and 44 deletions

View File

@ -1435,7 +1435,7 @@ OutputIt write_int(OutputIt out, int num_digits, string_view prefix,
template <typename StrChar, typename Char, typename OutputIt>
OutputIt write(OutputIt out, basic_string_view<StrChar> s,
const basic_format_specs<Char>& specs = {}) {
const basic_format_specs<Char>& specs) {
auto data = s.data();
auto size = s.size();
if (specs.precision >= 0 && to_unsigned(specs.precision) < size)
@ -1671,13 +1671,100 @@ template <typename T> struct is_integral : std::is_integral<T> {};
template <> struct is_integral<int128_t> : std::true_type {};
template <> struct is_integral<uint128_t> : std::true_type {};
template <typename Char, typename OutputIt>
OutputIt write(OutputIt out, monostate) {
FMT_ASSERT(false, "");
return out;
}
template <typename Char, typename OutputIt,
FMT_ENABLE_IF(!std::is_same<Char, char>::value)>
OutputIt write(OutputIt out, string_view value) {
auto it = reserve(out, value.size());
it = copy_str<Char>(value.begin(), value.end(), it);
return base_iterator(out, it);
}
template <typename Char, typename OutputIt>
OutputIt write(OutputIt out, basic_string_view<Char> value) {
auto it = reserve(out, value.size());
it = std::copy(value.begin(), value.end(), it);
return base_iterator(out, it);
}
template <typename Char, typename OutputIt, typename T,
FMT_ENABLE_IF(is_integral<T>::value &&
!std::is_same<T, bool>::value &&
!std::is_same<T, Char>::value)>
OutputIt write(OutputIt out, T value) {
auto abs_value = static_cast<uint32_or_64_or_128_t<T>>(value);
bool negative = is_negative(value);
// Don't do -abs_value since it trips unsigned-integer-overflow sanitizer.
if (negative) abs_value = ~abs_value + 1;
int num_digits = count_digits(abs_value);
auto it = reserve(out, (negative ? 1 : 0) + static_cast<size_t>(num_digits));
if (negative) *it++ = static_cast<Char>('-');
it = format_decimal<Char>(it, abs_value, num_digits);
return base_iterator(out, it);
}
template <typename Char, typename OutputIt>
OutputIt write(OutputIt out, bool value) {
return write<Char>(out, string_view(value ? "true" : "false"));
}
template <typename Char, typename OutputIt>
OutputIt write(OutputIt out, Char value) {
auto it = reserve(out, 1);
*it++ = value;
return base_iterator(out, it);
}
template <typename Char, typename OutputIt>
OutputIt write(OutputIt out, const Char* value) {
if (!value) {
FMT_THROW(format_error("string pointer is null"));
} else {
auto length = std::char_traits<Char>::length(value);
out = write(out, basic_string_view<Char>(value, length));
}
return out;
}
template <typename Char, typename OutputIt>
OutputIt write(OutputIt out, const void* value) {
return write_ptr<Char>(out, to_uintptr(value), nullptr);
}
// An argument visitor that formats the argument and writes it via the output
// iterator. It's a class and not a generic lambda for compatibility with C++11.
template <typename OutputIt, typename Char> struct default_arg_formatter {
using context = basic_format_context<OutputIt, Char>;
OutputIt out;
basic_format_args<context> args;
locale_ref loc;
template <typename T> OutputIt operator()(T value) {
return write<Char>(out, value);
}
OutputIt operator()(typename basic_format_arg<context>::handle handle) {
Char s[] = {Char('}')};
basic_format_parse_context<Char> parse_ctx(basic_string_view<Char>(s, 1));
basic_format_context<OutputIt, Char> format_ctx(out, args, loc);
handle.format(parse_ctx, format_ctx);
return format_ctx.out();
}
};
template <typename OutputIt, typename Char,
typename ErrorHandler = error_handler>
class arg_formatter_base {
public:
using iterator = OutputIt;
using char_type = Char;
using format_specs = basic_format_specs<char_type>;
using format_specs = basic_format_specs<Char>;
private:
iterator out_;
@ -1693,21 +1780,9 @@ class arg_formatter_base {
using reserve_iterator = remove_reference_t<decltype(
detail::reserve(std::declval<iterator&>(), 0))>;
// Writes a decimal integer.
template <typename Int> void write_decimal(Int value) {
auto abs_value = static_cast<uint32_or_64_or_128_t<Int>>(value);
bool negative = is_negative(value);
// Don't do -abs_value since it trips unsigned-integer-overflow sanitizer.
if (negative) abs_value = ~abs_value + 1;
int num_digits = count_digits(abs_value);
auto&& it = reserve((negative ? 1 : 0) + static_cast<size_t>(num_digits));
if (negative) *it++ = static_cast<char_type>('-');
it = format_decimal<char_type>(it, abs_value, num_digits);
}
template <typename T> void write_int(T value, const format_specs& spec) {
using uint_type = uint32_or_64_or_128_t<T>;
int_writer<iterator, char_type, uint_type> w(out_, locale_, value, spec);
int_writer<iterator, Char, uint_type> w(out_, locale_, value, spec);
handle_int_type_spec(spec.type, w);
out_ = w.out;
}
@ -1717,18 +1792,17 @@ class arg_formatter_base {
*it++ = value;
}
template <typename Ch, FMT_ENABLE_IF(std::is_same<Ch, char_type>::value)>
template <typename Ch, FMT_ENABLE_IF(std::is_same<Ch, Char>::value)>
void write(Ch value) {
auto&& it = reserve(1);
*it++ = value;
out_ = detail::write<Char>(out_, value);
}
void write(string_view value) {
auto&& it = reserve(value.size());
it = copy_str<char_type>(value.begin(), value.end(), it);
it = copy_str<Char>(value.begin(), value.end(), it);
}
void write(wstring_view value) {
static_assert(std::is_same<char_type, wchar_t>::value, "");
static_assert(std::is_same<Char, wchar_t>::value, "");
auto&& it = reserve(value.size());
it = std::copy(value.begin(), value.end(), it);
}
@ -1739,7 +1813,7 @@ class arg_formatter_base {
? count_code_points(basic_string_view<Ch>(s, size))
: 0;
out_ = write_padded(out_, specs, size, width, [=](reserve_iterator it) {
return copy_str<char_type>(s, s + size, it);
return copy_str<Char>(s, s + size, it);
});
}
@ -1754,9 +1828,9 @@ class arg_formatter_base {
struct char_spec_handler : ErrorHandler {
arg_formatter_base& formatter;
char_type value;
Char value;
char_spec_handler(arg_formatter_base& f, char_type val)
char_spec_handler(arg_formatter_base& f, Char val)
: formatter(f), value(val) {}
void on_int() {
@ -1773,9 +1847,9 @@ class arg_formatter_base {
struct cstring_spec_handler : error_handler {
arg_formatter_base& formatter;
const char_type* value;
const Char* value;
cstring_spec_handler(arg_formatter_base& f, const char_type* val)
cstring_spec_handler(arg_formatter_base& f, const Char* val)
: formatter(f), value(val) {}
void on_string() { formatter.write(value); }
@ -1787,11 +1861,13 @@ class arg_formatter_base {
format_specs* specs() { return specs_; }
void write(bool value) {
string_view sv(value ? "true" : "false");
specs_ ? write(sv, *specs_) : write(sv);
if (specs_)
write(string_view(value ? "true" : "false"), *specs_);
else
out_ = detail::write<Char>(out_, value);
}
void write(const char_type* value) {
void write(const Char* value) {
if (!value) {
FMT_THROW(format_error("string pointer is null"));
} else {
@ -1815,13 +1891,13 @@ class arg_formatter_base {
if (specs_)
write_int(value, *specs_);
else
write_decimal(value);
out_ = detail::write<Char>(out_, value);
return out_;
}
iterator operator()(char_type value) {
iterator operator()(Char value) {
handle_char_specs(specs_,
char_spec_handler(*this, static_cast<char_type>(value)));
char_spec_handler(*this, static_cast<Char>(value)));
return out_;
}
@ -1841,13 +1917,13 @@ class arg_formatter_base {
return out_;
}
iterator operator()(const char_type* value) {
iterator operator()(const Char* value) {
if (!specs_) return write(value), out_;
handle_cstring_type_spec(specs_->type, cstring_spec_handler(*this, value));
return out_;
}
iterator operator()(basic_string_view<char_type> value) {
iterator operator()(basic_string_view<Char> value) {
if (specs_) {
check_string_type_spec(specs_->type, error_handler());
write(value, *specs_);
@ -3125,14 +3201,15 @@ typename Context::iterator vformat_to(
typename ArgFormatter::iterator out, basic_string_view<Char> format_str,
basic_format_args<Context> args,
detail::locale_ref loc = detail::locale_ref()) {
if (format_str.size() == 2 && detail::equal2(format_str.data(), "{}")) {
auto arg = args.get(0);
if (!arg) detail::error_handler().on_error("argument not found");
using it = typename ArgFormatter::iterator;
return visit_format_arg(
detail::default_arg_formatter<it, Char>{out, args, loc}, arg);
}
detail::format_handler<ArgFormatter, Char, Context> h(out, format_str, args,
loc);
if (format_str.size() == 2 && detail::equal2(format_str.data(), "{}")) {
auto arg = detail::get_arg(h.context, 0);
return visit_format_arg(
ArgFormatter(h.context, &h.parse_context, nullptr, &format_str[1]),
arg);
}
detail::parse_format_string<false>(format_str, h);
return h.context.out();
}

View File

@ -80,7 +80,7 @@ template <typename T, typename Char> class is_streamable {
// Write the content of buf to os.
template <typename Char>
void write(std::basic_ostream<Char>& os, buffer<Char>& buf) {
void write_buffer(std::basic_ostream<Char>& os, buffer<Char>& buf) {
const Char* buf_data = buf.data();
using unsigned_streamsize = std::make_unsigned<std::streamsize>::type;
unsigned_streamsize size = buf.size();
@ -144,7 +144,7 @@ void vprint(std::basic_ostream<Char>& os, basic_string_view<Char> format_str,
basic_format_args<buffer_context<type_identity_t<Char>>> args) {
basic_memory_buffer<Char> buffer;
detail::vformat_to(buffer, format_str, args);
detail::write(os, buffer);
detail::write_buffer(os, buffer);
}
/**

View File

@ -714,7 +714,7 @@ inline int vfprintf(
basic_format_args<basic_printf_context_t<type_identity_t<Char>>> args) {
basic_memory_buffer<Char> buffer;
vprintf(buffer, to_string_view(format), args);
detail::write(os, buffer);
detail::write_buffer(os, buffer);
return static_cast<int>(buffer.size());
}

View File

@ -141,7 +141,7 @@ TEST(OStreamTest, WriteToOStream) {
fmt::memory_buffer buffer;
const char* foo = "foo";
buffer.append(foo, foo + std::strlen(foo));
fmt::detail::write(os, buffer);
fmt::detail::write_buffer(os, buffer);
EXPECT_EQ("foo", os.str());
}
@ -178,7 +178,7 @@ TEST(OStreamTest, WriteToOStreamMaxSize) {
data += n;
size -= n;
} while (size != 0);
fmt::detail::write(os, buffer);
fmt::detail::write_buffer(os, buffer);
}
TEST(OStreamTest, Join) {