Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

🛠 Add basic array safety functions and backwards-compatible result type #3805

Merged
merged 4 commits into from
Jan 16, 2024
Merged
Changes from 2 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
57 changes: 52 additions & 5 deletions include/fmt/base.h
Original file line number Diff line number Diff line change
Expand Up @@ -259,6 +259,8 @@
# define FMT_UNICODE !FMT_MSC_VERSION
#endif

#define FMT_FWD(...) static_cast<decltype(__VA_ARGS__)&&>(__VA_ARGS__)

// Enable minimal optimizations for more compact code in debug mode.
FMT_GCC_PRAGMA("GCC push_options")
#if !defined(__OPTIMIZE__) && !defined(__CUDACC__)
Expand Down Expand Up @@ -2811,13 +2813,39 @@ inline auto runtime(string_view s) -> runtime_format_string<> { return {{s}}; }

/** Formats a string and writes the output to ``out``. */
template <typename OutputIt,
FMT_ENABLE_IF(detail::is_output_iterator<OutputIt, char>::value)>
auto vformat_to(OutputIt out, string_view fmt, format_args args) -> OutputIt {
FMT_ENABLE_IF(detail::is_output_iterator<remove_cvref_t<OutputIt>,
char>::value)>
auto vformat_to(OutputIt&& out, string_view fmt, format_args args)
ThePhD marked this conversation as resolved.
Show resolved Hide resolved
-> remove_cvref_t<OutputIt> {
auto&& buf = detail::get_buffer<char>(out);
detail::vformat_to(buf, fmt, args, {});
return detail::get_iterator(buf, out);
}

template <typename OutputIt, typename OutputSen = OutputIt>
struct format_to_result {
/** Iterator past the end of the last write. */
ThePhD marked this conversation as resolved.
Show resolved Hide resolved
OutputIt out;
/** Iterator to the end of the output range. */
ThePhD marked this conversation as resolved.
Show resolved Hide resolved
OutputSen out_last;

FMT_CONSTEXPR operator OutputIt&() & noexcept { return out; }
FMT_CONSTEXPR operator const OutputIt&() const& noexcept { return out; }
FMT_CONSTEXPR operator OutputIt&&() && noexcept {
return static_cast<OutputIt&&>(out);
}
Copy link
Contributor

Choose a reason for hiding this comment

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

Do we need all three overloads and why?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

We could make a const-only version of this function that just unconditionally returns a copy. It'd be cheap for all the cases we care about, but if later functionality is added to do e.g. real output_range that has a move-only iterator, then it'll start being a compilation break to use this type.

There is also the case that for real output_range types, you'd want to return a std::ranges::subrange or a sufficient mockup of one, so it might be worth it to invest in making a real fmt::detail::subrange for the return value.

};

/** Formats a string and writes the output to ``out``. */
template <size_t Size>
auto vformat_to(char (&out)[Size], string_view fmt, format_args args)
-> format_to_result<char*> {
using traits = detail::fixed_buffer_traits;
auto buf = detail::iterator_buffer<char*, char, traits>(out, Size);
detail::vformat_to(buf, fmt, args, {});
return {out + buf.count(), out + Size};
ThePhD marked this conversation as resolved.
Show resolved Hide resolved
}

/**
\rst
Formats ``args`` according to specifications in ``fmt``, writes the result to
Expand All @@ -2831,9 +2859,28 @@ auto vformat_to(OutputIt out, string_view fmt, format_args args) -> OutputIt {
\endrst
*/
template <typename OutputIt, typename... T,
FMT_ENABLE_IF(detail::is_output_iterator<OutputIt, char>::value)>
FMT_INLINE auto format_to(OutputIt out, format_string<T...> fmt, T&&... args)
-> OutputIt {
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(FMT_FWD(out), fmt, fmt::make_format_args(args...));
}

/**
\rst
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);
\endrst
*/
ThePhD marked this conversation as resolved.
Show resolved Hide resolved
template <size_t Size, typename... T>
FMT_INLINE auto format_to(char (&out)[Size], format_string<T...> fmt,
T&&... args) -> format_to_result<char*> {
return vformat_to(out, fmt, fmt::make_format_args(args...));
}

Expand Down