Purge basic_writer

This commit is contained in:
Victor Zverovich 2020-05-07 14:15:12 -07:00
parent 2f05054dd3
commit c06851456d
5 changed files with 130 additions and 273 deletions

View File

@ -1100,8 +1100,6 @@ struct formatter<std::chrono::duration<Rep, Period>, Char> {
// is not specified.
basic_memory_buffer<Char> buf;
auto out = std::back_inserter(buf);
using range = internal::output_range<decltype(ctx.out()), Char>;
internal::basic_writer<range> w(range(ctx.out()));
internal::handle_dynamic_spec<internal::width_checker>(specs.width,
width_ref, ctx);
internal::handle_dynamic_spec<internal::precision_checker>(
@ -1115,8 +1113,8 @@ struct formatter<std::chrono::duration<Rep, Period>, Char> {
f.precision = precision;
parse_chrono_format(begin, end, f);
}
w.write(buf.data(), buf.size(), specs);
return w.out();
return internal::write(
ctx.out(), basic_string_view<Char>(buf.data(), buf.size()), specs);
}
};

View File

@ -383,9 +383,7 @@ OutputIt format_default(OutputIt out, T value) {
template <typename Char, typename OutputIt>
OutputIt format_default(OutputIt out, double value) {
basic_writer<buffer_range<char>> w(out);
w.write(value);
return w.out();
return internal::write(out, value);
}
template <typename Char, typename OutputIt>

View File

@ -1561,6 +1561,16 @@ template <typename OutputIt, typename Char, typename UInt> struct int_writer {
}
};
template <typename Char, typename OutputIt>
OutputIt write_bytes(OutputIt out, string_view bytes,
const basic_format_specs<Char>& specs) {
using iterator = remove_reference_t<decltype(reserve(out, 0))>;
return write_padded(out, specs, bytes.size(), [bytes](iterator it) {
const char* data = bytes.data();
return copy_str<Char>(data, data + bytes.size(), it);
});
}
template <typename Char, typename OutputIt>
OutputIt write_nonfinite(OutputIt out, bool isinf,
const basic_format_specs<Char>& specs,
@ -1577,6 +1587,71 @@ OutputIt write_nonfinite(OutputIt out, bool isinf,
});
}
template <typename Char, typename OutputIt, typename T,
FMT_ENABLE_IF(std::is_floating_point<T>::value)>
OutputIt write(OutputIt out, T value, basic_format_specs<Char> specs = {},
locale_ref loc = {}) {
if (const_check(!is_supported_floating_point(value))) return out;
float_specs fspecs = parse_float_type_spec(specs);
fspecs.sign = specs.sign;
if (std::signbit(value)) { // value < 0 is false for NaN so use signbit.
fspecs.sign = sign::minus;
value = -value;
} else if (fspecs.sign == sign::minus) {
fspecs.sign = sign::none;
}
if (!std::isfinite(value))
return write_nonfinite(out, std::isinf(value), specs, fspecs);
if (specs.align == align::numeric && fspecs.sign) {
auto it = reserve(out, 1);
*it++ = static_cast<Char>(data::signs[fspecs.sign]);
out = base_iterator(out, it);
fspecs.sign = sign::none;
if (specs.width != 0) --specs.width;
}
memory_buffer buffer;
if (fspecs.format == float_format::hex) {
if (fspecs.sign) buffer.push_back(data::signs[fspecs.sign]);
snprintf_float(promote_float(value), specs.precision, fspecs, buffer);
return write_bytes(out, {buffer.data(), buffer.size()}, specs);
}
int precision = specs.precision >= 0 || !specs.type ? specs.precision : 6;
if (fspecs.format == float_format::exp) {
if (precision == max_value<int>())
FMT_THROW(format_error("number is too big"));
else
++precision;
}
if (const_check(std::is_same<T, float>())) fspecs.binary32 = true;
fspecs.use_grisu = use_grisu<T>();
int exp = format_float(promote_float(value), precision, fspecs, buffer);
fspecs.precision = precision;
Char point =
fspecs.locale ? decimal_point<Char>(loc) : static_cast<Char>('.');
float_writer<Char> w(buffer.data(), static_cast<int>(buffer.size()), exp,
fspecs, point);
return write_padded<align::right>(out, specs, w.size(), w);
}
template <typename StrChar, typename Char, typename OutputIt>
OutputIt write(OutputIt out, basic_string_view<StrChar> s,
const basic_format_specs<Char>& specs = {}) {
auto data = s.data();
auto size = s.size();
if (specs.precision >= 0 && to_unsigned(specs.precision) < size)
size = code_point_index(s, to_unsigned(specs.precision));
auto width = specs.width != 0
? count_code_points(basic_string_view<StrChar>(data, size))
: 0;
using iterator = remove_reference_t<decltype(reserve(out, 0))>;
return write_padded(out, specs, size, width, [=](iterator it) {
return copy_str<Char>(data, data + size, it);
});
}
template <typename Char, typename OutputIt, typename UIntPtr>
OutputIt write_ptr(OutputIt out, UIntPtr value,
const basic_format_specs<Char>* specs) {
@ -1592,27 +1667,21 @@ OutputIt write_ptr(OutputIt out, UIntPtr value,
: base_iterator(out, write(reserve(out, size)));
}
template <typename Char, typename OutputIt>
OutputIt write_bytes(OutputIt out, string_view bytes,
const basic_format_specs<Char>& specs) {
using iterator = remove_reference_t<decltype(reserve(out, 0))>;
return write_padded(out, specs, bytes.size(), [bytes](iterator it) {
const char* data = bytes.data();
return copy_str<Char>(data, data + bytes.size(), it);
});
}
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 {};
// This template provides operations for formatting and writing data into a
// character range.
template <typename Range> class basic_writer {
template <typename Range, typename ErrorHandler = internal::error_handler>
class arg_formatter_base {
public:
using char_type = typename Range::value_type;
using iterator = typename Range::iterator;
using format_specs = basic_format_specs<char_type>;
protected:
private:
iterator out_; // Output iterator.
locale_ref locale_;
format_specs* specs_;
// Attempts to reserve space for n extra characters in the output range.
// Returns a pointer to the reserved range or a reference to out_.
@ -1620,6 +1689,27 @@ template <typename Range> class basic_writer {
return internal::reserve(out_, n);
}
using reserve_iterator = remove_reference_t<decltype(
internal::reserve(std::declval<iterator&>(), 0))>;
struct char_writer {
char_type value;
size_t size() const { return 1; }
template <typename It> It operator()(It it) const {
*it++ = value;
return it;
}
};
void write_char(char_type value) {
if (specs_)
out_ = write_padded(out_, *specs_, 1, char_writer{value});
else
write(value);
}
// 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);
@ -1632,15 +1722,6 @@ template <typename Range> class basic_writer {
it = format_decimal<char_type>(it, abs_value, num_digits);
}
using reserve_iterator = remove_reference_t<decltype(
internal::reserve(std::declval<iterator&>(), 0))>;
public:
explicit basic_writer(Range out, locale_ref loc = locale_ref())
: out_(out.begin()), locale_(loc) {}
iterator& out() { return out_; }
void write(int value) { write_decimal(value); }
void write(long value) { write_decimal(value); }
void write(long long value) { write_decimal(value); }
@ -1661,55 +1742,6 @@ template <typename Range> class basic_writer {
out_ = w.out;
}
template <typename T, FMT_ENABLE_IF(std::is_floating_point<T>::value)>
void write(T value, format_specs specs = {}) {
if (const_check(!is_supported_floating_point(value))) return;
float_specs fspecs = parse_float_type_spec(specs);
fspecs.sign = specs.sign;
if (std::signbit(value)) { // value < 0 is false for NaN so use signbit.
fspecs.sign = sign::minus;
value = -value;
} else if (fspecs.sign == sign::minus) {
fspecs.sign = sign::none;
}
if (!std::isfinite(value)) {
out_ = write_nonfinite(out_, std::isinf(value), specs, fspecs);
return;
}
if (specs.align == align::numeric && fspecs.sign) {
auto&& it = reserve(1);
*it++ = static_cast<char_type>(data::signs[fspecs.sign]);
fspecs.sign = sign::none;
if (specs.width != 0) --specs.width;
}
memory_buffer buffer;
if (fspecs.format == float_format::hex) {
if (fspecs.sign) buffer.push_back(data::signs[fspecs.sign]);
snprintf_float(promote_float(value), specs.precision, fspecs, buffer);
out_ = write_bytes(out_, {buffer.data(), buffer.size()}, specs);
return;
}
int precision = specs.precision >= 0 || !specs.type ? specs.precision : 6;
if (fspecs.format == float_format::exp) {
if (precision == max_value<int>())
FMT_THROW(format_error("number is too big"));
else
++precision;
}
if (const_check(std::is_same<T, float>())) fspecs.binary32 = true;
fspecs.use_grisu = use_grisu<T>();
int exp = format_float(promote_float(value), precision, fspecs, buffer);
fspecs.precision = precision;
char_type point = fspecs.locale ? decimal_point<char_type>(locale_)
: static_cast<char_type>('.');
float_writer<char_type> w(buffer.data(), static_cast<int>(buffer.size()),
exp, fspecs, point);
out_ = write_padded<align::right>(out_, specs, w.size(), w);
}
void write(char value) {
auto&& it = reserve(1);
*it++ = value;
@ -1743,49 +1775,7 @@ template <typename Range> class basic_writer {
template <typename Char>
void write(basic_string_view<Char> s, const format_specs& specs = {}) {
const Char* data = s.data();
std::size_t size = s.size();
if (specs.precision >= 0 && to_unsigned(specs.precision) < size)
size = code_point_index(s, to_unsigned(specs.precision));
write(data, size, specs);
}
};
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 Range, typename ErrorHandler = internal::error_handler>
class arg_formatter_base : private basic_writer<Range> {
public:
using char_type = typename Range::value_type;
using iterator = typename Range::iterator;
using format_specs = basic_format_specs<char_type>;
private:
using writer_type = basic_writer<Range>;
using writer_type::out_;
format_specs* specs_;
using writer_type::write;
using writer_type::write_int;
struct char_writer {
char_type value;
size_t size() const { return 1; }
template <typename It> It operator()(It it) const {
*it++ = value;
return it;
}
};
void write_char(char_type value) {
if (specs_)
out() = write_padded(out(), *specs_, 1, char_writer{value});
else
write(value);
out_ = internal::write(out_, s, specs);
}
void write_pointer(const void* p) {
@ -1793,8 +1783,7 @@ class arg_formatter_base : private basic_writer<Range> {
}
protected:
using writer_type::out;
writer_type& writer() { return *this; }
iterator out() { return out_; }
format_specs* specs() { return specs_; }
void write(bool value) {
@ -1814,7 +1803,7 @@ class arg_formatter_base : private basic_writer<Range> {
public:
arg_formatter_base(Range r, format_specs* s, locale_ref loc)
: basic_writer<Range>(r, loc), specs_(s) {}
: out_(r.begin()), locale_(loc), specs_(s) {}
iterator operator()(monostate) {
FMT_ASSERT(false, "invalid argument type");
@ -1844,8 +1833,9 @@ class arg_formatter_base : private basic_writer<Range> {
template <typename T, FMT_ENABLE_IF(std::is_floating_point<T>::value)>
iterator operator()(T value) {
auto specs = specs_ ? *specs_ : format_specs();
if (const_check(is_supported_floating_point(value)))
write(value, specs_ ? *specs_ : format_specs());
out_ = internal::write(out_, value, specs, locale_);
else
FMT_ASSERT(false, "unsupported float argument type");
return out();
@ -2503,7 +2493,7 @@ template <typename Handler, typename Char> struct id_adapter {
template <bool IS_CONSTEXPR, typename Char, typename Handler>
FMT_CONSTEXPR void parse_format_string(basic_string_view<Char> format_str,
Handler&& handler) {
struct pfs_writer {
struct writer {
FMT_CONSTEXPR void operator()(const Char* begin, const Char* end) {
if (begin == end) return;
for (;;) {
@ -3336,21 +3326,21 @@ extern template FMT_API char thousands_sep_impl<char>(locale_ref loc);
extern template FMT_API wchar_t thousands_sep_impl<wchar_t>(locale_ref loc);
extern template FMT_API char decimal_point_impl(locale_ref loc);
extern template FMT_API wchar_t decimal_point_impl(locale_ref loc);
extern template
int format_float<double>(double value, int precision, float_specs specs,
buffer<char>& buf);
extern template
int format_float<long double>(long double value, int precision,
float_specs specs, buffer<char>& buf);
extern template int format_float<double>(double value, int precision,
float_specs specs, buffer<char>& buf);
extern template int format_float<long double>(long double value, int precision,
float_specs specs,
buffer<char>& buf);
int snprintf_float(float value, int precision, float_specs specs,
buffer<char>& buf) = delete;
extern template
int snprintf_float<double>(double value, int precision, float_specs specs,
buffer<char>& buf);
extern template
int snprintf_float<long double>(long double value, int precision,
float_specs specs, buffer<char>& buf);
}
extern template int snprintf_float<double>(double value, int precision,
float_specs specs,
buffer<char>& buf);
extern template int snprintf_float<long double>(long double value,
int precision,
float_specs specs,
buffer<char>& buf);
} // namespace internal
#endif
template <typename S, typename Char = char_t<S>,

View File

@ -328,7 +328,9 @@ template <typename T> struct printf_formatter {
}
};
/** This template formats data and writes the output to a writer. */
/**
This template formats data and writes the output through an output iterator.
*/
template <typename OutputIt, typename Char> class basic_printf_context {
public:
/** The character type for the output. */
@ -358,9 +360,8 @@ template <typename OutputIt, typename Char> class basic_printf_context {
public:
/**
\rst
Constructs a ``printf_context`` object. References to the arguments and
the writer are stored in the context object so make sure they have
appropriate lifetimes.
Constructs a ``printf_context`` object. References to the arguments are
stored in the context object so make sure they have appropriate lifetimes.
\endrst
*/
basic_printf_context(OutputIt out, basic_string_view<char_type> format_str,

View File

@ -35,8 +35,6 @@
#include "util.h"
#undef ERROR
#undef min
#undef max
using std::size_t;
@ -47,7 +45,6 @@ using fmt::memory_buffer;
using fmt::string_view;
using fmt::wmemory_buffer;
using fmt::wstring_view;
using fmt::internal::basic_writer;
using fmt::internal::max_value;
using testing::Return;
@ -102,47 +99,6 @@ void std_format(long double value, std::wstring& result) {
result = buffer;
}
#endif
// Checks if writing value to BasicWriter<Char> produces the same result
// as writing it to std::basic_ostringstream<Char>.
template <typename Char, typename T>
::testing::AssertionResult check_write(const T& value, const char* type) {
fmt::basic_memory_buffer<Char> buffer;
using range = fmt::buffer_range<Char>;
basic_writer<range> writer(buffer);
writer.write(value);
std::basic_string<Char> actual = to_string(buffer);
std::basic_string<Char> expected;
std_format(value, expected);
if (expected == actual) return ::testing::AssertionSuccess();
return ::testing::AssertionFailure()
<< "Value of: (Writer<" << type << ">() << value).str()\n"
<< " Actual: " << actual << "\n"
<< "Expected: " << expected << "\n";
}
struct AnyWriteChecker {
template <typename T>
::testing::AssertionResult operator()(const char*, const T& value) const {
::testing::AssertionResult result = check_write<char>(value, "char");
return result ? check_write<wchar_t>(value, "wchar_t") : result;
}
};
template <typename Char> struct WriteChecker {
template <typename T>
::testing::AssertionResult operator()(const char*, const T& value) const {
return check_write<Char>(value, "char");
}
};
// Checks if writing value to BasicWriter produces the same result
// as writing it to std::ostringstream both for char and wchar_t.
#define CHECK_WRITE(value) EXPECT_PRED_FORMAT1(AnyWriteChecker(), value)
#define CHECK_WRITE_CHAR(value) EXPECT_PRED_FORMAT1(WriteChecker<char>(), value)
#define CHECK_WRITE_WCHAR(value) \
EXPECT_PRED_FORMAT1(WriteChecker<wchar_t>(), value)
} // namespace
struct uint32_pair {
@ -479,92 +435,6 @@ TEST(StringViewTest, Ctor) {
EXPECT_EQ(4u, string_view(std::string("defg")).size());
}
TEST(WriterTest, WriteInt) {
CHECK_WRITE(42);
CHECK_WRITE(-42);
CHECK_WRITE(static_cast<short>(12));
CHECK_WRITE(34u);
CHECK_WRITE(std::numeric_limits<int>::min());
CHECK_WRITE(max_value<int>());
CHECK_WRITE(max_value<unsigned>());
}
TEST(WriterTest, WriteLong) {
CHECK_WRITE(56l);
CHECK_WRITE(78ul);
CHECK_WRITE(std::numeric_limits<long>::min());
CHECK_WRITE(max_value<long>());
CHECK_WRITE(max_value<unsigned long>());
}
TEST(WriterTest, WriteLongLong) {
CHECK_WRITE(56ll);
CHECK_WRITE(78ull);
CHECK_WRITE(std::numeric_limits<long long>::min());
CHECK_WRITE(max_value<long long>());
CHECK_WRITE(max_value<unsigned long long>());
}
TEST(WriterTest, WriteDouble) {
CHECK_WRITE(4.2);
CHECK_WRITE(-4.2);
auto min = std::numeric_limits<double>::min();
auto max = max_value<double>();
if (fmt::internal::use_grisu<double>()) {
EXPECT_EQ("2.2250738585072014e-308", fmt::format("{}", min));
EXPECT_EQ("1.7976931348623157e+308", fmt::format("{}", max));
} else {
CHECK_WRITE(min);
CHECK_WRITE(max);
}
}
TEST(WriterTest, WriteLongDouble) {
CHECK_WRITE(4.2l);
CHECK_WRITE_CHAR(-4.2l);
std::wstring str;
std_format(4.2l, str);
if (str[0] != '-')
CHECK_WRITE_WCHAR(-4.2l);
else
fmt::print("warning: long double formatting with std::swprintf is broken");
auto min = std::numeric_limits<long double>::min();
auto max = max_value<long double>();
if (fmt::internal::use_grisu<long double>()) {
EXPECT_EQ("2.2250738585072014e-308", fmt::format("{}", min));
EXPECT_EQ("1.7976931348623157e+308", fmt::format("{}", max));
} else {
CHECK_WRITE(min);
CHECK_WRITE(max);
}
}
TEST(WriterTest, WriteDoubleAtBufferBoundary) {
memory_buffer buf;
for (int i = 0; i < 100; ++i) fmt::format_to(buf, "{}", 1.23456789);
}
TEST(WriterTest, WriteDoubleWithFilledBuffer) {
memory_buffer buf;
// Fill the buffer.
for (int i = 0; i < fmt::inline_buffer_size; ++i) fmt::format_to(buf, " ");
fmt::format_to(buf, "{}", 1.2);
fmt::string_view sv(buf.data(), buf.size());
sv.remove_prefix(fmt::inline_buffer_size);
EXPECT_EQ("1.2", sv);
}
TEST(WriterTest, WriteChar) { CHECK_WRITE('a'); }
TEST(WriterTest, WriteWideChar) { CHECK_WRITE_WCHAR(L'a'); }
TEST(WriterTest, WriteString) {
CHECK_WRITE_CHAR("abc");
CHECK_WRITE_WCHAR("abc");
}
TEST(WriterTest, WriteWideString) { CHECK_WRITE_WCHAR(L"abc"); }
TEST(FormatToTest, FormatWithoutArgs) {
std::string s;
fmt::format_to(std::back_inserter(s), "test");