Skip to content

Commit

Permalink
Decouple appender from back_insert_iterator
Browse files Browse the repository at this point in the history
  • Loading branch information
vitaut committed Jan 3, 2024
1 parent 4e43a46 commit 2b177b5
Show file tree
Hide file tree
Showing 3 changed files with 38 additions and 28 deletions.
23 changes: 18 additions & 5 deletions include/fmt/core.h
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
#include <cstddef> // std::byte
#include <cstdio> // std::FILE
#include <cstring> // std::strlen
#include <iterator> // std::back_insert_iterator
#include <iterator> // DEPRECATED!
#include <limits> // std::numeric_limits
#include <string>
#include <type_traits>
Expand Down Expand Up @@ -1099,14 +1099,25 @@ using has_formatter =

// An output iterator that appends to a buffer.
// It is used to reduce symbol sizes for the common case.
class appender : public std::back_insert_iterator<detail::buffer<char>> {
using base = std::back_insert_iterator<detail::buffer<char>>;
class appender {
private:
detail::buffer<char>* buffer_;

friend auto get_container(appender app) -> detail::buffer<char>& {
return *app.buffer_;
}

public:
using std::back_insert_iterator<detail::buffer<char>>::back_insert_iterator;
appender(base it) noexcept : base(it) {}
using difference_type = ptrdiff_t;
FMT_UNCHECKED_ITERATOR(appender);

appender(detail::buffer<char>& buf) : buffer_(&buf) {}

appender& operator=(char c) {
buffer_->push_back(c);
return *this;
}
appender& operator*() {return *this;}
auto operator++() noexcept -> appender& { return *this; }
auto operator++(int) noexcept -> appender { return *this; }
};
Expand Down Expand Up @@ -1545,6 +1556,8 @@ template <typename...> using void_t = void;
template <typename It, typename T, typename Enable = void>
struct is_output_iterator : std::false_type {};

template <> struct is_output_iterator<appender, char> : std::true_type {};

template <typename It, typename T>
struct is_output_iterator<
It, T,
Expand Down
8 changes: 8 additions & 0 deletions include/fmt/format.h
Original file line number Diff line number Diff line change
Expand Up @@ -262,6 +262,14 @@ inline auto ctzll(uint64_t x) -> int {
FMT_END_NAMESPACE
#endif

namespace std {
template <>
struct iterator_traits<fmt::appender> {
using value_type = void;
using iterator_category = std::output_iterator_tag;
};
}

FMT_BEGIN_NAMESPACE
namespace detail {

Expand Down
35 changes: 12 additions & 23 deletions test/core-test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@

#include "fmt/core.h"

#include <algorithm> // std::copy_n
#include <climits> // INT_MAX
#include <cstring> // std::strlen
#include <functional> // std::equal_to
Expand All @@ -33,6 +32,11 @@ using testing::Return;
# error core-test includes format.h
#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) {
static_assert(std::is_same<string_view::value_type, char>::value, "");
}
Expand Down Expand Up @@ -102,16 +106,6 @@ TEST(core_test, is_output_iterator) {
}

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
static_assert(std::output_iterator<fmt::detail::buffer_appender<char>, char>);
#endif
Expand Down Expand Up @@ -297,8 +291,7 @@ template <typename Char> struct formatter<test_struct, Char> {
}

auto format(test_struct, format_context& ctx) const -> decltype(ctx.out()) {
auto test = string_view("test");
return std::copy_n(test.data(), test.size(), ctx.out());
return copy("test", ctx.out());
}
};
FMT_END_NAMESPACE
Expand Down Expand Up @@ -619,8 +612,7 @@ template <> struct formatter<const_formattable> {

auto format(const const_formattable&, format_context& ctx)
-> decltype(ctx.out()) {
auto test = string_view("test");
return std::copy_n(test.data(), test.size(), ctx.out());
return copy("test", ctx.out());
}
};

Expand All @@ -631,8 +623,7 @@ template <> struct formatter<nonconst_formattable> {

auto format(nonconst_formattable&, format_context& ctx)
-> decltype(ctx.out()) {
auto test = string_view("test");
return std::copy_n(test.data(), test.size(), ctx.out());
return copy("test", ctx.out());
}
};
FMT_END_NAMESPACE
Expand All @@ -653,8 +644,7 @@ template <> struct formatter<convertible_to_pointer_formattable> {

auto format(convertible_to_pointer_formattable, format_context& ctx) const
-> decltype(ctx.out()) {
auto test = string_view("test");
return std::copy_n(test.data(), test.size(), ctx.out());
return copy("test", ctx.out());
}
};
FMT_END_NAMESPACE
Expand Down Expand Up @@ -732,7 +722,7 @@ template <> struct formatter<convertible_to_int> {
}
auto format(convertible_to_int, format_context& ctx) const
-> decltype(ctx.out()) {
return std::copy_n("foo", 3, ctx.out());
return copy("foo", ctx.out());
}
};

Expand All @@ -742,7 +732,7 @@ template <> struct formatter<convertible_to_cstring> {
}
auto format(convertible_to_cstring, format_context& ctx) const
-> decltype(ctx.out()) {
return std::copy_n("bar", 3, ctx.out());
return copy("bar", ctx.out());
}
};
FMT_END_NAMESPACE
Expand Down Expand Up @@ -853,8 +843,7 @@ template <> struct formatter<its_a_trap> {
}

auto format(its_a_trap, format_context& ctx) const -> decltype(ctx.out()) {
auto s = string_view("42");
return std::copy(s.begin(), s.end(), ctx.out());
return copy("42", ctx.out());
}
};
FMT_END_NAMESPACE
Expand Down

0 comments on commit 2b177b5

Please sign in to comment.