Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
253 changes: 0 additions & 253 deletions include/fmt/base.h
Original file line number Diff line number Diff line change
Expand Up @@ -1858,136 +1858,6 @@ class fixed_buffer_traits {
}
};

// A buffer that writes to an output iterator when flushed.
template <typename OutputIt, typename T, typename Traits = buffer_traits>
class iterator_buffer : public Traits, public buffer<T> {
Copy link
Contributor

@vitaut vitaut Mar 15, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was not suggesting to move iterator_buffer, just the implementation of the function you were modifying. Sorry if it wasn't clear enough.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If you move only the implementation, but not the interface, won't anything that include base.h but not format.h, get linker error? Besides, there is a header-only compilation option.

You can't just put it in the cpp, since the function is templated (depends on the template parameter OutputIt).

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No, only the consumers who need this will have to include format.h which is OK.

private:
OutputIt out_;
enum { buffer_size = 256 };
T data_[buffer_size];

static FMT_CONSTEXPR void grow(buffer<T>& buf, size_t) {
if (buf.size() == buffer_size) static_cast<iterator_buffer&>(buf).flush();
}

void flush() {
auto size = this->size();
this->clear();
const T* begin = data_;
const T* end = begin + this->limit(size);
while (begin != end) *out_++ = *begin++;
}

public:
explicit iterator_buffer(OutputIt out, size_t n = buffer_size)
: Traits(n), buffer<T>(grow, data_, 0, buffer_size), out_(out) {}
iterator_buffer(iterator_buffer&& other) noexcept
: Traits(other),
buffer<T>(grow, data_, 0, buffer_size),
out_(other.out_) {}
~iterator_buffer() {
// Don't crash if flush fails during unwinding.
FMT_TRY { flush(); }
FMT_CATCH(...) {}
}

auto out() -> OutputIt {
flush();
return out_;
}
auto count() const -> size_t { return Traits::count() + this->size(); }
};

template <typename T>
class iterator_buffer<T*, T, fixed_buffer_traits> : public fixed_buffer_traits,
public buffer<T> {
private:
T* out_;
enum { buffer_size = 256 };
T data_[buffer_size];

static FMT_CONSTEXPR void grow(buffer<T>& buf, size_t) {
if (buf.size() == buf.capacity())
static_cast<iterator_buffer&>(buf).flush();
}

void flush() {
size_t n = this->limit(this->size());
if (this->data() == out_) {
out_ += n;
this->set(data_, buffer_size);
}
this->clear();
}

public:
explicit iterator_buffer(T* out, size_t n = buffer_size)
: fixed_buffer_traits(n), buffer<T>(grow, out, 0, n), out_(out) {}
iterator_buffer(iterator_buffer&& other) noexcept
: fixed_buffer_traits(other),
buffer<T>(static_cast<iterator_buffer&&>(other)),
out_(other.out_) {
if (this->data() != out_) {
this->set(data_, buffer_size);
this->clear();
}
}
~iterator_buffer() { flush(); }

auto out() -> T* {
flush();
return out_;
}
auto count() const -> size_t {
return fixed_buffer_traits::count() + this->size();
}
};

template <typename T> class iterator_buffer<T*, T> : public buffer<T> {
public:
explicit iterator_buffer(T* out, size_t = 0)
: buffer<T>([](buffer<T>&, size_t) {}, out, 0, ~size_t()) {}

auto out() -> T* { return &*this->end(); }
};

template <typename Container>
class container_buffer : public buffer<typename Container::value_type> {
private:
using value_type = typename Container::value_type;

static FMT_CONSTEXPR void grow(buffer<value_type>& buf, size_t capacity) {
auto& self = static_cast<container_buffer&>(buf);
self.container.resize(capacity);
self.set(&self.container[0], capacity);
}

public:
Container& container;

explicit container_buffer(Container& c)
: buffer<value_type>(grow, c.size()), container(c) {}
};

// A buffer that writes to a container with the contiguous storage.
template <typename OutputIt>
class iterator_buffer<
OutputIt,
enable_if_t<is_back_insert_iterator<OutputIt>::value &&
is_contiguous<typename OutputIt::container_type>::value,
typename OutputIt::container_type::value_type>>
: public container_buffer<typename OutputIt::container_type> {
private:
using base = container_buffer<typename OutputIt::container_type>;

public:
explicit iterator_buffer(typename OutputIt::container_type& c) : base(c) {}
explicit iterator_buffer(OutputIt out, size_t = 0)
: base(get_container(out)) {}

auto out() -> OutputIt { return OutputIt(this->container); }
};

// A buffer that counts the number of code units written discarding the output.
template <typename T = char> class counting_buffer : public buffer<T> {
private:
Expand All @@ -2012,37 +1882,6 @@ template <typename T = char> class counting_buffer : public buffer<T> {
template <typename T>
struct is_back_insert_iterator<basic_appender<T>> : std::true_type {};

template <typename It, typename Enable = std::true_type>
struct is_buffer_appender : std::false_type {};
template <typename It>
struct is_buffer_appender<
It, bool_constant<
is_back_insert_iterator<It>::value &&
std::is_base_of<buffer<typename It::container_type::value_type>,
typename It::container_type>::value>>
: std::true_type {};

// Maps an output iterator to a buffer.
template <typename T, typename OutputIt,
FMT_ENABLE_IF(!is_buffer_appender<OutputIt>::value)>
auto get_buffer(OutputIt out) -> iterator_buffer<OutputIt, T> {
return iterator_buffer<OutputIt, T>(out);
}
template <typename T, typename OutputIt,
FMT_ENABLE_IF(is_buffer_appender<OutputIt>::value)>
auto get_buffer(OutputIt out) -> buffer<T>& {
return get_container(out);
}

template <typename Buf, typename OutputIt>
auto get_iterator(Buf& buf, OutputIt) -> decltype(buf.out()) {
return buf.out();
}
template <typename T, typename OutputIt>
auto get_iterator(buffer<T>&, OutputIt out) -> OutputIt {
return out;
}

// This type is intentionally undefined, only used for errors.
template <typename T, typename Char> struct type_is_unformattable_for;

Expand Down Expand Up @@ -2727,98 +2566,6 @@ inline auto arg(const char* name, const T& arg) -> named_arg<T> {
return {name, arg};
}

/// Formats a string and writes the output to `out`.
template <typename OutputIt,
FMT_ENABLE_IF(detail::is_output_iterator<remove_cvref_t<OutputIt>,
char>::value)>
// DEPRECATED! Passing out as a forwarding reference.
auto vformat_to(OutputIt&& out, string_view fmt, format_args args)
-> remove_cvref_t<OutputIt> {
auto&& buf = detail::get_buffer<char>(out);
detail::vformat_to(buf, fmt, args, {});
return detail::get_iterator(buf, out);
}

/**
* Formats `args` according to specifications in `fmt`, writes the result to
* the output iterator `out` and returns the iterator past the end of the output
* range. `format_to` does not append a terminating null character.
*
* **Example**:
*
* auto out = std::vector<char>();
* fmt::format_to(std::back_inserter(out), "{}", 42);
*/
template <typename OutputIt, typename... T,
FMT_ENABLE_IF(detail::is_output_iterator<remove_cvref_t<OutputIt>,
char>::value)>
FMT_INLINE auto format_to(OutputIt&& out, format_string<T...> fmt, T&&... args)
-> remove_cvref_t<OutputIt> {
return vformat_to(out, fmt.str, vargs<T...>{{args...}});
}

template <typename OutputIt> struct format_to_n_result {
OutputIt out; ///< Iterator past the end of the output range.
size_t size; ///< Total (not truncated) output size.
};

template <typename OutputIt, typename... T,
FMT_ENABLE_IF(detail::is_output_iterator<OutputIt, char>::value)>
auto vformat_to_n(OutputIt out, size_t n, string_view fmt, format_args args)
-> format_to_n_result<OutputIt> {
using traits = detail::fixed_buffer_traits;
auto buf = detail::iterator_buffer<OutputIt, char, traits>(out, n);
detail::vformat_to(buf, fmt, args, {});
return {buf.out(), buf.count()};
}

/**
* Formats `args` according to specifications in `fmt`, writes up to `n`
* characters of the result to the output iterator `out` and returns the total
* (not truncated) output size and the iterator past the end of the output
* range. `format_to_n` does not append a terminating null character.
*/
template <typename OutputIt, typename... T,
FMT_ENABLE_IF(detail::is_output_iterator<OutputIt, char>::value)>
FMT_INLINE auto format_to_n(OutputIt out, size_t n, format_string<T...> fmt,
T&&... args) -> format_to_n_result<OutputIt> {
return vformat_to_n(out, n, fmt.str, vargs<T...>{{args...}});
}

struct format_to_result {
char* out; ///< Pointer to just after the last successful write.
bool truncated; ///< Specifies if the output was truncated.

FMT_CONSTEXPR operator char*() const {
// Report truncation to prevent silent data loss.
if (truncated) report_error("output is truncated");
return out;
}
};

template <size_t N>
FMT_DEPRECATED auto vformat_to(char (&out)[N], string_view fmt,
format_args args) -> format_to_result {
auto result = vformat_to_n(out, N, fmt, args);
return {result.out, result.size > N};
}

template <size_t N, typename... T>
FMT_INLINE auto format_to(char (&out)[N], format_string<T...> fmt, T&&... args)
-> format_to_result {
auto result = vformat_to_n(out, N, fmt.str, vargs<T...>{{args...}});
return {result.out, result.size > N};
}

/// Returns the number of chars in the output of `format(fmt, args...)`.
template <typename... T>
FMT_NODISCARD FMT_INLINE auto formatted_size(format_string<T...> fmt,
T&&... args) -> size_t {
auto buf = detail::counting_buffer<>();
detail::vformat_to(buf, fmt.str, vargs<T...>{{args...}}, {});
return buf.count();
}

FMT_API void vprint(string_view fmt, format_args args);
FMT_API void vprint(FILE* f, string_view fmt, format_args args);
FMT_API void vprintln(FILE* f, string_view fmt, format_args args);
Expand Down
Loading
Loading