Decouple appender from back_insert_iterator

This commit is contained in:
Victor Zverovich 2024-01-02 20:56:51 -08:00
parent 242bcaec04
commit c9d233c0a4
3 changed files with 40 additions and 30 deletions

View File

@ -11,7 +11,7 @@
#include <cstddef> // std::byte #include <cstddef> // std::byte
#include <cstdio> // std::FILE #include <cstdio> // std::FILE
#include <cstring> // std::strlen #include <cstring> // std::strlen
#include <iterator> // std::back_insert_iterator #include <iterator> // DEPRECATED!
#include <limits> // std::numeric_limits #include <limits> // std::numeric_limits
#include <string> #include <string>
#include <type_traits> #include <type_traits>
@ -1099,16 +1099,27 @@ using has_formatter =
// An output iterator that appends to a buffer. // An output iterator that appends to a buffer.
// It is used to reduce symbol sizes for the common case. // It is used to reduce symbol sizes for the common case.
class appender : public std::back_insert_iterator<detail::buffer<char>> { class appender {
using base = std::back_insert_iterator<detail::buffer<char>>; private:
detail::buffer<char>* buffer_;
friend auto get_container(appender app) -> detail::buffer<char>& {
return *app.buffer_;
}
public: public:
using std::back_insert_iterator<detail::buffer<char>>::back_insert_iterator; using difference_type = ptrdiff_t;
appender(base it) noexcept : base(it) {}
FMT_UNCHECKED_ITERATOR(appender); FMT_UNCHECKED_ITERATOR(appender);
auto operator++() noexcept -> appender& { return *this; } appender(detail::buffer<char>& buf) : buffer_(&buf) {}
auto operator++(int) noexcept -> appender { return *this; }
auto operator=(char c) -> appender& {
buffer_->push_back(c);
return *this;
}
auto operator*() -> appender& {return *this;}
auto operator++() -> appender& { return *this; }
auto operator++(int) -> appender { return *this; }
}; };
namespace detail { namespace detail {
@ -1545,6 +1556,8 @@ template <typename...> using void_t = void;
template <typename It, typename T, typename Enable = void> template <typename It, typename T, typename Enable = void>
struct is_output_iterator : std::false_type {}; struct is_output_iterator : std::false_type {};
template <> struct is_output_iterator<appender, char> : std::true_type {};
template <typename It, typename T> template <typename It, typename T>
struct is_output_iterator< struct is_output_iterator<
It, T, It, T,

View File

@ -262,6 +262,14 @@ inline auto ctzll(uint64_t x) -> int {
FMT_END_NAMESPACE FMT_END_NAMESPACE
#endif #endif
namespace std {
template <>
struct iterator_traits<fmt::appender> {
using value_type = void;
using iterator_category = std::output_iterator_tag;
};
}
FMT_BEGIN_NAMESPACE FMT_BEGIN_NAMESPACE
namespace detail { namespace detail {

View File

@ -11,7 +11,6 @@
#include "fmt/core.h" #include "fmt/core.h"
#include <algorithm> // std::copy_n
#include <climits> // INT_MAX #include <climits> // INT_MAX
#include <cstring> // std::strlen #include <cstring> // std::strlen
#include <functional> // std::equal_to #include <functional> // std::equal_to
@ -33,6 +32,11 @@ using testing::Return;
# error core-test includes format.h # error core-test includes format.h
#endif #endif
fmt::appender copy(fmt::string_view s, fmt::appender out) {
for (char c : s) *out++ = c;
return out;
}
TEST(string_view_test, value_type) { TEST(string_view_test, value_type) {
static_assert(std::is_same<string_view::value_type, char>::value, ""); static_assert(std::is_same<string_view::value_type, char>::value, "");
} }
@ -102,16 +106,6 @@ TEST(core_test, is_output_iterator) {
} }
TEST(core_test, buffer_appender) { TEST(core_test, buffer_appender) {
// back_insert_iterator is not default-constructible before C++20, so
// buffer_appender can only be default-constructible when back_insert_iterator
// is.
static_assert(
std::is_default_constructible<
std::back_insert_iterator<fmt::detail::buffer<char>>>::value ==
std::is_default_constructible<
fmt::detail::buffer_appender<char>>::value,
"");
#ifdef __cpp_lib_ranges #ifdef __cpp_lib_ranges
static_assert(std::output_iterator<fmt::detail::buffer_appender<char>, char>); static_assert(std::output_iterator<fmt::detail::buffer_appender<char>, char>);
#endif #endif
@ -297,8 +291,7 @@ template <typename Char> struct formatter<test_struct, Char> {
} }
auto format(test_struct, format_context& ctx) const -> decltype(ctx.out()) { auto format(test_struct, format_context& ctx) const -> decltype(ctx.out()) {
auto test = string_view("test"); return copy("test", ctx.out());
return std::copy_n(test.data(), test.size(), ctx.out());
} }
}; };
FMT_END_NAMESPACE FMT_END_NAMESPACE
@ -619,8 +612,7 @@ template <> struct formatter<const_formattable> {
auto format(const const_formattable&, format_context& ctx) auto format(const const_formattable&, format_context& ctx)
-> decltype(ctx.out()) { -> decltype(ctx.out()) {
auto test = string_view("test"); return copy("test", ctx.out());
return std::copy_n(test.data(), test.size(), ctx.out());
} }
}; };
@ -631,8 +623,7 @@ template <> struct formatter<nonconst_formattable> {
auto format(nonconst_formattable&, format_context& ctx) auto format(nonconst_formattable&, format_context& ctx)
-> decltype(ctx.out()) { -> decltype(ctx.out()) {
auto test = string_view("test"); return copy("test", ctx.out());
return std::copy_n(test.data(), test.size(), ctx.out());
} }
}; };
FMT_END_NAMESPACE FMT_END_NAMESPACE
@ -653,8 +644,7 @@ template <> struct formatter<convertible_to_pointer_formattable> {
auto format(convertible_to_pointer_formattable, format_context& ctx) const auto format(convertible_to_pointer_formattable, format_context& ctx) const
-> decltype(ctx.out()) { -> decltype(ctx.out()) {
auto test = string_view("test"); return copy("test", ctx.out());
return std::copy_n(test.data(), test.size(), ctx.out());
} }
}; };
FMT_END_NAMESPACE FMT_END_NAMESPACE
@ -732,7 +722,7 @@ template <> struct formatter<convertible_to_int> {
} }
auto format(convertible_to_int, format_context& ctx) const auto format(convertible_to_int, format_context& ctx) const
-> decltype(ctx.out()) { -> decltype(ctx.out()) {
return std::copy_n("foo", 3, ctx.out()); return copy("foo", ctx.out());
} }
}; };
@ -742,7 +732,7 @@ template <> struct formatter<convertible_to_cstring> {
} }
auto format(convertible_to_cstring, format_context& ctx) const auto format(convertible_to_cstring, format_context& ctx) const
-> decltype(ctx.out()) { -> decltype(ctx.out()) {
return std::copy_n("bar", 3, ctx.out()); return copy("bar", ctx.out());
} }
}; };
FMT_END_NAMESPACE FMT_END_NAMESPACE
@ -853,8 +843,7 @@ template <> struct formatter<its_a_trap> {
} }
auto format(its_a_trap, format_context& ctx) const -> decltype(ctx.out()) { auto format(its_a_trap, format_context& ctx) const -> decltype(ctx.out()) {
auto s = string_view("42"); return copy("42", ctx.out());
return std::copy(s.begin(), s.end(), ctx.out());
} }
}; };
FMT_END_NAMESPACE FMT_END_NAMESPACE