external: Update fmtlib.

This commit is contained in:
BearOso 2024-10-25 18:54:14 -05:00
parent f6a46f5831
commit 8a33829bce
16 changed files with 8934 additions and 7642 deletions

View File

@ -1,4 +1,4 @@
// Formatting library for C++ - dynamic format arguments // Formatting library for C++ - dynamic argument lists
// //
// Copyright (c) 2012 - present, Victor Zverovich // Copyright (c) 2012 - present, Victor Zverovich
// All rights reserved. // All rights reserved.
@ -8,11 +8,13 @@
#ifndef FMT_ARGS_H_ #ifndef FMT_ARGS_H_
#define FMT_ARGS_H_ #define FMT_ARGS_H_
#ifndef FMT_MODULE
# include <functional> // std::reference_wrapper # include <functional> // std::reference_wrapper
# include <memory> // std::unique_ptr # include <memory> // std::unique_ptr
# include <vector> # include <vector>
#endif
#include "core.h" #include "format.h" // std_string_view
FMT_BEGIN_NAMESPACE FMT_BEGIN_NAMESPACE
@ -22,20 +24,24 @@ template <typename T> struct is_reference_wrapper : std::false_type {};
template <typename T> template <typename T>
struct is_reference_wrapper<std::reference_wrapper<T>> : std::true_type {}; struct is_reference_wrapper<std::reference_wrapper<T>> : std::true_type {};
template <typename T> const T& unwrap(const T& v) { return v; } template <typename T> auto unwrap(const T& v) -> const T& { return v; }
template <typename T> const T& unwrap(const std::reference_wrapper<T>& v) { template <typename T>
auto unwrap(const std::reference_wrapper<T>& v) -> const T& {
return static_cast<const T&>(v); return static_cast<const T&>(v);
} }
class dynamic_arg_list { // node is defined outside dynamic_arg_list to workaround a C2504 bug in MSVC
// 2022 (v17.10.0).
//
// Workaround for clang's -Wweak-vtables. Unlike for regular classes, for // Workaround for clang's -Wweak-vtables. Unlike for regular classes, for
// templates it doesn't complain about inability to deduce single translation // templates it doesn't complain about inability to deduce single translation
// unit for placing vtable. So storage_node_base is made a fake template. // unit for placing vtable. So node is made a fake template.
template <typename = void> struct node { template <typename = void> struct node {
virtual ~node() = default; virtual ~node() = default;
std::unique_ptr<node<>> next; std::unique_ptr<node<>> next;
}; };
class dynamic_arg_list {
template <typename T> struct typed_node : node<> { template <typename T> struct typed_node : node<> {
T value; T value;
@ -50,7 +56,7 @@ class dynamic_arg_list {
std::unique_ptr<node<>> head_; std::unique_ptr<node<>> head_;
public: public:
template <typename T, typename Arg> const T& push(const Arg& arg) { template <typename T, typename Arg> auto push(const Arg& arg) -> const T& {
auto new_node = std::unique_ptr<typed_node<T>>(new typed_node<T>(arg)); auto new_node = std::unique_ptr<typed_node<T>>(new typed_node<T>(arg));
auto& value = new_node->value; auto& value = new_node->value;
new_node->next = std::move(head_); new_node->next = std::move(head_);
@ -61,14 +67,10 @@ class dynamic_arg_list {
} // namespace detail } // namespace detail
/** /**
\rst * A dynamic list of formatting arguments with storage.
A dynamic version of `fmt::format_arg_store`. *
It's equipped with a storage to potentially temporary objects which lifetimes * It can be implicitly converted into `fmt::basic_format_args` for passing
could be shorter than the format arguments object. * into type-erased formatting functions such as `fmt::vformat`.
It can be implicitly converted into `~fmt::basic_format_args` for passing
into type-erased formatting functions such as `~fmt::vformat`.
\endrst
*/ */
template <typename Context> template <typename Context>
class dynamic_format_arg_store class dynamic_format_arg_store
@ -110,14 +112,14 @@ class dynamic_format_arg_store
friend class basic_format_args<Context>; friend class basic_format_args<Context>;
unsigned long long get_types() const { auto get_types() const -> unsigned long long {
return detail::is_unpacked_bit | data_.size() | return detail::is_unpacked_bit | data_.size() |
(named_info_.empty() (named_info_.empty()
? 0ULL ? 0ULL
: static_cast<unsigned long long>(detail::has_named_args_bit)); : static_cast<unsigned long long>(detail::has_named_args_bit));
} }
const basic_format_arg<Context>* data() const { auto data() const -> const basic_format_arg<Context>* {
return named_info_.empty() ? data_.data() : data_.data() + 1; return named_info_.empty() ? data_.data() : data_.data() + 1;
} }
@ -146,21 +148,19 @@ class dynamic_format_arg_store
constexpr dynamic_format_arg_store() = default; constexpr dynamic_format_arg_store() = default;
/** /**
\rst * Adds an argument into the dynamic store for later passing to a formatting
Adds an argument into the dynamic store for later passing to a formatting * function.
function. *
* Note that custom types and string types (but not string views) are copied
Note that custom types and string types (but not string views) are copied * into the store dynamically allocating memory if necessary.
into the store dynamically allocating memory if necessary. *
* **Example**:
**Example**:: *
* fmt::dynamic_format_arg_store<fmt::format_context> store;
fmt::dynamic_format_arg_store<fmt::format_context> store; * store.push_back(42);
store.push_back(42); * store.push_back("abc");
store.push_back("abc"); * store.push_back(1.5f);
store.push_back(1.5f); * std::string result = fmt::vformat("{} and {} and {}", store);
std::string result = fmt::vformat("{} and {} and {}", store);
\endrst
*/ */
template <typename T> void push_back(const T& arg) { template <typename T> void push_back(const T& arg) {
if (detail::const_check(need_copy<T>::value)) if (detail::const_check(need_copy<T>::value))
@ -170,19 +170,17 @@ class dynamic_format_arg_store
} }
/** /**
\rst * Adds a reference to the argument into the dynamic store for later passing
Adds a reference to the argument into the dynamic store for later passing to * to a formatting function.
a formatting function. *
* **Example**:
**Example**:: *
* fmt::dynamic_format_arg_store<fmt::format_context> store;
fmt::dynamic_format_arg_store<fmt::format_context> store; * char band[] = "Rolling Stones";
char band[] = "Rolling Stones"; * store.push_back(std::cref(band));
store.push_back(std::cref(band)); * band[9] = 'c'; // Changing str affects the output.
band[9] = 'c'; // Changing str affects the output. * std::string result = fmt::vformat("{}", store);
std::string result = fmt::vformat("{}", store); * // result == "Rolling Scones"
// result == "Rolling Scones"
\endrst
*/ */
template <typename T> void push_back(std::reference_wrapper<T> arg) { template <typename T> void push_back(std::reference_wrapper<T> arg) {
static_assert( static_assert(
@ -192,9 +190,9 @@ class dynamic_format_arg_store
} }
/** /**
Adds named argument into the dynamic store for later passing to a formatting * Adds named argument into the dynamic store for later passing to a
function. ``std::reference_wrapper`` is supported to avoid copying of the * formatting function. `std::reference_wrapper` is supported to avoid
argument. The name is always copied into the store. * copying of the argument. The name is always copied into the store.
*/ */
template <typename T> template <typename T>
void push_back(const detail::named_arg<char_type, T>& arg) { void push_back(const detail::named_arg<char_type, T>& arg) {
@ -208,19 +206,15 @@ class dynamic_format_arg_store
} }
} }
/** Erase all elements from the store */ /// Erase all elements from the store.
void clear() { void clear() {
data_.clear(); data_.clear();
named_info_.clear(); named_info_.clear();
dynamic_args_ = detail::dynamic_arg_list(); dynamic_args_ = detail::dynamic_arg_list();
} }
/** /// Reserves space to store at least `new_cap` arguments including
\rst /// `new_cap_named` named arguments.
Reserves space to store at least *new_cap* arguments including
*new_cap_named* named arguments.
\endrst
*/
void reserve(size_t new_cap, size_t new_cap_named) { void reserve(size_t new_cap, size_t new_cap_named) {
FMT_ASSERT(new_cap >= new_cap_named, FMT_ASSERT(new_cap >= new_cap_named,
"Set of arguments includes set of named arguments"); "Set of arguments includes set of named arguments");

3077
external/fmt/include/fmt/base.h vendored Normal file

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -10,15 +10,8 @@
#include "format.h" #include "format.h"
// __declspec(deprecated) is broken in some MSVC versions.
#if FMT_MSC_VER
# define FMT_DEPRECATED_NONMSVC
#else
# define FMT_DEPRECATED_NONMSVC FMT_DEPRECATED
#endif
FMT_BEGIN_NAMESPACE FMT_BEGIN_NAMESPACE
FMT_MODULE_EXPORT_BEGIN FMT_BEGIN_EXPORT
enum class color : uint32_t { enum class color : uint32_t {
alice_blue = 0xF0F8FF, // rgb(240,248,255) alice_blue = 0xF0F8FF, // rgb(240,248,255)
@ -210,7 +203,7 @@ struct rgb {
uint8_t b; uint8_t b;
}; };
FMT_BEGIN_DETAIL_NAMESPACE namespace detail {
// color is a struct of either a rgb color or a terminal color. // color is a struct of either a rgb color or a terminal color.
struct color_type { struct color_type {
@ -232,22 +225,21 @@ struct color_type {
uint32_t rgb_color; uint32_t rgb_color;
} value; } value;
}; };
} // namespace detail
FMT_END_DETAIL_NAMESPACE /// A text style consisting of foreground and background colors and emphasis.
/** A text style consisting of foreground and background colors and emphasis. */
class text_style { class text_style {
public: public:
FMT_CONSTEXPR text_style(emphasis em = emphasis()) noexcept FMT_CONSTEXPR text_style(emphasis em = emphasis()) noexcept
: set_foreground_color(), set_background_color(), ems(em) {} : set_foreground_color(), set_background_color(), ems(em) {}
FMT_CONSTEXPR text_style& operator|=(const text_style& rhs) { FMT_CONSTEXPR auto operator|=(const text_style& rhs) -> text_style& {
if (!set_foreground_color) { if (!set_foreground_color) {
set_foreground_color = rhs.set_foreground_color; set_foreground_color = rhs.set_foreground_color;
foreground_color = rhs.foreground_color; foreground_color = rhs.foreground_color;
} else if (rhs.set_foreground_color) { } else if (rhs.set_foreground_color) {
if (!foreground_color.is_rgb || !rhs.foreground_color.is_rgb) if (!foreground_color.is_rgb || !rhs.foreground_color.is_rgb)
FMT_THROW(format_error("can't OR a terminal color")); report_error("can't OR a terminal color");
foreground_color.value.rgb_color |= rhs.foreground_color.value.rgb_color; foreground_color.value.rgb_color |= rhs.foreground_color.value.rgb_color;
} }
@ -256,7 +248,7 @@ class text_style {
background_color = rhs.background_color; background_color = rhs.background_color;
} else if (rhs.set_background_color) { } else if (rhs.set_background_color) {
if (!background_color.is_rgb || !rhs.background_color.is_rgb) if (!background_color.is_rgb || !rhs.background_color.is_rgb)
FMT_THROW(format_error("can't OR a terminal color")); report_error("can't OR a terminal color");
background_color.value.rgb_color |= rhs.background_color.value.rgb_color; background_color.value.rgb_color |= rhs.background_color.value.rgb_color;
} }
@ -265,39 +257,29 @@ class text_style {
return *this; return *this;
} }
friend FMT_CONSTEXPR text_style operator|(text_style lhs, friend FMT_CONSTEXPR auto operator|(text_style lhs, const text_style& rhs)
const text_style& rhs) { -> text_style {
return lhs |= rhs; return lhs |= rhs;
} }
FMT_DEPRECATED_NONMSVC FMT_CONSTEXPR text_style& operator&=( FMT_CONSTEXPR auto has_foreground() const noexcept -> bool {
const text_style& rhs) {
return and_assign(rhs);
}
FMT_DEPRECATED_NONMSVC friend FMT_CONSTEXPR text_style
operator&(text_style lhs, const text_style& rhs) {
return lhs.and_assign(rhs);
}
FMT_CONSTEXPR bool has_foreground() const noexcept {
return set_foreground_color; return set_foreground_color;
} }
FMT_CONSTEXPR bool has_background() const noexcept { FMT_CONSTEXPR auto has_background() const noexcept -> bool {
return set_background_color; return set_background_color;
} }
FMT_CONSTEXPR bool has_emphasis() const noexcept { FMT_CONSTEXPR auto has_emphasis() const noexcept -> bool {
return static_cast<uint8_t>(ems) != 0; return static_cast<uint8_t>(ems) != 0;
} }
FMT_CONSTEXPR detail::color_type get_foreground() const noexcept { FMT_CONSTEXPR auto get_foreground() const noexcept -> detail::color_type {
FMT_ASSERT(has_foreground(), "no foreground specified for this style"); FMT_ASSERT(has_foreground(), "no foreground specified for this style");
return foreground_color; return foreground_color;
} }
FMT_CONSTEXPR detail::color_type get_background() const noexcept { FMT_CONSTEXPR auto get_background() const noexcept -> detail::color_type {
FMT_ASSERT(has_background(), "no background specified for this style"); FMT_ASSERT(has_background(), "no background specified for this style");
return background_color; return background_color;
} }
FMT_CONSTEXPR emphasis get_emphasis() const noexcept { FMT_CONSTEXPR auto get_emphasis() const noexcept -> emphasis {
FMT_ASSERT(has_emphasis(), "no emphasis specified for this style"); FMT_ASSERT(has_emphasis(), "no emphasis specified for this style");
return ems; return ems;
} }
@ -315,36 +297,11 @@ class text_style {
} }
} }
// DEPRECATED! friend FMT_CONSTEXPR auto fg(detail::color_type foreground) noexcept
FMT_CONSTEXPR text_style& and_assign(const text_style& rhs) { -> text_style;
if (!set_foreground_color) {
set_foreground_color = rhs.set_foreground_color;
foreground_color = rhs.foreground_color;
} else if (rhs.set_foreground_color) {
if (!foreground_color.is_rgb || !rhs.foreground_color.is_rgb)
FMT_THROW(format_error("can't AND a terminal color"));
foreground_color.value.rgb_color &= rhs.foreground_color.value.rgb_color;
}
if (!set_background_color) { friend FMT_CONSTEXPR auto bg(detail::color_type background) noexcept
set_background_color = rhs.set_background_color; -> text_style;
background_color = rhs.background_color;
} else if (rhs.set_background_color) {
if (!background_color.is_rgb || !rhs.background_color.is_rgb)
FMT_THROW(format_error("can't AND a terminal color"));
background_color.value.rgb_color &= rhs.background_color.value.rgb_color;
}
ems = static_cast<emphasis>(static_cast<uint8_t>(ems) &
static_cast<uint8_t>(rhs.ems));
return *this;
}
friend FMT_CONSTEXPR_DECL text_style
fg(detail::color_type foreground) noexcept;
friend FMT_CONSTEXPR_DECL text_style
bg(detail::color_type background) noexcept;
detail::color_type foreground_color; detail::color_type foreground_color;
detail::color_type background_color; detail::color_type background_color;
@ -353,21 +310,24 @@ class text_style {
emphasis ems; emphasis ems;
}; };
/** Creates a text style from the foreground (text) color. */ /// Creates a text style from the foreground (text) color.
FMT_CONSTEXPR inline text_style fg(detail::color_type foreground) noexcept { FMT_CONSTEXPR inline auto fg(detail::color_type foreground) noexcept
-> text_style {
return text_style(true, foreground); return text_style(true, foreground);
} }
/** Creates a text style from the background color. */ /// Creates a text style from the background color.
FMT_CONSTEXPR inline text_style bg(detail::color_type background) noexcept { FMT_CONSTEXPR inline auto bg(detail::color_type background) noexcept
-> text_style {
return text_style(false, background); return text_style(false, background);
} }
FMT_CONSTEXPR inline text_style operator|(emphasis lhs, emphasis rhs) noexcept { FMT_CONSTEXPR inline auto operator|(emphasis lhs, emphasis rhs) noexcept
-> text_style {
return text_style(lhs) | rhs; return text_style(lhs) | rhs;
} }
FMT_BEGIN_DETAIL_NAMESPACE namespace detail {
template <typename Char> struct ansi_color_escape { template <typename Char> struct ansi_color_escape {
FMT_CONSTEXPR ansi_color_escape(detail::color_type text_color, FMT_CONSTEXPR ansi_color_escape(detail::color_type text_color,
@ -429,9 +389,9 @@ template <typename Char> struct ansi_color_escape {
} }
FMT_CONSTEXPR operator const Char*() const noexcept { return buffer; } FMT_CONSTEXPR operator const Char*() const noexcept { return buffer; }
FMT_CONSTEXPR const Char* begin() const noexcept { return buffer; } FMT_CONSTEXPR auto begin() const noexcept -> const Char* { return buffer; }
FMT_CONSTEXPR_CHAR_TRAITS const Char* end() const noexcept { FMT_CONSTEXPR20 auto end() const noexcept -> const Char* {
return buffer + std::char_traits<Char>::length(buffer); return buffer + basic_string_view<Char>(buffer).size();
} }
private: private:
@ -445,62 +405,45 @@ template <typename Char> struct ansi_color_escape {
out[2] = static_cast<Char>('0' + c % 10); out[2] = static_cast<Char>('0' + c % 10);
out[3] = static_cast<Char>(delimiter); out[3] = static_cast<Char>(delimiter);
} }
static FMT_CONSTEXPR bool has_emphasis(emphasis em, emphasis mask) noexcept { static FMT_CONSTEXPR auto has_emphasis(emphasis em, emphasis mask) noexcept
-> bool {
return static_cast<uint8_t>(em) & static_cast<uint8_t>(mask); return static_cast<uint8_t>(em) & static_cast<uint8_t>(mask);
} }
}; };
template <typename Char> template <typename Char>
FMT_CONSTEXPR ansi_color_escape<Char> make_foreground_color( FMT_CONSTEXPR auto make_foreground_color(detail::color_type foreground) noexcept
detail::color_type foreground) noexcept { -> ansi_color_escape<Char> {
return ansi_color_escape<Char>(foreground, "\x1b[38;2;"); return ansi_color_escape<Char>(foreground, "\x1b[38;2;");
} }
template <typename Char> template <typename Char>
FMT_CONSTEXPR ansi_color_escape<Char> make_background_color( FMT_CONSTEXPR auto make_background_color(detail::color_type background) noexcept
detail::color_type background) noexcept { -> ansi_color_escape<Char> {
return ansi_color_escape<Char>(background, "\x1b[48;2;"); return ansi_color_escape<Char>(background, "\x1b[48;2;");
} }
template <typename Char> template <typename Char>
FMT_CONSTEXPR ansi_color_escape<Char> make_emphasis(emphasis em) noexcept { FMT_CONSTEXPR auto make_emphasis(emphasis em) noexcept
-> ansi_color_escape<Char> {
return ansi_color_escape<Char>(em); return ansi_color_escape<Char>(em);
} }
template <typename Char> inline void fputs(const Char* chars, FILE* stream) {
int result = std::fputs(chars, stream);
if (result < 0)
FMT_THROW(system_error(errno, FMT_STRING("cannot write to file")));
}
template <> inline void fputs<wchar_t>(const wchar_t* chars, FILE* stream) {
int result = std::fputws(chars, stream);
if (result < 0)
FMT_THROW(system_error(errno, FMT_STRING("cannot write to file")));
}
template <typename Char> inline void reset_color(FILE* stream) {
fputs("\x1b[0m", stream);
}
template <> inline void reset_color<wchar_t>(FILE* stream) {
fputs(L"\x1b[0m", stream);
}
template <typename Char> inline void reset_color(buffer<Char>& buffer) { template <typename Char> inline void reset_color(buffer<Char>& buffer) {
auto reset_color = string_view("\x1b[0m"); auto reset_color = string_view("\x1b[0m");
buffer.append(reset_color.begin(), reset_color.end()); buffer.append(reset_color.begin(), reset_color.end());
} }
template <typename T> struct styled_arg { template <typename T> struct styled_arg : detail::view {
const T& value; const T& value;
text_style style; text_style style;
styled_arg(const T& v, text_style s) : value(v), style(s) {}
}; };
template <typename Char> template <typename Char>
void vformat_to(buffer<Char>& buf, const text_style& ts, void vformat_to(
basic_string_view<Char> format_str, buffer<Char>& buf, const text_style& ts, basic_string_view<Char> format_str,
basic_format_args<buffer_context<type_identity_t<Char>>> args) { basic_format_args<buffered_context<type_identity_t<Char>>> args) {
bool has_style = false; bool has_style = false;
if (ts.has_emphasis()) { if (ts.has_emphasis()) {
has_style = true; has_style = true;
@ -521,118 +464,94 @@ void vformat_to(buffer<Char>& buf, const text_style& ts,
if (has_style) detail::reset_color<Char>(buf); if (has_style) detail::reset_color<Char>(buf);
} }
FMT_END_DETAIL_NAMESPACE } // namespace detail
template <typename S, typename Char = char_t<S>> inline void vprint(FILE* f, const text_style& ts, string_view fmt,
void vprint(std::FILE* f, const text_style& ts, const S& format, format_args args) {
basic_format_args<buffer_context<type_identity_t<Char>>> args) { auto buf = memory_buffer();
basic_memory_buffer<Char> buf; detail::vformat_to(buf, ts, fmt, args);
detail::vformat_to(buf, ts, to_string_view(format), args); print(f, FMT_STRING("{}"), string_view(buf.begin(), buf.size()));
if (detail::is_utf8()) {
detail::print(f, basic_string_view<Char>(buf.begin(), buf.size()));
} else {
buf.push_back(Char(0));
detail::fputs(buf.data(), f);
}
} }
/** /**
\rst * Formats a string and prints it to the specified file stream using ANSI
Formats a string and prints it to the specified file stream using ANSI * escape sequences to specify text formatting.
escape sequences to specify text formatting. *
* **Example**:
**Example**:: *
* fmt::print(fmt::emphasis::bold | fg(fmt::color::red),
fmt::print(fmt::emphasis::bold | fg(fmt::color::red), * "Elapsed time: {0:.2f} seconds", 1.23);
"Elapsed time: {0:.2f} seconds", 1.23);
\endrst
*/ */
template <typename S, typename... Args, template <typename... T>
FMT_ENABLE_IF(detail::is_string<S>::value)> void print(FILE* f, const text_style& ts, format_string<T...> fmt,
void print(std::FILE* f, const text_style& ts, const S& format_str, T&&... args) {
const Args&... args) { vprint(f, ts, fmt, fmt::make_format_args(args...));
vprint(f, ts, format_str,
fmt::make_format_args<buffer_context<char_t<S>>>(args...));
} }
/** /**
\rst * Formats a string and prints it to stdout using ANSI escape sequences to
Formats a string and prints it to stdout using ANSI escape sequences to * specify text formatting.
specify text formatting. *
* **Example**:
**Example**:: *
* fmt::print(fmt::emphasis::bold | fg(fmt::color::red),
fmt::print(fmt::emphasis::bold | fg(fmt::color::red), * "Elapsed time: {0:.2f} seconds", 1.23);
"Elapsed time: {0:.2f} seconds", 1.23);
\endrst
*/ */
template <typename S, typename... Args, template <typename... T>
FMT_ENABLE_IF(detail::is_string<S>::value)> void print(const text_style& ts, format_string<T...> fmt, T&&... args) {
void print(const text_style& ts, const S& format_str, const Args&... args) { return print(stdout, ts, fmt, std::forward<T>(args)...);
return print(stdout, ts, format_str, args...);
} }
template <typename S, typename Char = char_t<S>> inline auto vformat(const text_style& ts, string_view fmt, format_args args)
inline std::basic_string<Char> vformat( -> std::string {
const text_style& ts, const S& format_str, auto buf = memory_buffer();
basic_format_args<buffer_context<type_identity_t<Char>>> args) { detail::vformat_to(buf, ts, fmt, args);
basic_memory_buffer<Char> buf;
detail::vformat_to(buf, ts, to_string_view(format_str), args);
return fmt::to_string(buf); return fmt::to_string(buf);
} }
/** /**
\rst * Formats arguments and returns the result as a string using ANSI escape
Formats arguments and returns the result as a string using ANSI * sequences to specify text formatting.
escape sequences to specify text formatting. *
* **Example**:
**Example**:: *
* ```
#include <fmt/color.h> * #include <fmt/color.h>
std::string message = fmt::format(fmt::emphasis::bold | fg(fmt::color::red), * std::string message = fmt::format(fmt::emphasis::bold | fg(fmt::color::red),
"The answer is {}", 42); * "The answer is {}", 42);
\endrst * ```
*/ */
template <typename S, typename... Args, typename Char = char_t<S>> template <typename... T>
inline std::basic_string<Char> format(const text_style& ts, const S& format_str, inline auto format(const text_style& ts, format_string<T...> fmt, T&&... args)
const Args&... args) { -> std::string {
return fmt::vformat(ts, to_string_view(format_str), return fmt::vformat(ts, fmt, fmt::make_format_args(args...));
fmt::make_format_args<buffer_context<Char>>(args...)); }
/// Formats a string with the given text_style and writes the output to `out`.
template <typename OutputIt,
FMT_ENABLE_IF(detail::is_output_iterator<OutputIt, char>::value)>
auto vformat_to(OutputIt out, const text_style& ts, string_view fmt,
format_args args) -> OutputIt {
auto&& buf = detail::get_buffer<char>(out);
detail::vformat_to(buf, ts, fmt, args);
return detail::get_iterator(buf, out);
} }
/** /**
Formats a string with the given text_style and writes the output to ``out``. * Formats arguments with the given text style, writes the result to the output
* iterator `out` and returns the iterator past the end of the output range.
*
* **Example**:
*
* std::vector<char> out;
* fmt::format_to(std::back_inserter(out),
* fmt::emphasis::bold | fg(fmt::color::red), "{}", 42);
*/ */
template <typename OutputIt, typename Char, template <typename OutputIt, typename... T,
FMT_ENABLE_IF(detail::is_output_iterator<OutputIt, Char>::value)> FMT_ENABLE_IF(detail::is_output_iterator<OutputIt, char>::value)>
OutputIt vformat_to( inline auto format_to(OutputIt out, const text_style& ts,
OutputIt out, const text_style& ts, basic_string_view<Char> format_str, format_string<T...> fmt, T&&... args) -> OutputIt {
basic_format_args<buffer_context<type_identity_t<Char>>> args) { return vformat_to(out, ts, fmt, fmt::make_format_args(args...));
auto&& buf = detail::get_buffer<Char>(out);
detail::vformat_to(buf, ts, format_str, args);
return detail::get_iterator(buf);
}
/**
\rst
Formats arguments with the given text_style, writes the result to the output
iterator ``out`` and returns the iterator past the end of the output range.
**Example**::
std::vector<char> out;
fmt::format_to(std::back_inserter(out),
fmt::emphasis::bold | fg(fmt::color::red), "{}", 42);
\endrst
*/
template <typename OutputIt, typename S, typename... Args,
bool enable = detail::is_output_iterator<OutputIt, char_t<S>>::value&&
detail::is_string<S>::value>
inline auto format_to(OutputIt out, const text_style& ts, const S& format_str,
Args&&... args) ->
typename std::enable_if<enable, OutputIt>::type {
return vformat_to(out, ts, to_string_view(format_str),
fmt::make_format_args<buffer_context<char_t<S>>>(args...));
} }
template <typename T, typename Char> template <typename T, typename Char>
@ -672,15 +591,14 @@ struct formatter<detail::styled_arg<T>, Char> : formatter<T, Char> {
}; };
/** /**
\rst * Returns an argument that will be formatted using ANSI escape sequences,
Returns an argument that will be formatted using ANSI escape sequences, * to be used in a formatting function.
to be used in a formatting function. *
* **Example**:
**Example**:: *
* fmt::print("Elapsed time: {0:.2f} seconds",
fmt::print("Elapsed time: {s:.2f} seconds", * fmt::styled(1.23, fmt::fg(fmt::color::green) |
fmt::styled(1.23, fmt::fg(fmt::color::green) | fmt::bg(fmt::color::blue))); * fmt::bg(fmt::color::blue)));
\endrst
*/ */
template <typename T> template <typename T>
FMT_CONSTEXPR auto styled(const T& value, text_style ts) FMT_CONSTEXPR auto styled(const T& value, text_style ts)
@ -688,7 +606,7 @@ FMT_CONSTEXPR auto styled(const T& value, text_style ts)
return detail::styled_arg<remove_cvref_t<T>>{value, ts}; return detail::styled_arg<remove_cvref_t<T>>{value, ts};
} }
FMT_MODULE_EXPORT_END FMT_END_EXPORT
FMT_END_NAMESPACE FMT_END_NAMESPACE
#endif // FMT_COLOR_H_ #endif // FMT_COLOR_H_

View File

@ -8,122 +8,46 @@
#ifndef FMT_COMPILE_H_ #ifndef FMT_COMPILE_H_
#define FMT_COMPILE_H_ #define FMT_COMPILE_H_
#ifndef FMT_MODULE
# include <iterator> // std::back_inserter
#endif
#include "format.h" #include "format.h"
FMT_BEGIN_NAMESPACE FMT_BEGIN_NAMESPACE
namespace detail {
template <typename Char, typename InputIt>
inline counting_iterator copy_str(InputIt begin, InputIt end,
counting_iterator it) {
return it + (end - begin);
}
template <typename OutputIt> class truncating_iterator_base {
protected:
OutputIt out_;
size_t limit_;
size_t count_ = 0;
truncating_iterator_base() : out_(), limit_(0) {}
truncating_iterator_base(OutputIt out, size_t limit)
: out_(out), limit_(limit) {}
public:
using iterator_category = std::output_iterator_tag;
using value_type = typename std::iterator_traits<OutputIt>::value_type;
using difference_type = std::ptrdiff_t;
using pointer = void;
using reference = void;
FMT_UNCHECKED_ITERATOR(truncating_iterator_base);
OutputIt base() const { return out_; }
size_t count() const { return count_; }
};
// An output iterator that truncates the output and counts the number of objects
// written to it.
template <typename OutputIt,
typename Enable = typename std::is_void<
typename std::iterator_traits<OutputIt>::value_type>::type>
class truncating_iterator;
template <typename OutputIt>
class truncating_iterator<OutputIt, std::false_type>
: public truncating_iterator_base<OutputIt> {
mutable typename truncating_iterator_base<OutputIt>::value_type blackhole_;
public:
using value_type = typename truncating_iterator_base<OutputIt>::value_type;
truncating_iterator() = default;
truncating_iterator(OutputIt out, size_t limit)
: truncating_iterator_base<OutputIt>(out, limit) {}
truncating_iterator& operator++() {
if (this->count_++ < this->limit_) ++this->out_;
return *this;
}
truncating_iterator operator++(int) {
auto it = *this;
++*this;
return it;
}
value_type& operator*() const {
return this->count_ < this->limit_ ? *this->out_ : blackhole_;
}
};
template <typename OutputIt>
class truncating_iterator<OutputIt, std::true_type>
: public truncating_iterator_base<OutputIt> {
public:
truncating_iterator() = default;
truncating_iterator(OutputIt out, size_t limit)
: truncating_iterator_base<OutputIt>(out, limit) {}
template <typename T> truncating_iterator& operator=(T val) {
if (this->count_++ < this->limit_) *this->out_++ = val;
return *this;
}
truncating_iterator& operator++() { return *this; }
truncating_iterator& operator++(int) { return *this; }
truncating_iterator& operator*() { return *this; }
};
// A compile-time string which is compiled into fast formatting code. // A compile-time string which is compiled into fast formatting code.
class compiled_string {}; FMT_EXPORT class compiled_string {};
namespace detail {
template <typename T, typename InputIt>
FMT_CONSTEXPR inline auto copy(InputIt begin, InputIt end, counting_iterator it)
-> counting_iterator {
return it + (end - begin);
}
template <typename S> template <typename S>
struct is_compiled_string : std::is_base_of<compiled_string, S> {}; struct is_compiled_string : std::is_base_of<compiled_string, S> {};
/** /**
\rst * Converts a string literal `s` into a format string that will be parsed at
Converts a string literal *s* into a format string that will be parsed at * compile time and converted into efficient formatting code. Requires C++17
compile time and converted into efficient formatting code. Requires C++17 * `constexpr if` compiler support.
``constexpr if`` compiler support. *
* **Example**:
**Example**:: *
* // Converts 42 into std::string using the most efficient method and no
// Converts 42 into std::string using the most efficient method and no * // runtime format string processing.
// runtime format string processing. * std::string s = fmt::format(FMT_COMPILE("{}"), 42);
std::string s = fmt::format(FMT_COMPILE("{}"), 42);
\endrst
*/ */
#if defined(__cpp_if_constexpr) && defined(__cpp_return_type_deduction) #if defined(__cpp_if_constexpr) && defined(__cpp_return_type_deduction)
# define FMT_COMPILE(s) \ # define FMT_COMPILE(s) FMT_STRING_IMPL(s, fmt::compiled_string, explicit)
FMT_STRING_IMPL(s, fmt::detail::compiled_string, explicit)
#else #else
# define FMT_COMPILE(s) FMT_STRING(s) # define FMT_COMPILE(s) FMT_STRING(s)
#endif #endif
#if FMT_USE_NONTYPE_TEMPLATE_PARAMETERS #if FMT_USE_NONTYPE_TEMPLATE_ARGS
template <typename Char, size_t N, template <typename Char, size_t N,
fmt::detail_exported::fixed_string<Char, N> Str> fmt::detail_exported::fixed_string<Char, N> Str>
struct udl_compiled_string : compiled_string { struct udl_compiled_string : compiled_string {
@ -135,7 +59,7 @@ struct udl_compiled_string : compiled_string {
#endif #endif
template <typename T, typename... Tail> template <typename T, typename... Tail>
const T& first(const T& value, const Tail&...) { auto first(const T& value, const Tail&...) -> const T& {
return value; return value;
} }
@ -196,7 +120,8 @@ template <typename Char> struct code_unit {
template <typename OutputIt, typename... Args> template <typename OutputIt, typename... Args>
constexpr OutputIt format(OutputIt out, const Args&...) const { constexpr OutputIt format(OutputIt out, const Args&...) const {
return write<Char>(out, value); *out++ = value;
return out;
} }
}; };
@ -220,7 +145,12 @@ template <typename Char, typename T, int N> struct field {
template <typename OutputIt, typename... Args> template <typename OutputIt, typename... Args>
constexpr OutputIt format(OutputIt out, const Args&... args) const { constexpr OutputIt format(OutputIt out, const Args&... args) const {
return write<Char>(out, get_arg_checked<T, N>(args...)); const T& arg = get_arg_checked<T, N>(args...);
if constexpr (std::is_convertible<T, basic_string_view<Char>>::value) {
auto s = basic_string_view<Char>(arg);
return copy<Char>(s.begin(), s.end(), out);
}
return write<Char>(out, arg);
} }
}; };
@ -308,13 +238,12 @@ constexpr size_t parse_text(basic_string_view<Char> str, size_t pos) {
} }
template <typename Args, size_t POS, int ID, typename S> template <typename Args, size_t POS, int ID, typename S>
constexpr auto compile_format_string(S format_str); constexpr auto compile_format_string(S fmt);
template <typename Args, size_t POS, int ID, typename T, typename S> template <typename Args, size_t POS, int ID, typename T, typename S>
constexpr auto parse_tail(T head, S format_str) { constexpr auto parse_tail(T head, S fmt) {
if constexpr (POS != if constexpr (POS != basic_string_view<typename S::char_type>(fmt).size()) {
basic_string_view<typename S::char_type>(format_str).size()) { constexpr auto tail = compile_format_string<Args, POS, ID>(fmt);
constexpr auto tail = compile_format_string<Args, POS, ID>(format_str);
if constexpr (std::is_same<remove_cvref_t<decltype(tail)>, if constexpr (std::is_same<remove_cvref_t<decltype(tail)>,
unknown_format>()) unknown_format>())
return tail; return tail;
@ -331,38 +260,35 @@ template <typename T, typename Char> struct parse_specs_result {
int next_arg_id; int next_arg_id;
}; };
constexpr int manual_indexing_id = -1; enum { manual_indexing_id = -1 };
template <typename T, typename Char> template <typename T, typename Char>
constexpr parse_specs_result<T, Char> parse_specs(basic_string_view<Char> str, constexpr parse_specs_result<T, Char> parse_specs(basic_string_view<Char> str,
size_t pos, int next_arg_id) { size_t pos, int next_arg_id) {
str.remove_prefix(pos); str.remove_prefix(pos);
auto ctx = basic_format_parse_context<Char>(str, {}, next_arg_id); auto ctx =
compile_parse_context<Char>(str, max_value<int>(), nullptr, next_arg_id);
auto f = formatter<T, Char>(); auto f = formatter<T, Char>();
auto end = f.parse(ctx); auto end = f.parse(ctx);
return {f, pos + fmt::detail::to_unsigned(end - str.data()) + 1, return {f, pos + fmt::detail::to_unsigned(end - str.data()),
next_arg_id == 0 ? manual_indexing_id : ctx.next_arg_id()}; next_arg_id == 0 ? manual_indexing_id : ctx.next_arg_id()};
} }
template <typename Char> struct arg_id_handler { template <typename Char> struct arg_id_handler {
arg_ref<Char> arg_id; arg_ref<Char> arg_id;
constexpr int operator()() { constexpr int on_auto() {
FMT_ASSERT(false, "handler cannot be used with automatic indexing"); FMT_ASSERT(false, "handler cannot be used with automatic indexing");
return 0; return 0;
} }
constexpr int operator()(int id) { constexpr int on_index(int id) {
arg_id = arg_ref<Char>(id); arg_id = arg_ref<Char>(id);
return 0; return 0;
} }
constexpr int operator()(basic_string_view<Char> id) { constexpr int on_name(basic_string_view<Char> id) {
arg_id = arg_ref<Char>(id); arg_id = arg_ref<Char>(id);
return 0; return 0;
} }
constexpr void on_error(const char* message) {
FMT_THROW(format_error(message));
}
}; };
template <typename Char> struct parse_arg_id_result { template <typename Char> struct parse_arg_id_result {
@ -388,43 +314,48 @@ struct field_type<T, enable_if_t<detail::is_named_arg<T>::value>> {
template <typename T, typename Args, size_t END_POS, int ARG_INDEX, int NEXT_ID, template <typename T, typename Args, size_t END_POS, int ARG_INDEX, int NEXT_ID,
typename S> typename S>
constexpr auto parse_replacement_field_then_tail(S format_str) { constexpr auto parse_replacement_field_then_tail(S fmt) {
using char_type = typename S::char_type; using char_type = typename S::char_type;
constexpr auto str = basic_string_view<char_type>(format_str); constexpr auto str = basic_string_view<char_type>(fmt);
constexpr char_type c = END_POS != str.size() ? str[END_POS] : char_type(); constexpr char_type c = END_POS != str.size() ? str[END_POS] : char_type();
if constexpr (c == '}') { if constexpr (c == '}') {
return parse_tail<Args, END_POS + 1, NEXT_ID>( return parse_tail<Args, END_POS + 1, NEXT_ID>(
field<char_type, typename field_type<T>::type, ARG_INDEX>(), field<char_type, typename field_type<T>::type, ARG_INDEX>(), fmt);
format_str); } else if constexpr (c != ':') {
} else if constexpr (c == ':') { FMT_THROW(format_error("expected ':'"));
} else {
constexpr auto result = parse_specs<typename field_type<T>::type>( constexpr auto result = parse_specs<typename field_type<T>::type>(
str, END_POS + 1, NEXT_ID == manual_indexing_id ? 0 : NEXT_ID); str, END_POS + 1, NEXT_ID == manual_indexing_id ? 0 : NEXT_ID);
return parse_tail<Args, result.end, result.next_arg_id>( if constexpr (result.end >= str.size() || str[result.end] != '}') {
FMT_THROW(format_error("expected '}'"));
return 0;
} else {
return parse_tail<Args, result.end + 1, result.next_arg_id>(
spec_field<char_type, typename field_type<T>::type, ARG_INDEX>{ spec_field<char_type, typename field_type<T>::type, ARG_INDEX>{
result.fmt}, result.fmt},
format_str); fmt);
}
} }
} }
// Compiles a non-empty format string and returns the compiled representation // Compiles a non-empty format string and returns the compiled representation
// or unknown_format() on unrecognized input. // or unknown_format() on unrecognized input.
template <typename Args, size_t POS, int ID, typename S> template <typename Args, size_t POS, int ID, typename S>
constexpr auto compile_format_string(S format_str) { constexpr auto compile_format_string(S fmt) {
using char_type = typename S::char_type; using char_type = typename S::char_type;
constexpr auto str = basic_string_view<char_type>(format_str); constexpr auto str = basic_string_view<char_type>(fmt);
if constexpr (str[POS] == '{') { if constexpr (str[POS] == '{') {
if constexpr (POS + 1 == str.size()) if constexpr (POS + 1 == str.size())
FMT_THROW(format_error("unmatched '{' in format string")); FMT_THROW(format_error("unmatched '{' in format string"));
if constexpr (str[POS + 1] == '{') { if constexpr (str[POS + 1] == '{') {
return parse_tail<Args, POS + 2, ID>(make_text(str, POS, 1), format_str); return parse_tail<Args, POS + 2, ID>(make_text(str, POS, 1), fmt);
} else if constexpr (str[POS + 1] == '}' || str[POS + 1] == ':') { } else if constexpr (str[POS + 1] == '}' || str[POS + 1] == ':') {
static_assert(ID != manual_indexing_id, static_assert(ID != manual_indexing_id,
"cannot switch from manual to automatic argument indexing"); "cannot switch from manual to automatic argument indexing");
constexpr auto next_id = constexpr auto next_id =
ID != manual_indexing_id ? ID + 1 : manual_indexing_id; ID != manual_indexing_id ? ID + 1 : manual_indexing_id;
return parse_replacement_field_then_tail<get_type<ID, Args>, Args, return parse_replacement_field_then_tail<get_type<ID, Args>, Args,
POS + 1, ID, next_id>( POS + 1, ID, next_id>(fmt);
format_str);
} else { } else {
constexpr auto arg_id_result = constexpr auto arg_id_result =
parse_arg_id<ID>(str.data() + POS + 1, str.data() + str.size()); parse_arg_id<ID>(str.data() + POS + 1, str.data() + str.size());
@ -440,60 +371,55 @@ constexpr auto compile_format_string(S format_str) {
return parse_replacement_field_then_tail<get_type<arg_index, Args>, return parse_replacement_field_then_tail<get_type<arg_index, Args>,
Args, arg_id_end_pos, Args, arg_id_end_pos,
arg_index, manual_indexing_id>( arg_index, manual_indexing_id>(
format_str); fmt);
} else if constexpr (arg_id_result.arg_id.kind == arg_id_kind::name) { } else if constexpr (arg_id_result.arg_id.kind == arg_id_kind::name) {
constexpr auto arg_index = constexpr auto arg_index =
get_arg_index_by_name(arg_id_result.arg_id.val.name, Args{}); get_arg_index_by_name(arg_id_result.arg_id.val.name, Args{});
if constexpr (arg_index != invalid_arg_index) { if constexpr (arg_index >= 0) {
constexpr auto next_id = constexpr auto next_id =
ID != manual_indexing_id ? ID + 1 : manual_indexing_id; ID != manual_indexing_id ? ID + 1 : manual_indexing_id;
return parse_replacement_field_then_tail< return parse_replacement_field_then_tail<
decltype(get_type<arg_index, Args>::value), Args, arg_id_end_pos, decltype(get_type<arg_index, Args>::value), Args, arg_id_end_pos,
arg_index, next_id>(format_str); arg_index, next_id>(fmt);
} else { } else if constexpr (c == '}') {
if constexpr (c == '}') {
return parse_tail<Args, arg_id_end_pos + 1, ID>( return parse_tail<Args, arg_id_end_pos + 1, ID>(
runtime_named_field<char_type>{arg_id_result.arg_id.val.name}, runtime_named_field<char_type>{arg_id_result.arg_id.val.name},
format_str); fmt);
} else if constexpr (c == ':') { } else if constexpr (c == ':') {
return unknown_format(); // no type info for specs parsing return unknown_format(); // no type info for specs parsing
} }
} }
} }
}
} else if constexpr (str[POS] == '}') { } else if constexpr (str[POS] == '}') {
if constexpr (POS + 1 == str.size()) if constexpr (POS + 1 == str.size())
FMT_THROW(format_error("unmatched '}' in format string")); FMT_THROW(format_error("unmatched '}' in format string"));
return parse_tail<Args, POS + 2, ID>(make_text(str, POS, 1), format_str); return parse_tail<Args, POS + 2, ID>(make_text(str, POS, 1), fmt);
} else { } else {
constexpr auto end = parse_text(str, POS + 1); constexpr auto end = parse_text(str, POS + 1);
if constexpr (end - POS > 1) { if constexpr (end - POS > 1) {
return parse_tail<Args, end, ID>(make_text(str, POS, end - POS), return parse_tail<Args, end, ID>(make_text(str, POS, end - POS), fmt);
format_str);
} else { } else {
return parse_tail<Args, end, ID>(code_unit<char_type>{str[POS]}, return parse_tail<Args, end, ID>(code_unit<char_type>{str[POS]}, fmt);
format_str);
} }
} }
} }
template <typename... Args, typename S, template <typename... Args, typename S,
FMT_ENABLE_IF(detail::is_compiled_string<S>::value)> FMT_ENABLE_IF(detail::is_compiled_string<S>::value)>
constexpr auto compile(S format_str) { constexpr auto compile(S fmt) {
constexpr auto str = basic_string_view<typename S::char_type>(format_str); constexpr auto str = basic_string_view<typename S::char_type>(fmt);
if constexpr (str.size() == 0) { if constexpr (str.size() == 0) {
return detail::make_text(str, 0, 0); return detail::make_text(str, 0, 0);
} else { } else {
constexpr auto result = constexpr auto result =
detail::compile_format_string<detail::type_list<Args...>, 0, 0>( detail::compile_format_string<detail::type_list<Args...>, 0, 0>(fmt);
format_str);
return result; return result;
} }
} }
#endif // defined(__cpp_if_constexpr) && defined(__cpp_return_type_deduction) #endif // defined(__cpp_if_constexpr) && defined(__cpp_return_type_deduction)
} // namespace detail } // namespace detail
FMT_MODULE_EXPORT_BEGIN FMT_BEGIN_EXPORT
#if defined(__cpp_if_constexpr) && defined(__cpp_return_type_deduction) #if defined(__cpp_if_constexpr) && defined(__cpp_return_type_deduction)
@ -558,35 +484,36 @@ FMT_CONSTEXPR OutputIt format_to(OutputIt out, const S&, Args&&... args) {
template <typename OutputIt, typename S, typename... Args, template <typename OutputIt, typename S, typename... Args,
FMT_ENABLE_IF(detail::is_compiled_string<S>::value)> FMT_ENABLE_IF(detail::is_compiled_string<S>::value)>
format_to_n_result<OutputIt> format_to_n(OutputIt out, size_t n, auto format_to_n(OutputIt out, size_t n, const S& fmt, Args&&... args)
const S& format_str, Args&&... args) { -> format_to_n_result<OutputIt> {
auto it = fmt::format_to(detail::truncating_iterator<OutputIt>(out, n), using traits = detail::fixed_buffer_traits;
format_str, std::forward<Args>(args)...); auto buf = detail::iterator_buffer<OutputIt, char, traits>(out, n);
return {it.base(), it.count()}; fmt::format_to(std::back_inserter(buf), fmt, std::forward<Args>(args)...);
return {buf.out(), buf.count()};
} }
template <typename S, typename... Args, template <typename S, typename... Args,
FMT_ENABLE_IF(detail::is_compiled_string<S>::value)> FMT_ENABLE_IF(detail::is_compiled_string<S>::value)>
size_t formatted_size(const S& format_str, const Args&... args) { FMT_CONSTEXPR20 auto formatted_size(const S& fmt, const Args&... args)
return fmt::format_to(detail::counting_iterator(), format_str, args...) -> size_t {
.count(); return fmt::format_to(detail::counting_iterator(), fmt, args...).count();
} }
template <typename S, typename... Args, template <typename S, typename... Args,
FMT_ENABLE_IF(detail::is_compiled_string<S>::value)> FMT_ENABLE_IF(detail::is_compiled_string<S>::value)>
void print(std::FILE* f, const S& format_str, const Args&... args) { void print(std::FILE* f, const S& fmt, const Args&... args) {
memory_buffer buffer; memory_buffer buffer;
fmt::format_to(std::back_inserter(buffer), format_str, args...); fmt::format_to(std::back_inserter(buffer), fmt, args...);
detail::print(f, {buffer.data(), buffer.size()}); detail::print(f, {buffer.data(), buffer.size()});
} }
template <typename S, typename... Args, template <typename S, typename... Args,
FMT_ENABLE_IF(detail::is_compiled_string<S>::value)> FMT_ENABLE_IF(detail::is_compiled_string<S>::value)>
void print(const S& format_str, const Args&... args) { void print(const S& fmt, const Args&... args) {
print(stdout, format_str, args...); print(stdout, fmt, args...);
} }
#if FMT_USE_NONTYPE_TEMPLATE_PARAMETERS #if FMT_USE_NONTYPE_TEMPLATE_ARGS
inline namespace literals { inline namespace literals {
template <detail_exported::fixed_string Str> constexpr auto operator""_cf() { template <detail_exported::fixed_string Str> constexpr auto operator""_cf() {
using char_t = remove_cvref_t<decltype(Str.data[0])>; using char_t = remove_cvref_t<decltype(Str.data[0])>;
@ -596,7 +523,7 @@ template <detail_exported::fixed_string Str> constexpr auto operator""_cf() {
} // namespace literals } // namespace literals
#endif #endif
FMT_MODULE_EXPORT_END FMT_END_EXPORT
FMT_END_NAMESPACE FMT_END_NAMESPACE
#endif // FMT_COMPILE_H_ #endif // FMT_COMPILE_H_

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -8,16 +8,18 @@
#ifndef FMT_OS_H_ #ifndef FMT_OS_H_
#define FMT_OS_H_ #define FMT_OS_H_
#include "format.h"
#ifndef FMT_MODULE
# include <cerrno> # include <cerrno>
# include <cstddef> # include <cstddef>
# include <cstdio> # include <cstdio>
# include <system_error> // std::system_error # include <system_error> // std::system_error
#if defined __APPLE__ || defined(__FreeBSD__) # if FMT_HAS_INCLUDE(<xlocale.h>)
# include <xlocale.h> // for LC_NUMERIC_MASK on OS X # include <xlocale.h> // LC_NUMERIC_MASK on macOS
# endif # endif
#endif // FMT_MODULE
#include "format.h"
#ifndef FMT_USE_FCNTL #ifndef FMT_USE_FCNTL
// UWP doesn't provide _pipe. // UWP doesn't provide _pipe.
@ -46,6 +48,7 @@
// Calls to system functions are wrapped in FMT_SYSTEM for testability. // Calls to system functions are wrapped in FMT_SYSTEM for testability.
#ifdef FMT_SYSTEM #ifdef FMT_SYSTEM
# define FMT_HAS_SYSTEM
# define FMT_POSIX_CALL(call) FMT_SYSTEM(call) # define FMT_POSIX_CALL(call) FMT_SYSTEM(call)
#else #else
# define FMT_SYSTEM(call) ::call # define FMT_SYSTEM(call) ::call
@ -71,131 +74,77 @@
#define FMT_RETRY(result, expression) FMT_RETRY_VAL(result, expression, -1) #define FMT_RETRY(result, expression) FMT_RETRY_VAL(result, expression, -1)
FMT_BEGIN_NAMESPACE FMT_BEGIN_NAMESPACE
FMT_MODULE_EXPORT_BEGIN FMT_BEGIN_EXPORT
/** /**
\rst * A reference to a null-terminated string. It can be constructed from a C
A reference to a null-terminated string. It can be constructed from a C * string or `std::string`.
string or ``std::string``. *
* You can use one of the following type aliases for common character types:
You can use one of the following type aliases for common character types: *
* +---------------+-----------------------------+
+---------------+-----------------------------+ * | Type | Definition |
| Type | Definition | * +===============+=============================+
+===============+=============================+ * | cstring_view | basic_cstring_view<char> |
| cstring_view | basic_cstring_view<char> | * +---------------+-----------------------------+
+---------------+-----------------------------+ * | wcstring_view | basic_cstring_view<wchar_t> |
| wcstring_view | basic_cstring_view<wchar_t> | * +---------------+-----------------------------+
+---------------+-----------------------------+ *
* This class is most useful as a parameter type for functions that wrap C APIs.
This class is most useful as a parameter type to allow passing
different types of strings to a function, for example::
template <typename... Args>
std::string format(cstring_view format_str, const Args & ... args);
format("{}", 42);
format(std::string("{}"), 42);
\endrst
*/ */
template <typename Char> class basic_cstring_view { template <typename Char> class basic_cstring_view {
private: private:
const Char* data_; const Char* data_;
public: public:
/** Constructs a string reference object from a C string. */ /// Constructs a string reference object from a C string.
basic_cstring_view(const Char* s) : data_(s) {} basic_cstring_view(const Char* s) : data_(s) {}
/** /// Constructs a string reference from an `std::string` object.
\rst
Constructs a string reference from an ``std::string`` object.
\endrst
*/
basic_cstring_view(const std::basic_string<Char>& s) : data_(s.c_str()) {} basic_cstring_view(const std::basic_string<Char>& s) : data_(s.c_str()) {}
/** Returns the pointer to a C string. */ /// Returns the pointer to a C string.
const Char* c_str() const { return data_; } auto c_str() const -> const Char* { return data_; }
}; };
using cstring_view = basic_cstring_view<char>; using cstring_view = basic_cstring_view<char>;
using wcstring_view = basic_cstring_view<wchar_t>; using wcstring_view = basic_cstring_view<wchar_t>;
template <typename Char> struct formatter<std::error_code, Char> {
template <typename ParseContext>
FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) {
return ctx.begin();
}
template <typename FormatContext>
FMT_CONSTEXPR auto format(const std::error_code& ec, FormatContext& ctx) const
-> decltype(ctx.out()) {
auto out = ctx.out();
out = detail::write_bytes(out, ec.category().name(),
basic_format_specs<Char>());
out = detail::write<Char>(out, Char(':'));
out = detail::write<Char>(out, ec.value());
return out;
}
};
#ifdef _WIN32 #ifdef _WIN32
FMT_API const std::error_category& system_category() noexcept; FMT_API const std::error_category& system_category() noexcept;
FMT_BEGIN_DETAIL_NAMESPACE namespace detail {
// A converter from UTF-16 to UTF-8.
// It is only provided for Windows since other systems support UTF-8 natively.
class utf16_to_utf8 {
private:
memory_buffer buffer_;
public:
utf16_to_utf8() {}
FMT_API explicit utf16_to_utf8(basic_string_view<wchar_t> s);
operator string_view() const { return string_view(&buffer_[0], size()); }
size_t size() const { return buffer_.size() - 1; }
const char* c_str() const { return &buffer_[0]; }
std::string str() const { return std::string(&buffer_[0], size()); }
// Performs conversion returning a system error code instead of
// throwing exception on conversion error. This method may still throw
// in case of memory allocation error.
FMT_API int convert(basic_string_view<wchar_t> s);
};
FMT_API void format_windows_error(buffer<char>& out, int error_code, FMT_API void format_windows_error(buffer<char>& out, int error_code,
const char* message) noexcept; const char* message) noexcept;
FMT_END_DETAIL_NAMESPACE }
FMT_API std::system_error vwindows_error(int error_code, string_view format_str, FMT_API std::system_error vwindows_error(int error_code, string_view format_str,
format_args args); format_args args);
/** /**
\rst * Constructs a `std::system_error` object with the description of the form
Constructs a :class:`std::system_error` object with the description *
of the form * <message>: <system-message>
*
.. parsed-literal:: * where `<message>` is the formatted message and `<system-message>` is the
*<message>*: *<system-message>* * system message corresponding to the error code.
* `error_code` is a Windows error code as given by `GetLastError`.
where *<message>* is the formatted message and *<system-message>* is the * If `error_code` is not a valid error code such as -1, the system message
system message corresponding to the error code. * will look like "error -1".
*error_code* is a Windows error code as given by ``GetLastError``. *
If *error_code* is not a valid error code such as -1, the system message * **Example**:
will look like "error -1". *
* // This throws a system_error with the description
**Example**:: * // cannot open file 'madeup': The system cannot find the file
* specified.
// This throws a system_error with the description * // or similar (system message may vary).
// cannot open file 'madeup': The system cannot find the file specified. * const char *filename = "madeup";
// or similar (system message may vary). * LPOFSTRUCT of = LPOFSTRUCT();
const char *filename = "madeup"; * HFILE file = OpenFile(filename, &of, OF_READ);
LPOFSTRUCT of = LPOFSTRUCT(); * if (file == HFILE_ERROR) {
HFILE file = OpenFile(filename, &of, OF_READ); * throw fmt::windows_error(GetLastError(),
if (file == HFILE_ERROR) { * "cannot open file '{}'", filename);
throw fmt::windows_error(GetLastError(), * }
"cannot open file '{}'", filename);
}
\endrst
*/ */
template <typename... Args> template <typename... Args>
std::system_error windows_error(int error_code, string_view message, std::system_error windows_error(int error_code, string_view message,
@ -207,7 +156,7 @@ std::system_error windows_error(int error_code, string_view message,
// Can be used to report errors from destructors. // Can be used to report errors from destructors.
FMT_API void report_windows_error(int error_code, const char* message) noexcept; FMT_API void report_windows_error(int error_code, const char* message) noexcept;
#else #else
inline const std::error_category& system_category() noexcept { inline auto system_category() noexcept -> const std::error_category& {
return std::system_category(); return std::system_category();
} }
#endif // _WIN32 #endif // _WIN32
@ -244,7 +193,7 @@ class buffered_file {
other.file_ = nullptr; other.file_ = nullptr;
} }
buffered_file& operator=(buffered_file&& other) { auto operator=(buffered_file&& other) -> buffered_file& {
close(); close();
file_ = other.file_; file_ = other.file_;
other.file_ = nullptr; other.file_ = nullptr;
@ -258,24 +207,20 @@ class buffered_file {
FMT_API void close(); FMT_API void close();
// Returns the pointer to a FILE object representing this file. // Returns the pointer to a FILE object representing this file.
FILE* get() const noexcept { return file_; } auto get() const noexcept -> FILE* { return file_; }
// We place parentheses around fileno to workaround a bug in some versions FMT_API auto descriptor() const -> int;
// of MinGW that define fileno as a macro.
// DEPRECATED! Rename to descriptor to avoid issues with macros.
FMT_API int(fileno)() const;
void vprint(string_view format_str, format_args args) { template <typename... T>
fmt::vprint(file_, format_str, args); inline void print(string_view fmt, const T&... args) {
} const auto& vargs = fmt::make_format_args(args...);
detail::is_locking<T...>() ? fmt::vprint_buffered(file_, fmt, vargs)
template <typename... Args> : fmt::vprint(file_, fmt, vargs);
inline void print(string_view format_str, const Args&... args) {
vprint(format_str, fmt::make_format_args(args...));
} }
}; };
#if FMT_USE_FCNTL #if FMT_USE_FCNTL
// A file. Closed file is represented by a file object with descriptor -1. // A file. Closed file is represented by a file object with descriptor -1.
// Methods that are not declared with noexcept may throw // Methods that are not declared with noexcept may throw
// fmt::system_error in case of failure. Note that some errors such as // fmt::system_error in case of failure. Note that some errors such as
@ -289,6 +234,8 @@ class FMT_API file {
// Constructs a file object with a given descriptor. // Constructs a file object with a given descriptor.
explicit file(int fd) : fd_(fd) {} explicit file(int fd) : fd_(fd) {}
friend struct pipe;
public: public:
// Possible values for the oflag argument to the constructor. // Possible values for the oflag argument to the constructor.
enum { enum {
@ -313,7 +260,7 @@ class FMT_API file {
file(file&& other) noexcept : fd_(other.fd_) { other.fd_ = -1; } file(file&& other) noexcept : fd_(other.fd_) { other.fd_ = -1; }
// Move assignment is not noexcept because close may throw. // Move assignment is not noexcept because close may throw.
file& operator=(file&& other) { auto operator=(file&& other) -> file& {
close(); close();
fd_ = other.fd_; fd_ = other.fd_;
other.fd_ = -1; other.fd_ = -1;
@ -324,24 +271,24 @@ class FMT_API file {
~file() noexcept; ~file() noexcept;
// Returns the file descriptor. // Returns the file descriptor.
int descriptor() const noexcept { return fd_; } auto descriptor() const noexcept -> int { return fd_; }
// Closes the file. // Closes the file.
void close(); void close();
// Returns the file size. The size has signed type for consistency with // Returns the file size. The size has signed type for consistency with
// stat::st_size. // stat::st_size.
long long size() const; auto size() const -> long long;
// Attempts to read count bytes from the file into the specified buffer. // Attempts to read count bytes from the file into the specified buffer.
size_t read(void* buffer, size_t count); auto read(void* buffer, size_t count) -> size_t;
// Attempts to write count bytes from the specified buffer to the file. // Attempts to write count bytes from the specified buffer to the file.
size_t write(const void* buffer, size_t count); auto write(const void* buffer, size_t count) -> size_t;
// Duplicates a file descriptor with the dup function and returns // Duplicates a file descriptor with the dup function and returns
// the duplicate as a file object. // the duplicate as a file object.
static file dup(int fd); static auto dup(int fd) -> file;
// Makes fd be the copy of this file descriptor, closing fd first if // Makes fd be the copy of this file descriptor, closing fd first if
// necessary. // necessary.
@ -351,24 +298,35 @@ class FMT_API file {
// necessary. // necessary.
void dup2(int fd, std::error_code& ec) noexcept; void dup2(int fd, std::error_code& ec) noexcept;
// Creates a pipe setting up read_end and write_end file objects for reading
// and writing respectively.
static void pipe(file& read_end, file& write_end);
// Creates a buffered_file object associated with this file and detaches // Creates a buffered_file object associated with this file and detaches
// this file object from the file. // this file object from the file.
buffered_file fdopen(const char* mode); auto fdopen(const char* mode) -> buffered_file;
# if defined(_WIN32) && !defined(__MINGW32__)
// Opens a file and constructs a file object representing this file by
// wcstring_view filename. Windows only.
static file open_windows_file(wcstring_view path, int oflag);
# endif
};
struct FMT_API pipe {
file read_end;
file write_end;
// Creates a pipe setting up read_end and write_end file objects for reading
// and writing respectively.
pipe();
}; };
// Returns the memory page size. // Returns the memory page size.
long getpagesize(); auto getpagesize() -> long;
FMT_BEGIN_DETAIL_NAMESPACE namespace detail {
struct buffer_size { struct buffer_size {
buffer_size() = default; buffer_size() = default;
size_t value = 0; size_t value = 0;
buffer_size operator=(size_t val) const { auto operator=(size_t val) const -> buffer_size {
auto bs = buffer_size(); auto bs = buffer_size();
bs.value = val; bs.value = val;
return bs; return bs;
@ -400,82 +358,82 @@ struct ostream_params {
# endif # endif
}; };
FMT_END_DETAIL_NAMESPACE class file_buffer final : public buffer<char> {
// Added {} below to work around default constructor error known to
// occur in Xcode versions 7.2.1 and 8.2.1.
constexpr detail::buffer_size buffer_size{};
/** A fast output stream which is not thread-safe. */
class FMT_API ostream final : private detail::buffer<char> {
private: private:
file file_; file file_;
void grow(size_t) override; FMT_API static void grow(buffer<char>& buf, size_t);
ostream(cstring_view path, const detail::ostream_params& params)
: file_(path, params.oflag) {
set(new char[params.buffer_size], params.buffer_size);
}
public: public:
ostream(ostream&& other) FMT_API file_buffer(cstring_view path, const ostream_params& params);
: detail::buffer<char>(other.data(), other.size(), other.capacity()), FMT_API file_buffer(file_buffer&& other) noexcept;
file_(std::move(other.file_)) { FMT_API ~file_buffer();
other.clear();
other.set(nullptr, 0);
}
~ostream() {
flush();
delete[] data();
}
void flush() { void flush() {
if (size() == 0) return; if (size() == 0) return;
file_.write(data(), size()); file_.write(data(), size() * sizeof(data()[0]));
clear(); clear();
} }
template <typename... T>
friend ostream output_file(cstring_view path, T... params);
void close() { void close() {
flush(); flush();
file_.close(); file_.close();
} }
};
/** } // namespace detail
Formats ``args`` according to specifications in ``fmt`` and writes the
output to the file. constexpr auto buffer_size = detail::buffer_size();
*/
/// A fast output stream for writing from a single thread. Writing from
/// multiple threads without external synchronization may result in a data race.
class FMT_API ostream {
private:
FMT_MSC_WARNING(suppress : 4251)
detail::file_buffer buffer_;
ostream(cstring_view path, const detail::ostream_params& params)
: buffer_(path, params) {}
public:
ostream(ostream&& other) : buffer_(std::move(other.buffer_)) {}
~ostream();
void flush() { buffer_.flush(); }
template <typename... T>
friend auto output_file(cstring_view path, T... params) -> ostream;
void close() { buffer_.close(); }
/// Formats `args` according to specifications in `fmt` and writes the
/// output to the file.
template <typename... T> void print(format_string<T...> fmt, T&&... args) { template <typename... T> void print(format_string<T...> fmt, T&&... args) {
vformat_to(detail::buffer_appender<char>(*this), fmt, vformat_to(appender(buffer_), fmt, fmt::make_format_args(args...));
fmt::make_format_args(args...));
} }
}; };
/** /**
\rst * Opens a file for writing. Supported parameters passed in `params`:
Opens a file for writing. Supported parameters passed in *params*: *
* - `<integer>`: Flags passed to [open](
* ``<integer>``: Flags passed to `open * https://pubs.opengroup.org/onlinepubs/007904875/functions/open.html)
<https://pubs.opengroup.org/onlinepubs/007904875/functions/open.html>`_ * (`file::WRONLY | file::CREATE | file::TRUNC` by default)
(``file::WRONLY | file::CREATE | file::TRUNC`` by default) * - `buffer_size=<integer>`: Output buffer size
* ``buffer_size=<integer>``: Output buffer size *
* **Example**:
**Example**:: *
* auto out = fmt::output_file("guide.txt");
auto out = fmt::output_file("guide.txt"); * out.print("Don't {}", "Panic");
out.print("Don't {}", "Panic");
\endrst
*/ */
template <typename... T> template <typename... T>
inline ostream output_file(cstring_view path, T... params) { inline auto output_file(cstring_view path, T... params) -> ostream {
return {path, detail::ostream_params(params...)}; return {path, detail::ostream_params(params...)};
} }
#endif // FMT_USE_FCNTL #endif // FMT_USE_FCNTL
FMT_MODULE_EXPORT_END FMT_END_EXPORT
FMT_END_NAMESPACE FMT_END_NAMESPACE
#endif // FMT_OS_H_ #endif // FMT_OS_H_

View File

@ -8,84 +8,71 @@
#ifndef FMT_OSTREAM_H_ #ifndef FMT_OSTREAM_H_
#define FMT_OSTREAM_H_ #define FMT_OSTREAM_H_
#include <fstream> #ifndef FMT_MODULE
#include <ostream> # include <fstream> // std::filebuf
#endif
#include "format.h" #ifdef _WIN32
# ifdef __GLIBCXX__
# include <ext/stdio_filebuf.h>
# include <ext/stdio_sync_filebuf.h>
# endif
# include <io.h>
#endif
#include "chrono.h" // formatbuf
FMT_BEGIN_NAMESPACE FMT_BEGIN_NAMESPACE
template <typename OutputIt, typename Char> class basic_printf_context;
namespace detail { namespace detail {
// Checks if T has a user-defined operator<<.
template <typename T, typename Char, typename Enable = void>
class is_streamable {
private:
template <typename U>
static auto test(int)
-> bool_constant<sizeof(std::declval<std::basic_ostream<Char>&>()
<< std::declval<U>()) != 0>;
template <typename> static auto test(...) -> std::false_type;
using result = decltype(test<T>(0));
public:
is_streamable() = default;
static const bool value = result::value;
};
// Formatting of built-in types and arrays is intentionally disabled because
// it's handled by standard (non-ostream) formatters.
template <typename T, typename Char>
struct is_streamable<
T, Char,
enable_if_t<
std::is_arithmetic<T>::value || std::is_array<T>::value ||
std::is_pointer<T>::value || std::is_same<T, char8_type>::value ||
std::is_convertible<T, fmt::basic_string_view<Char>>::value ||
std::is_same<T, std_string_view<Char>>::value ||
(std::is_convertible<T, int>::value && !std::is_enum<T>::value)>>
: std::false_type {};
template <typename Char> FILE* get_file(std::basic_filebuf<Char>&) {
return nullptr;
}
struct dummy_filebuf {
FILE* _Myfile;
};
template <typename T, typename U = int> struct ms_filebuf {
using type = dummy_filebuf;
};
template <typename T> struct ms_filebuf<T, decltype(T::_Myfile, 0)> {
using type = T;
};
using filebuf_type = ms_filebuf<std::filebuf>::type;
FILE* get_file(filebuf_type& buf);
// Generate a unique explicit instantion in every translation unit using a tag // Generate a unique explicit instantion in every translation unit using a tag
// type in an anonymous namespace. // type in an anonymous namespace.
namespace { namespace {
struct filebuf_access_tag {}; struct file_access_tag {};
} // namespace } // namespace
template <typename Tag, typename FileMemberPtr, FileMemberPtr file> template <typename Tag, typename BufType, FILE* BufType::*FileMemberPtr>
class filebuf_access { class file_access {
friend FILE* get_file(filebuf_type& buf) { return buf.*file; } friend auto get_file(BufType& obj) -> FILE* { return obj.*FileMemberPtr; }
}; };
template class filebuf_access<filebuf_access_tag,
decltype(&filebuf_type::_Myfile),
&filebuf_type::_Myfile>;
inline bool write(std::filebuf& buf, fmt::string_view data) { #if FMT_MSC_VERSION
print(get_file(buf), data); template class file_access<file_access_tag, std::filebuf,
return true; &std::filebuf::_Myfile>;
auto get_file(std::filebuf&) -> FILE*;
#endif
inline auto write_ostream_unicode(std::ostream& os, fmt::string_view data)
-> bool {
FILE* f = nullptr;
#if FMT_MSC_VERSION && FMT_USE_RTTI
if (auto* buf = dynamic_cast<std::filebuf*>(os.rdbuf()))
f = get_file(*buf);
else
return false;
#elif defined(_WIN32) && defined(__GLIBCXX__) && FMT_USE_RTTI
auto* rdbuf = os.rdbuf();
if (auto* sfbuf = dynamic_cast<__gnu_cxx::stdio_sync_filebuf<char>*>(rdbuf))
f = sfbuf->file();
else if (auto* fbuf = dynamic_cast<__gnu_cxx::stdio_filebuf<char>*>(rdbuf))
f = fbuf->file();
else
return false;
#else
ignore_unused(os, data, f);
#endif
#ifdef _WIN32
if (f) {
int fd = _fileno(f);
if (_isatty(fd)) {
os.flush();
return write_console(fd, data);
} }
inline bool write(std::wfilebuf&, fmt::basic_string_view<wchar_t>) { }
#endif
return false;
}
inline auto write_ostream_unicode(std::wostream&,
fmt::basic_string_view<wchar_t>) -> bool {
return false; return false;
} }
@ -93,10 +80,6 @@ inline bool write(std::wfilebuf&, fmt::basic_string_view<wchar_t>) {
// It is a separate function rather than a part of vprint to simplify testing. // It is a separate function rather than a part of vprint to simplify testing.
template <typename Char> template <typename Char>
void write_buffer(std::basic_ostream<Char>& os, buffer<Char>& buf) { void write_buffer(std::basic_ostream<Char>& os, buffer<Char>& buf) {
if (const_check(FMT_MSC_VER)) {
auto filebuf = dynamic_cast<std::basic_filebuf<Char>*>(os.rdbuf());
if (filebuf && write(*filebuf, {buf.data(), buf.size()})) return;
}
const Char* buf_data = buf.data(); const Char* buf_data = buf.data();
using unsigned_streamsize = std::make_unsigned<std::streamsize>::type; using unsigned_streamsize = std::make_unsigned<std::streamsize>::type;
unsigned_streamsize size = buf.size(); unsigned_streamsize size = buf.size();
@ -110,26 +93,31 @@ void write_buffer(std::basic_ostream<Char>& os, buffer<Char>& buf) {
} }
template <typename Char, typename T> template <typename Char, typename T>
void format_value(buffer<Char>& buf, const T& value, void format_value(buffer<Char>& buf, const T& value) {
locale_ref loc = locale_ref()) {
auto&& format_buf = formatbuf<std::basic_streambuf<Char>>(buf); auto&& format_buf = formatbuf<std::basic_streambuf<Char>>(buf);
auto&& output = std::basic_ostream<Char>(&format_buf); auto&& output = std::basic_ostream<Char>(&format_buf);
#if !defined(FMT_STATIC_THOUSANDS_SEPARATOR) #if !defined(FMT_STATIC_THOUSANDS_SEPARATOR)
if (loc) output.imbue(loc.get<std::locale>()); output.imbue(std::locale::classic()); // The default is always unlocalized.
#endif #endif
output << value; output << value;
output.exceptions(std::ios_base::failbit | std::ios_base::badbit); output.exceptions(std::ios_base::failbit | std::ios_base::badbit);
} }
template <typename T> struct streamed_view {
const T& value;
};
} // namespace detail } // namespace detail
// Formats an object of type T that has an overloaded ostream operator<<. // Formats an object of type T that has an overloaded ostream operator<<.
template <typename Char> template <typename Char>
struct basic_ostream_formatter : formatter<basic_string_view<Char>, Char> { struct basic_ostream_formatter : formatter<basic_string_view<Char>, Char> {
template <typename T, typename OutputIt> void set_debug_format() = delete;
auto format(const T& value, basic_format_context<OutputIt, Char>& ctx) const
-> OutputIt { template <typename T, typename Context>
auto format(const T& value, Context& ctx) const -> decltype(ctx.out()) {
auto buffer = basic_memory_buffer<Char>(); auto buffer = basic_memory_buffer<Char>();
format_value(buffer, value, ctx.locale()); detail::format_value(buffer, value);
return formatter<basic_string_view<Char>, Char>::format( return formatter<basic_string_view<Char>, Char>::format(
{buffer.data(), buffer.size()}, ctx); {buffer.data(), buffer.size()}, ctx);
} }
@ -137,55 +125,85 @@ struct basic_ostream_formatter : formatter<basic_string_view<Char>, Char> {
using ostream_formatter = basic_ostream_formatter<char>; using ostream_formatter = basic_ostream_formatter<char>;
namespace detail {
// Formats an object of type T that has an overloaded ostream operator<<.
template <typename T, typename Char> template <typename T, typename Char>
struct fallback_formatter<T, Char, enable_if_t<is_streamable<T, Char>::value>> struct formatter<detail::streamed_view<T>, Char>
: basic_ostream_formatter<Char> { : basic_ostream_formatter<Char> {
using basic_ostream_formatter<Char>::format; template <typename Context>
// DEPRECATED! auto format(detail::streamed_view<T> view, Context& ctx) const
template <typename OutputIt> -> decltype(ctx.out()) {
auto format(const T& value, basic_printf_context<OutputIt, Char>& ctx) const return basic_ostream_formatter<Char>::format(view.value, ctx);
-> OutputIt {
auto buffer = basic_memory_buffer<Char>();
format_value(buffer, value, ctx.locale());
return std::copy(buffer.begin(), buffer.end(), ctx.out());
} }
}; };
} // namespace detail
FMT_MODULE_EXPORT /**
template <typename Char> * Returns a view that formats `value` via an ostream `operator<<`.
void vprint(std::basic_ostream<Char>& os, *
basic_string_view<type_identity_t<Char>> format_str, * **Example**:
basic_format_args<buffer_context<type_identity_t<Char>>> args) { *
auto buffer = basic_memory_buffer<Char>(); * fmt::print("Current thread id: {}\n",
* fmt::streamed(std::this_thread::get_id()));
*/
template <typename T>
constexpr auto streamed(const T& value) -> detail::streamed_view<T> {
return {value};
}
namespace detail {
inline void vprint_directly(std::ostream& os, string_view format_str,
format_args args) {
auto buffer = memory_buffer();
detail::vformat_to(buffer, format_str, args); detail::vformat_to(buffer, format_str, args);
detail::write_buffer(os, buffer); detail::write_buffer(os, buffer);
} }
/** } // namespace detail
\rst
Prints formatted data to the stream *os*.
**Example**:: FMT_EXPORT template <typename Char>
void vprint(std::basic_ostream<Char>& os,
fmt::print(cerr, "Don't {}!", "panic"); basic_string_view<type_identity_t<Char>> format_str,
\endrst typename detail::vformat_args<Char>::type args) {
*/ auto buffer = basic_memory_buffer<Char>();
FMT_MODULE_EXPORT detail::vformat_to(buffer, format_str, args);
template <typename... T> if (detail::write_ostream_unicode(os, {buffer.data(), buffer.size()})) return;
void print(std::ostream& os, format_string<T...> fmt, T&&... args) { detail::write_buffer(os, buffer);
vprint(os, fmt, fmt::make_format_args(args...));
} }
FMT_MODULE_EXPORT /**
* Prints formatted data to the stream `os`.
*
* **Example**:
*
* fmt::print(cerr, "Don't {}!", "panic");
*/
FMT_EXPORT template <typename... T>
void print(std::ostream& os, format_string<T...> fmt, T&&... args) {
const auto& vargs = fmt::make_format_args(args...);
if (detail::use_utf8())
vprint(os, fmt, vargs);
else
detail::vprint_directly(os, fmt, vargs);
}
FMT_EXPORT
template <typename... Args> template <typename... Args>
void print(std::wostream& os, void print(std::wostream& os,
basic_format_string<wchar_t, type_identity_t<Args>...> fmt, basic_format_string<wchar_t, type_identity_t<Args>...> fmt,
Args&&... args) { Args&&... args) {
vprint(os, fmt, fmt::make_format_args<buffer_context<wchar_t>>(args...)); vprint(os, fmt, fmt::make_format_args<buffered_context<wchar_t>>(args...));
}
FMT_EXPORT template <typename... T>
void println(std::ostream& os, format_string<T...> fmt, T&&... args) {
fmt::print(os, "{}\n", fmt::format(fmt, std::forward<T>(args)...));
}
FMT_EXPORT
template <typename... Args>
void println(std::wostream& os,
basic_format_string<wchar_t, type_identity_t<Args>...> fmt,
Args&&... args) {
print(os, L"{}\n", fmt::format(fmt, std::forward<Args>(args)...));
} }
FMT_END_NAMESPACE FMT_END_NAMESPACE

View File

@ -8,108 +8,103 @@
#ifndef FMT_PRINTF_H_ #ifndef FMT_PRINTF_H_
#define FMT_PRINTF_H_ #define FMT_PRINTF_H_
#ifndef FMT_MODULE
# include <algorithm> // std::max # include <algorithm> // std::max
# include <limits> // std::numeric_limits # include <limits> // std::numeric_limits
#include <ostream> #endif
#include "format.h" #include "format.h"
FMT_BEGIN_NAMESPACE FMT_BEGIN_NAMESPACE
FMT_MODULE_EXPORT_BEGIN FMT_BEGIN_EXPORT
template <typename T> struct printf_formatter { printf_formatter() = delete; }; template <typename T> struct printf_formatter {
printf_formatter() = delete;
template <typename Char>
class basic_printf_parse_context : public basic_format_parse_context<Char> {
using basic_format_parse_context<Char>::basic_format_parse_context;
}; };
template <typename OutputIt, typename Char> class basic_printf_context { template <typename Char> class basic_printf_context {
private: private:
OutputIt out_; basic_appender<Char> out_;
basic_format_args<basic_printf_context> args_; basic_format_args<basic_printf_context> args_;
static_assert(std::is_same<Char, char>::value ||
std::is_same<Char, wchar_t>::value,
"Unsupported code unit type.");
public: public:
using char_type = Char; using char_type = Char;
using format_arg = basic_format_arg<basic_printf_context>; using parse_context_type = basic_format_parse_context<Char>;
using parse_context_type = basic_printf_parse_context<Char>;
template <typename T> using formatter_type = printf_formatter<T>; template <typename T> using formatter_type = printf_formatter<T>;
/** /// Constructs a `printf_context` object. References to the arguments are
\rst /// stored in the context object so make sure they have appropriate lifetimes.
Constructs a ``printf_context`` object. References to the arguments are basic_printf_context(basic_appender<Char> out,
stored in the context object so make sure they have appropriate lifetimes.
\endrst
*/
basic_printf_context(OutputIt out,
basic_format_args<basic_printf_context> args) basic_format_args<basic_printf_context> args)
: out_(out), args_(args) {} : out_(out), args_(args) {}
OutputIt out() { return out_; } auto out() -> basic_appender<Char> { return out_; }
void advance_to(OutputIt it) { out_ = it; } void advance_to(basic_appender<Char>) {}
detail::locale_ref locale() { return {}; } auto locale() -> detail::locale_ref { return {}; }
format_arg arg(int id) const { return args_.get(id); } auto arg(int id) const -> basic_format_arg<basic_printf_context> {
return args_.get(id);
FMT_CONSTEXPR void on_error(const char* message) {
detail::error_handler().on_error(message);
} }
}; };
FMT_BEGIN_DETAIL_NAMESPACE namespace detail {
// Checks if a value fits in int - used to avoid warnings about comparing // Checks if a value fits in int - used to avoid warnings about comparing
// signed and unsigned integers. // signed and unsigned integers.
template <bool IsSigned> struct int_checker { template <bool IsSigned> struct int_checker {
template <typename T> static bool fits_in_int(T value) { template <typename T> static auto fits_in_int(T value) -> bool {
unsigned max = max_value<int>(); unsigned max = to_unsigned(max_value<int>());
return value <= max; return value <= max;
} }
static bool fits_in_int(bool) { return true; } static auto fits_in_int(bool) -> bool { return true; }
}; };
template <> struct int_checker<true> { template <> struct int_checker<true> {
template <typename T> static bool fits_in_int(T value) { template <typename T> static auto fits_in_int(T value) -> bool {
return value >= (std::numeric_limits<int>::min)() && return value >= (std::numeric_limits<int>::min)() &&
value <= max_value<int>(); value <= max_value<int>();
} }
static bool fits_in_int(int) { return true; } static auto fits_in_int(int) -> bool { return true; }
}; };
class printf_precision_handler { struct printf_precision_handler {
public:
template <typename T, FMT_ENABLE_IF(std::is_integral<T>::value)> template <typename T, FMT_ENABLE_IF(std::is_integral<T>::value)>
int operator()(T value) { auto operator()(T value) -> int {
if (!int_checker<std::numeric_limits<T>::is_signed>::fits_in_int(value)) if (!int_checker<std::numeric_limits<T>::is_signed>::fits_in_int(value))
FMT_THROW(format_error("number is too big")); report_error("number is too big");
return (std::max)(static_cast<int>(value), 0); return (std::max)(static_cast<int>(value), 0);
} }
template <typename T, FMT_ENABLE_IF(!std::is_integral<T>::value)> template <typename T, FMT_ENABLE_IF(!std::is_integral<T>::value)>
int operator()(T) { auto operator()(T) -> int {
FMT_THROW(format_error("precision is not integer")); report_error("precision is not integer");
return 0; return 0;
} }
}; };
// An argument visitor that returns true iff arg is a zero integer. // An argument visitor that returns true iff arg is a zero integer.
class is_zero_int { struct is_zero_int {
public:
template <typename T, FMT_ENABLE_IF(std::is_integral<T>::value)> template <typename T, FMT_ENABLE_IF(std::is_integral<T>::value)>
bool operator()(T value) { auto operator()(T value) -> bool {
return value == 0; return value == 0;
} }
template <typename T, FMT_ENABLE_IF(!std::is_integral<T>::value)> template <typename T, FMT_ENABLE_IF(!std::is_integral<T>::value)>
bool operator()(T) { auto operator()(T) -> bool {
return false; return false;
} }
}; };
template <typename T> struct make_unsigned_or_bool : std::make_unsigned<T> {}; template <typename T> struct make_unsigned_or_bool : std::make_unsigned<T> {};
template <> struct make_unsigned_or_bool<bool> { using type = bool; }; template <> struct make_unsigned_or_bool<bool> {
using type = bool;
};
template <typename T, typename Context> class arg_converter { template <typename T, typename Context> class arg_converter {
private: private:
@ -133,22 +128,23 @@ template <typename T, typename Context> class arg_converter {
if (const_check(sizeof(target_type) <= sizeof(int))) { if (const_check(sizeof(target_type) <= sizeof(int))) {
// Extra casts are used to silence warnings. // Extra casts are used to silence warnings.
if (is_signed) { if (is_signed) {
arg_ = detail::make_arg<Context>( auto n = static_cast<int>(static_cast<target_type>(value));
static_cast<int>(static_cast<target_type>(value))); arg_ = detail::make_arg<Context>(n);
} else { } else {
using unsigned_type = typename make_unsigned_or_bool<target_type>::type; using unsigned_type = typename make_unsigned_or_bool<target_type>::type;
arg_ = detail::make_arg<Context>( auto n = static_cast<unsigned>(static_cast<unsigned_type>(value));
static_cast<unsigned>(static_cast<unsigned_type>(value))); arg_ = detail::make_arg<Context>(n);
} }
} else { } else {
if (is_signed) { if (is_signed) {
// glibc's printf doesn't sign extend arguments of smaller types: // glibc's printf doesn't sign extend arguments of smaller types:
// std::printf("%lld", -42); // prints "4294967254" // std::printf("%lld", -42); // prints "4294967254"
// but we don't have to do the same because it's a UB. // but we don't have to do the same because it's a UB.
arg_ = detail::make_arg<Context>(static_cast<long long>(value)); auto n = static_cast<long long>(value);
arg_ = detail::make_arg<Context>(n);
} else { } else {
arg_ = detail::make_arg<Context>( auto n = static_cast<typename make_unsigned_or_bool<U>::type>(value);
static_cast<typename make_unsigned_or_bool<U>::type>(value)); arg_ = detail::make_arg<Context>(n);
} }
} }
} }
@ -163,7 +159,7 @@ template <typename T, typename Context> class arg_converter {
// unsigned). // unsigned).
template <typename T, typename Context, typename Char> template <typename T, typename Context, typename Char>
void convert_arg(basic_format_arg<Context>& arg, Char type) { void convert_arg(basic_format_arg<Context>& arg, Char type) {
visit_format_arg(arg_converter<T, Context>(arg, type), arg); arg.visit(arg_converter<T, Context>(arg, type));
} }
// Converts an integer argument to char for printf. // Converts an integer argument to char for printf.
@ -176,8 +172,8 @@ template <typename Context> class char_converter {
template <typename T, FMT_ENABLE_IF(std::is_integral<T>::value)> template <typename T, FMT_ENABLE_IF(std::is_integral<T>::value)>
void operator()(T value) { void operator()(T value) {
arg_ = detail::make_arg<Context>( auto c = static_cast<typename Context::char_type>(value);
static_cast<typename Context::char_type>(value)); arg_ = detail::make_arg<Context>(c);
} }
template <typename T, FMT_ENABLE_IF(!std::is_integral<T>::value)> template <typename T, FMT_ENABLE_IF(!std::is_integral<T>::value)>
@ -187,122 +183,126 @@ template <typename Context> class char_converter {
// An argument visitor that return a pointer to a C string if argument is a // An argument visitor that return a pointer to a C string if argument is a
// string or null otherwise. // string or null otherwise.
template <typename Char> struct get_cstring { template <typename Char> struct get_cstring {
template <typename T> const Char* operator()(T) { return nullptr; } template <typename T> auto operator()(T) -> const Char* { return nullptr; }
const Char* operator()(const Char* s) { return s; } auto operator()(const Char* s) -> const Char* { return s; }
}; };
// Checks if an argument is a valid printf width specifier and sets // Checks if an argument is a valid printf width specifier and sets
// left alignment if it is negative. // left alignment if it is negative.
template <typename Char> class printf_width_handler { class printf_width_handler {
private: private:
using format_specs = basic_format_specs<Char>;
format_specs& specs_; format_specs& specs_;
public: public:
explicit printf_width_handler(format_specs& specs) : specs_(specs) {} explicit printf_width_handler(format_specs& specs) : specs_(specs) {}
template <typename T, FMT_ENABLE_IF(std::is_integral<T>::value)> template <typename T, FMT_ENABLE_IF(std::is_integral<T>::value)>
unsigned operator()(T value) { auto operator()(T value) -> unsigned {
auto width = static_cast<uint32_or_64_or_128_t<T>>(value); auto width = static_cast<uint32_or_64_or_128_t<T>>(value);
if (detail::is_negative(value)) { if (detail::is_negative(value)) {
specs_.align = align::left; specs_.align = align::left;
width = 0 - width; width = 0 - width;
} }
unsigned int_max = max_value<int>(); unsigned int_max = to_unsigned(max_value<int>());
if (width > int_max) FMT_THROW(format_error("number is too big")); if (width > int_max) report_error("number is too big");
return static_cast<unsigned>(width); return static_cast<unsigned>(width);
} }
template <typename T, FMT_ENABLE_IF(!std::is_integral<T>::value)> template <typename T, FMT_ENABLE_IF(!std::is_integral<T>::value)>
unsigned operator()(T) { auto operator()(T) -> unsigned {
FMT_THROW(format_error("width is not integer")); report_error("width is not integer");
return 0; return 0;
} }
}; };
// The ``printf`` argument formatter. // Workaround for a bug with the XL compiler when initializing
template <typename OutputIt, typename Char> // printf_arg_formatter's base class.
template <typename Char>
auto make_arg_formatter(basic_appender<Char> iter, format_specs& s)
-> arg_formatter<Char> {
return {iter, s, locale_ref()};
}
// The `printf` argument formatter.
template <typename Char>
class printf_arg_formatter : public arg_formatter<Char> { class printf_arg_formatter : public arg_formatter<Char> {
private: private:
using base = arg_formatter<Char>; using base = arg_formatter<Char>;
using context_type = basic_printf_context<OutputIt, Char>; using context_type = basic_printf_context<Char>;
using format_specs = basic_format_specs<Char>;
context_type& context_; context_type& context_;
OutputIt write_null_pointer(bool is_string = false) { void write_null_pointer(bool is_string = false) {
auto s = this->specs; auto s = this->specs;
s.type = presentation_type::none; s.type = presentation_type::none;
return write_bytes(this->out, is_string ? "(null)" : "(nil)", s); write_bytes<Char>(this->out, is_string ? "(null)" : "(nil)", s);
} }
public: public:
printf_arg_formatter(OutputIt iter, format_specs& s, context_type& ctx) printf_arg_formatter(basic_appender<Char> iter, format_specs& s,
: base{iter, s, locale_ref()}, context_(ctx) {} context_type& ctx)
: base(make_arg_formatter(iter, s)), context_(ctx) {}
OutputIt operator()(monostate value) { return base::operator()(value); } void operator()(monostate value) { base::operator()(value); }
template <typename T, FMT_ENABLE_IF(detail::is_integral<T>::value)> template <typename T, FMT_ENABLE_IF(detail::is_integral<T>::value)>
OutputIt operator()(T value) { void operator()(T value) {
// MSVC2013 fails to compile separate overloads for bool and Char so use // MSVC2013 fails to compile separate overloads for bool and Char so use
// std::is_same instead. // std::is_same instead.
if (std::is_same<T, Char>::value) { if (!std::is_same<T, Char>::value) {
format_specs fmt_specs = this->specs; base::operator()(value);
if (fmt_specs.type != presentation_type::none && return;
fmt_specs.type != presentation_type::chr) { }
format_specs s = this->specs;
if (s.type != presentation_type::none && s.type != presentation_type::chr) {
return (*this)(static_cast<int>(value)); return (*this)(static_cast<int>(value));
} }
fmt_specs.sign = sign::none; s.sign = sign::none;
fmt_specs.alt = false; s.alt = false;
fmt_specs.fill[0] = ' '; // Ignore '0' flag for char types. s.fill = ' '; // Ignore '0' flag for char types.
// align::numeric needs to be overwritten here since the '0' flag is // align::numeric needs to be overwritten here since the '0' flag is
// ignored for non-numeric types // ignored for non-numeric types
if (fmt_specs.align == align::none || fmt_specs.align == align::numeric) if (s.align == align::none || s.align == align::numeric)
fmt_specs.align = align::right; s.align = align::right;
return write<Char>(this->out, static_cast<Char>(value), fmt_specs); write<Char>(this->out, static_cast<Char>(value), s);
}
return base::operator()(value);
} }
template <typename T, FMT_ENABLE_IF(std::is_floating_point<T>::value)> template <typename T, FMT_ENABLE_IF(std::is_floating_point<T>::value)>
OutputIt operator()(T value) { void operator()(T value) {
return base::operator()(value); base::operator()(value);
} }
/** Formats a null-terminated C string. */ void operator()(const char* value) {
OutputIt operator()(const char* value) { if (value)
if (value) return base::operator()(value); base::operator()(value);
return write_null_pointer(this->specs.type != presentation_type::pointer); else
write_null_pointer(this->specs.type != presentation_type::pointer);
} }
/** Formats a null-terminated wide C string. */ void operator()(const wchar_t* value) {
OutputIt operator()(const wchar_t* value) { if (value)
if (value) return base::operator()(value); base::operator()(value);
return write_null_pointer(this->specs.type != presentation_type::pointer); else
write_null_pointer(this->specs.type != presentation_type::pointer);
} }
OutputIt operator()(basic_string_view<Char> value) { void operator()(basic_string_view<Char> value) { base::operator()(value); }
return base::operator()(value);
void operator()(const void* value) {
if (value)
base::operator()(value);
else
write_null_pointer();
} }
/** Formats a pointer. */ void operator()(typename basic_format_arg<context_type>::handle handle) {
OutputIt operator()(const void* value) { auto parse_ctx = basic_format_parse_context<Char>({});
return value ? base::operator()(value) : write_null_pointer();
}
/** Formats an argument of a custom (user-defined) type. */
OutputIt operator()(typename basic_format_arg<context_type>::handle handle) {
auto parse_ctx =
basic_printf_parse_context<Char>(basic_string_view<Char>());
handle.format(parse_ctx, context_); handle.format(parse_ctx, context_);
return this->out;
} }
}; };
template <typename Char> template <typename Char>
void parse_flags(basic_format_specs<Char>& specs, const Char*& it, void parse_flags(format_specs& specs, const Char*& it, const Char* end) {
const Char* end) {
for (; it != end; ++it) { for (; it != end; ++it) {
switch (*it) { switch (*it) {
case '-': case '-':
@ -312,12 +312,10 @@ void parse_flags(basic_format_specs<Char>& specs, const Char*& it,
specs.sign = sign::plus; specs.sign = sign::plus;
break; break;
case '0': case '0':
specs.fill[0] = '0'; specs.fill = '0';
break; break;
case ' ': case ' ':
if (specs.sign != sign::plus) { if (specs.sign != sign::plus) specs.sign = sign::space;
specs.sign = sign::space;
}
break; break;
case '#': case '#':
specs.alt = true; specs.alt = true;
@ -329,8 +327,8 @@ void parse_flags(basic_format_specs<Char>& specs, const Char*& it,
} }
template <typename Char, typename GetArg> template <typename Char, typename GetArg>
int parse_header(const Char*& it, const Char* end, auto parse_header(const Char*& it, const Char* end, format_specs& specs,
basic_format_specs<Char>& specs, GetArg get_arg) { GetArg get_arg) -> int {
int arg_index = -1; int arg_index = -1;
Char c = *it; Char c = *it;
if (c >= '0' && c <= '9') { if (c >= '0' && c <= '9') {
@ -341,11 +339,11 @@ int parse_header(const Char*& it, const Char* end,
++it; ++it;
arg_index = value != -1 ? value : max_value<int>(); arg_index = value != -1 ? value : max_value<int>();
} else { } else {
if (c == '0') specs.fill[0] = '0'; if (c == '0') specs.fill = '0';
if (value != 0) { if (value != 0) {
// Nonzero value means that we parsed width and don't need to // Nonzero value means that we parsed width and don't need to
// parse it or flags again, so return now. // parse it or flags again, so return now.
if (value == -1) FMT_THROW(format_error("number is too big")); if (value == -1) report_error("number is too big");
specs.width = value; specs.width = value;
return arg_index; return arg_index;
} }
@ -356,23 +354,68 @@ int parse_header(const Char*& it, const Char* end,
if (it != end) { if (it != end) {
if (*it >= '0' && *it <= '9') { if (*it >= '0' && *it <= '9') {
specs.width = parse_nonnegative_int(it, end, -1); specs.width = parse_nonnegative_int(it, end, -1);
if (specs.width == -1) FMT_THROW(format_error("number is too big")); if (specs.width == -1) report_error("number is too big");
} else if (*it == '*') { } else if (*it == '*') {
++it; ++it;
specs.width = static_cast<int>(visit_format_arg( specs.width = static_cast<int>(
detail::printf_width_handler<Char>(specs), get_arg(-1))); get_arg(-1).visit(detail::printf_width_handler(specs)));
} }
} }
return arg_index; return arg_index;
} }
inline auto parse_printf_presentation_type(char c, type t, bool& upper)
-> presentation_type {
using pt = presentation_type;
constexpr auto integral_set = sint_set | uint_set | bool_set | char_set;
switch (c) {
case 'd':
return in(t, integral_set) ? pt::dec : pt::none;
case 'o':
return in(t, integral_set) ? pt::oct : pt::none;
case 'X':
upper = true;
FMT_FALLTHROUGH;
case 'x':
return in(t, integral_set) ? pt::hex : pt::none;
case 'E':
upper = true;
FMT_FALLTHROUGH;
case 'e':
return in(t, float_set) ? pt::exp : pt::none;
case 'F':
upper = true;
FMT_FALLTHROUGH;
case 'f':
return in(t, float_set) ? pt::fixed : pt::none;
case 'G':
upper = true;
FMT_FALLTHROUGH;
case 'g':
return in(t, float_set) ? pt::general : pt::none;
case 'A':
upper = true;
FMT_FALLTHROUGH;
case 'a':
return in(t, float_set) ? pt::hexfloat : pt::none;
case 'c':
return in(t, integral_set) ? pt::chr : pt::none;
case 's':
return in(t, string_set | cstring_set) ? pt::string : pt::none;
case 'p':
return in(t, pointer_set | cstring_set) ? pt::pointer : pt::none;
default:
return pt::none;
}
}
template <typename Char, typename Context> template <typename Char, typename Context>
void vprintf(buffer<Char>& buf, basic_string_view<Char> format, void vprintf(buffer<Char>& buf, basic_string_view<Char> format,
basic_format_args<Context> args) { basic_format_args<Context> args) {
using OutputIt = buffer_appender<Char>; using iterator = basic_appender<Char>;
auto out = OutputIt(buf); auto out = iterator(buf);
auto context = basic_printf_context<OutputIt, Char>(out, args); auto context = basic_printf_context<Char>(out, args);
auto parse_ctx = basic_printf_parse_context<Char>(format); auto parse_ctx = basic_format_parse_context<Char>(format);
// Returns the argument with specified index or, if arg_index is -1, the next // Returns the argument with specified index or, if arg_index is -1, the next
// argument. // argument.
@ -388,26 +431,24 @@ void vprintf(buffer<Char>& buf, basic_string_view<Char> format,
const Char* end = parse_ctx.end(); const Char* end = parse_ctx.end();
auto it = start; auto it = start;
while (it != end) { while (it != end) {
if (!detail::find<false, Char>(it, end, '%', it)) { if (!find<false, Char>(it, end, '%', it)) {
it = end; // detail::find leaves it == nullptr if it doesn't find '%' it = end; // find leaves it == nullptr if it doesn't find '%'.
break; break;
} }
Char c = *it++; Char c = *it++;
if (it != end && *it == c) { if (it != end && *it == c) {
out = detail::write( write(out, basic_string_view<Char>(start, to_unsigned(it - start)));
out, basic_string_view<Char>(start, detail::to_unsigned(it - start)));
start = ++it; start = ++it;
continue; continue;
} }
out = detail::write(out, basic_string_view<Char>( write(out, basic_string_view<Char>(start, to_unsigned(it - 1 - start)));
start, detail::to_unsigned(it - 1 - start)));
basic_format_specs<Char> specs; auto specs = format_specs();
specs.align = align::right; specs.align = align::right;
// Parse argument index, flags and width. // Parse argument index, flags and width.
int arg_index = parse_header(it, end, specs, get_arg); int arg_index = parse_header(it, end, specs, get_arg);
if (arg_index == 0) parse_ctx.on_error("argument not found"); if (arg_index == 0) report_error("argument not found");
// Parse precision. // Parse precision.
if (it != end && *it == '.') { if (it != end && *it == '.') {
@ -417,8 +458,8 @@ void vprintf(buffer<Char>& buf, basic_string_view<Char> format,
specs.precision = parse_nonnegative_int(it, end, 0); specs.precision = parse_nonnegative_int(it, end, 0);
} else if (c == '*') { } else if (c == '*') {
++it; ++it;
specs.precision = static_cast<int>( specs.precision =
visit_format_arg(detail::printf_precision_handler(), get_arg(-1))); static_cast<int>(get_arg(-1).visit(printf_precision_handler()));
} else { } else {
specs.precision = 0; specs.precision = 0;
} }
@ -427,32 +468,30 @@ void vprintf(buffer<Char>& buf, basic_string_view<Char> format,
auto arg = get_arg(arg_index); auto arg = get_arg(arg_index);
// For d, i, o, u, x, and X conversion specifiers, if a precision is // For d, i, o, u, x, and X conversion specifiers, if a precision is
// specified, the '0' flag is ignored // specified, the '0' flag is ignored
if (specs.precision >= 0 && arg.is_integral()) if (specs.precision >= 0 && arg.is_integral()) {
specs.fill[0] = // Ignore '0' for non-numeric types or if '-' present.
' '; // Ignore '0' flag for non-numeric types or if '-' present. specs.fill = ' ';
if (specs.precision >= 0 && arg.type() == detail::type::cstring_type) { }
auto str = visit_format_arg(detail::get_cstring<Char>(), arg); if (specs.precision >= 0 && arg.type() == type::cstring_type) {
auto str = arg.visit(get_cstring<Char>());
auto str_end = str + specs.precision; auto str_end = str + specs.precision;
auto nul = std::find(str, str_end, Char()); auto nul = std::find(str, str_end, Char());
arg = detail::make_arg<basic_printf_context<OutputIt, Char>>( auto sv = basic_string_view<Char>(
basic_string_view<Char>( str, to_unsigned(nul != str_end ? nul - str : specs.precision));
str, detail::to_unsigned(nul != str_end ? nul - str arg = make_arg<basic_printf_context<Char>>(sv);
: specs.precision)));
} }
if (specs.alt && visit_format_arg(detail::is_zero_int(), arg)) if (specs.alt && arg.visit(is_zero_int())) specs.alt = false;
specs.alt = false; if (specs.fill.template get<Char>() == '0') {
if (specs.fill[0] == '0') {
if (arg.is_arithmetic() && specs.align != align::left) if (arg.is_arithmetic() && specs.align != align::left)
specs.align = align::numeric; specs.align = align::numeric;
else else
specs.fill[0] = ' '; // Ignore '0' flag for non-numeric types or if '-' specs.fill = ' '; // Ignore '0' flag for non-numeric types or if '-'
// flag is also present. // flag is also present.
} }
// Parse length and convert the argument to the required type. // Parse length and convert the argument to the required type.
c = it != end ? *it++ : 0; c = it != end ? *it++ : 0;
Char t = it != end ? *it : 0; Char t = it != end ? *it : 0;
using detail::convert_arg;
switch (c) { switch (c) {
case 'h': case 'h':
if (t == 'h') { if (t == 'h') {
@ -491,7 +530,7 @@ void vprintf(buffer<Char>& buf, basic_string_view<Char> format,
} }
// Parse type. // Parse type.
if (it == end) FMT_THROW(format_error("invalid format string")); if (it == end) report_error("invalid format string");
char type = static_cast<char>(*it++); char type = static_cast<char>(*it++);
if (arg.is_integral()) { if (arg.is_integral()) {
// Normalize type. // Normalize type.
@ -501,157 +540,117 @@ void vprintf(buffer<Char>& buf, basic_string_view<Char> format,
type = 'd'; type = 'd';
break; break;
case 'c': case 'c':
visit_format_arg( arg.visit(char_converter<basic_printf_context<Char>>(arg));
detail::char_converter<basic_printf_context<OutputIt, Char>>(arg),
arg);
break; break;
} }
} }
specs.type = parse_presentation_type(type); bool upper = false;
specs.type = parse_printf_presentation_type(type, arg.type(), upper);
if (specs.type == presentation_type::none) if (specs.type == presentation_type::none)
parse_ctx.on_error("invalid type specifier"); report_error("invalid format specifier");
specs.upper = upper;
start = it; start = it;
// Format argument. // Format argument.
out = visit_format_arg( arg.visit(printf_arg_formatter<Char>(out, specs, context));
detail::printf_arg_formatter<OutputIt, Char>(out, specs, context), arg);
} }
detail::write(out, basic_string_view<Char>(start, to_unsigned(it - start))); write(out, basic_string_view<Char>(start, to_unsigned(it - start)));
} }
FMT_END_DETAIL_NAMESPACE } // namespace detail
template <typename Char> using printf_context = basic_printf_context<char>;
using basic_printf_context_t = using wprintf_context = basic_printf_context<wchar_t>;
basic_printf_context<detail::buffer_appender<Char>, Char>;
using printf_context = basic_printf_context_t<char>;
using wprintf_context = basic_printf_context_t<wchar_t>;
using printf_args = basic_format_args<printf_context>; using printf_args = basic_format_args<printf_context>;
using wprintf_args = basic_format_args<wprintf_context>; using wprintf_args = basic_format_args<wprintf_context>;
/** /// Constructs an `format_arg_store` object that contains references to
\rst /// arguments and can be implicitly converted to `printf_args`.
Constructs an `~fmt::format_arg_store` object that contains references to template <typename Char = char, typename... T>
arguments and can be implicitly converted to `~fmt::printf_args`. inline auto make_printf_args(T&... args)
\endrst -> decltype(fmt::make_format_args<basic_printf_context<Char>>(args...)) {
*/ return fmt::make_format_args<basic_printf_context<Char>>(args...);
template <typename... T>
inline auto make_printf_args(const T&... args)
-> format_arg_store<printf_context, T...> {
return {args...};
} }
/** template <typename Char> struct vprintf_args {
\rst using type = basic_format_args<basic_printf_context<Char>>;
Constructs an `~fmt::format_arg_store` object that contains references to };
arguments and can be implicitly converted to `~fmt::wprintf_args`.
\endrst
*/
template <typename... T>
inline auto make_wprintf_args(const T&... args)
-> format_arg_store<wprintf_context, T...> {
return {args...};
}
template <typename S, typename Char = char_t<S>> template <typename Char>
inline auto vsprintf( inline auto vsprintf(basic_string_view<Char> fmt,
const S& fmt, typename vprintf_args<Char>::type args)
basic_format_args<basic_printf_context_t<type_identity_t<Char>>> args)
-> std::basic_string<Char> { -> std::basic_string<Char> {
basic_memory_buffer<Char> buffer; auto buf = basic_memory_buffer<Char>();
vprintf(buffer, to_string_view(fmt), args); detail::vprintf(buf, fmt, args);
return to_string(buffer); return to_string(buf);
} }
/** /**
\rst * Formats `args` according to specifications in `fmt` and returns the result
Formats arguments and returns the result as a string. * as as string.
*
**Example**:: * **Example**:
*
std::string message = fmt::sprintf("The answer is %d", 42); * std::string message = fmt::sprintf("The answer is %d", 42);
\endrst
*/ */
template <typename S, typename... T, template <typename S, typename... T, typename Char = char_t<S>>
typename Char = enable_if_t<detail::is_string<S>::value, char_t<S>>>
inline auto sprintf(const S& fmt, const T&... args) -> std::basic_string<Char> { inline auto sprintf(const S& fmt, const T&... args) -> std::basic_string<Char> {
using context = basic_printf_context_t<Char>; return vsprintf(detail::to_string_view(fmt),
return vsprintf(to_string_view(fmt), fmt::make_format_args<context>(args...)); fmt::make_format_args<basic_printf_context<Char>>(args...));
} }
template <typename S, typename Char = char_t<S>> template <typename Char>
inline auto vfprintf( inline auto vfprintf(std::FILE* f, basic_string_view<Char> fmt,
std::FILE* f, const S& fmt, typename vprintf_args<Char>::type args) -> int {
basic_format_args<basic_printf_context_t<type_identity_t<Char>>> args) auto buf = basic_memory_buffer<Char>();
-> int { detail::vprintf(buf, fmt, args);
basic_memory_buffer<Char> buffer; size_t size = buf.size();
vprintf(buffer, to_string_view(fmt), args); return std::fwrite(buf.data(), sizeof(Char), size, f) < size
size_t size = buffer.size();
return std::fwrite(buffer.data(), sizeof(Char), size, f) < size
? -1 ? -1
: static_cast<int>(size); : static_cast<int>(size);
} }
/** /**
\rst * Formats `args` according to specifications in `fmt` and writes the output
Prints formatted data to the file *f*. * to `f`.
*
**Example**:: * **Example**:
*
fmt::fprintf(stderr, "Don't %s!", "panic"); * fmt::fprintf(stderr, "Don't %s!", "panic");
\endrst
*/ */
template <typename S, typename... T, typename Char = char_t<S>> template <typename S, typename... T, typename Char = char_t<S>>
inline auto fprintf(std::FILE* f, const S& fmt, const T&... args) -> int { inline auto fprintf(std::FILE* f, const S& fmt, const T&... args) -> int {
using context = basic_printf_context_t<Char>; return vfprintf(f, detail::to_string_view(fmt),
return vfprintf(f, to_string_view(fmt), make_printf_args<Char>(args...));
fmt::make_format_args<context>(args...));
} }
template <typename S, typename Char = char_t<S>> template <typename Char>
inline auto vprintf( FMT_DEPRECATED inline auto vprintf(basic_string_view<Char> fmt,
const S& fmt, typename vprintf_args<Char>::type args)
basic_format_args<basic_printf_context_t<type_identity_t<Char>>> args)
-> int { -> int {
return vfprintf(stdout, to_string_view(fmt), args); return vfprintf(stdout, fmt, args);
} }
/** /**
\rst * Formats `args` according to specifications in `fmt` and writes the output
Prints formatted data to ``stdout``. * to `stdout`.
*
**Example**:: * **Example**:
*
fmt::printf("Elapsed time: %.2f seconds", 1.23); * fmt::printf("Elapsed time: %.2f seconds", 1.23);
\endrst
*/ */
template <typename S, typename... T, FMT_ENABLE_IF(detail::is_string<S>::value)> template <typename... T>
inline auto printf(const S& fmt, const T&... args) -> int { inline auto printf(string_view fmt, const T&... args) -> int {
return vprintf( return vfprintf(stdout, fmt, make_printf_args(args...));
to_string_view(fmt),
fmt::make_format_args<basic_printf_context_t<char_t<S>>>(args...));
} }
template <typename... T>
template <typename S, typename Char = char_t<S>> FMT_DEPRECATED inline auto printf(basic_string_view<wchar_t> fmt,
FMT_DEPRECATED auto vfprintf(
std::basic_ostream<Char>& os, const S& fmt,
basic_format_args<basic_printf_context_t<type_identity_t<Char>>> args)
-> int {
basic_memory_buffer<Char> buffer;
vprintf(buffer, to_string_view(fmt), args);
os.write(buffer.data(), static_cast<std::streamsize>(buffer.size()));
return static_cast<int>(buffer.size());
}
template <typename S, typename... T, typename Char = char_t<S>>
FMT_DEPRECATED auto fprintf(std::basic_ostream<Char>& os, const S& fmt,
const T&... args) -> int { const T&... args) -> int {
return vfprintf(os, to_string_view(fmt), return vfprintf(stdout, fmt, make_printf_args<wchar_t>(args...));
fmt::make_format_args<basic_printf_context_t<Char>>(args...));
} }
FMT_MODULE_EXPORT_END FMT_END_EXPORT
FMT_END_NAMESPACE FMT_END_NAMESPACE
#endif // FMT_PRINTF_H_ #endif // FMT_PRINTF_H_

File diff suppressed because it is too large Load Diff

699
external/fmt/include/fmt/std.h vendored Normal file
View File

@ -0,0 +1,699 @@
// Formatting library for C++ - formatters for standard library types
//
// Copyright (c) 2012 - present, Victor Zverovich
// All rights reserved.
//
// For the license information refer to format.h.
#ifndef FMT_STD_H_
#define FMT_STD_H_
#include "format.h"
#include "ostream.h"
#ifndef FMT_MODULE
# include <atomic>
# include <bitset>
# include <complex>
# include <cstdlib>
# include <exception>
# include <memory>
# include <thread>
# include <type_traits>
# include <typeinfo>
# include <utility>
# include <vector>
// Check FMT_CPLUSPLUS to suppress a bogus warning in MSVC.
# if FMT_CPLUSPLUS >= 201703L
# if FMT_HAS_INCLUDE(<filesystem>)
# include <filesystem>
# endif
# if FMT_HAS_INCLUDE(<variant>)
# include <variant>
# endif
# if FMT_HAS_INCLUDE(<optional>)
# include <optional>
# endif
# endif
// Use > instead of >= in the version check because <source_location> may be
// available after C++17 but before C++20 is marked as implemented.
# if FMT_CPLUSPLUS > 201703L && FMT_HAS_INCLUDE(<source_location>)
# include <source_location>
# endif
# if FMT_CPLUSPLUS > 202002L && FMT_HAS_INCLUDE(<expected>)
# include <expected>
# endif
#endif // FMT_MODULE
#if FMT_HAS_INCLUDE(<version>)
# include <version>
#endif
// GCC 4 does not support FMT_HAS_INCLUDE.
#if FMT_HAS_INCLUDE(<cxxabi.h>) || defined(__GLIBCXX__)
# include <cxxabi.h>
// Android NDK with gabi++ library on some architectures does not implement
// abi::__cxa_demangle().
# ifndef __GABIXX_CXXABI_H__
# define FMT_HAS_ABI_CXA_DEMANGLE
# endif
#endif
// For older Xcode versions, __cpp_lib_xxx flags are inaccurately defined.
#ifndef FMT_CPP_LIB_FILESYSTEM
# ifdef __cpp_lib_filesystem
# define FMT_CPP_LIB_FILESYSTEM __cpp_lib_filesystem
# else
# define FMT_CPP_LIB_FILESYSTEM 0
# endif
#endif
#ifndef FMT_CPP_LIB_VARIANT
# ifdef __cpp_lib_variant
# define FMT_CPP_LIB_VARIANT __cpp_lib_variant
# else
# define FMT_CPP_LIB_VARIANT 0
# endif
#endif
#if FMT_CPP_LIB_FILESYSTEM
FMT_BEGIN_NAMESPACE
namespace detail {
template <typename Char, typename PathChar>
auto get_path_string(const std::filesystem::path& p,
const std::basic_string<PathChar>& native) {
if constexpr (std::is_same_v<Char, char> && std::is_same_v<PathChar, wchar_t>)
return to_utf8<wchar_t>(native, to_utf8_error_policy::replace);
else
return p.string<Char>();
}
template <typename Char, typename PathChar>
void write_escaped_path(basic_memory_buffer<Char>& quoted,
const std::filesystem::path& p,
const std::basic_string<PathChar>& native) {
if constexpr (std::is_same_v<Char, char> &&
std::is_same_v<PathChar, wchar_t>) {
auto buf = basic_memory_buffer<wchar_t>();
write_escaped_string<wchar_t>(std::back_inserter(buf), native);
bool valid = to_utf8<wchar_t>::convert(quoted, {buf.data(), buf.size()});
FMT_ASSERT(valid, "invalid utf16");
} else if constexpr (std::is_same_v<Char, PathChar>) {
write_escaped_string<std::filesystem::path::value_type>(
std::back_inserter(quoted), native);
} else {
write_escaped_string<Char>(std::back_inserter(quoted), p.string<Char>());
}
}
} // namespace detail
FMT_EXPORT
template <typename Char> struct formatter<std::filesystem::path, Char> {
private:
format_specs specs_;
detail::arg_ref<Char> width_ref_;
bool debug_ = false;
char path_type_ = 0;
public:
FMT_CONSTEXPR void set_debug_format(bool set = true) { debug_ = set; }
template <typename ParseContext> FMT_CONSTEXPR auto parse(ParseContext& ctx) {
auto it = ctx.begin(), end = ctx.end();
if (it == end) return it;
it = detail::parse_align(it, end, specs_);
if (it == end) return it;
it = detail::parse_dynamic_spec(it, end, specs_.width, width_ref_, ctx);
if (it != end && *it == '?') {
debug_ = true;
++it;
}
if (it != end && (*it == 'g')) path_type_ = detail::to_ascii(*it++);
return it;
}
template <typename FormatContext>
auto format(const std::filesystem::path& p, FormatContext& ctx) const {
auto specs = specs_;
auto path_string =
!path_type_ ? p.native()
: p.generic_string<std::filesystem::path::value_type>();
detail::handle_dynamic_spec<detail::width_checker>(specs.width, width_ref_,
ctx);
if (!debug_) {
auto s = detail::get_path_string<Char>(p, path_string);
return detail::write(ctx.out(), basic_string_view<Char>(s), specs);
}
auto quoted = basic_memory_buffer<Char>();
detail::write_escaped_path(quoted, p, path_string);
return detail::write(ctx.out(),
basic_string_view<Char>(quoted.data(), quoted.size()),
specs);
}
};
class path : public std::filesystem::path {
public:
auto display_string() const -> std::string {
const std::filesystem::path& base = *this;
return fmt::format(FMT_STRING("{}"), base);
}
auto system_string() const -> std::string { return string(); }
auto generic_display_string() const -> std::string {
const std::filesystem::path& base = *this;
return fmt::format(FMT_STRING("{:g}"), base);
}
auto generic_system_string() const -> std::string { return generic_string(); }
};
FMT_END_NAMESPACE
#endif // FMT_CPP_LIB_FILESYSTEM
FMT_BEGIN_NAMESPACE
FMT_EXPORT
template <std::size_t N, typename Char>
struct formatter<std::bitset<N>, Char> : nested_formatter<string_view> {
private:
// Functor because C++11 doesn't support generic lambdas.
struct writer {
const std::bitset<N>& bs;
template <typename OutputIt>
FMT_CONSTEXPR auto operator()(OutputIt out) -> OutputIt {
for (auto pos = N; pos > 0; --pos) {
out = detail::write<Char>(out, bs[pos - 1] ? Char('1') : Char('0'));
}
return out;
}
};
public:
template <typename FormatContext>
auto format(const std::bitset<N>& bs, FormatContext& ctx) const
-> decltype(ctx.out()) {
return write_padded(ctx, writer{bs});
}
};
FMT_EXPORT
template <typename Char>
struct formatter<std::thread::id, Char> : basic_ostream_formatter<Char> {};
FMT_END_NAMESPACE
#ifdef __cpp_lib_optional
FMT_BEGIN_NAMESPACE
FMT_EXPORT
template <typename T, typename Char>
struct formatter<std::optional<T>, Char,
std::enable_if_t<is_formattable<T, Char>::value>> {
private:
formatter<T, Char> underlying_;
static constexpr basic_string_view<Char> optional =
detail::string_literal<Char, 'o', 'p', 't', 'i', 'o', 'n', 'a', 'l',
'('>{};
static constexpr basic_string_view<Char> none =
detail::string_literal<Char, 'n', 'o', 'n', 'e'>{};
template <class U>
FMT_CONSTEXPR static auto maybe_set_debug_format(U& u, bool set)
-> decltype(u.set_debug_format(set)) {
u.set_debug_format(set);
}
template <class U>
FMT_CONSTEXPR static void maybe_set_debug_format(U&, ...) {}
public:
template <typename ParseContext> FMT_CONSTEXPR auto parse(ParseContext& ctx) {
maybe_set_debug_format(underlying_, true);
return underlying_.parse(ctx);
}
template <typename FormatContext>
auto format(const std::optional<T>& opt, FormatContext& ctx) const
-> decltype(ctx.out()) {
if (!opt) return detail::write<Char>(ctx.out(), none);
auto out = ctx.out();
out = detail::write<Char>(out, optional);
ctx.advance_to(out);
out = underlying_.format(*opt, ctx);
return detail::write(out, ')');
}
};
FMT_END_NAMESPACE
#endif // __cpp_lib_optional
#if defined(__cpp_lib_expected) || FMT_CPP_LIB_VARIANT
FMT_BEGIN_NAMESPACE
namespace detail {
template <typename Char, typename OutputIt, typename T>
auto write_escaped_alternative(OutputIt out, const T& v) -> OutputIt {
if constexpr (has_to_string_view<T>::value)
return write_escaped_string<Char>(out, detail::to_string_view(v));
if constexpr (std::is_same_v<T, Char>) return write_escaped_char(out, v);
return write<Char>(out, v);
}
} // namespace detail
FMT_END_NAMESPACE
#endif
#ifdef __cpp_lib_expected
FMT_BEGIN_NAMESPACE
FMT_EXPORT
template <typename T, typename E, typename Char>
struct formatter<std::expected<T, E>, Char,
std::enable_if_t<is_formattable<T, Char>::value &&
is_formattable<E, Char>::value>> {
template <typename ParseContext>
FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) {
return ctx.begin();
}
template <typename FormatContext>
auto format(const std::expected<T, E>& value, FormatContext& ctx) const
-> decltype(ctx.out()) {
auto out = ctx.out();
if (value.has_value()) {
out = detail::write<Char>(out, "expected(");
out = detail::write_escaped_alternative<Char>(out, *value);
} else {
out = detail::write<Char>(out, "unexpected(");
out = detail::write_escaped_alternative<Char>(out, value.error());
}
*out++ = ')';
return out;
}
};
FMT_END_NAMESPACE
#endif // __cpp_lib_expected
#ifdef __cpp_lib_source_location
FMT_BEGIN_NAMESPACE
FMT_EXPORT
template <> struct formatter<std::source_location> {
template <typename ParseContext> FMT_CONSTEXPR auto parse(ParseContext& ctx) {
return ctx.begin();
}
template <typename FormatContext>
auto format(const std::source_location& loc, FormatContext& ctx) const
-> decltype(ctx.out()) {
auto out = ctx.out();
out = detail::write(out, loc.file_name());
out = detail::write(out, ':');
out = detail::write<char>(out, loc.line());
out = detail::write(out, ':');
out = detail::write<char>(out, loc.column());
out = detail::write(out, ": ");
out = detail::write(out, loc.function_name());
return out;
}
};
FMT_END_NAMESPACE
#endif
#if FMT_CPP_LIB_VARIANT
FMT_BEGIN_NAMESPACE
namespace detail {
template <typename T>
using variant_index_sequence =
std::make_index_sequence<std::variant_size<T>::value>;
template <typename> struct is_variant_like_ : std::false_type {};
template <typename... Types>
struct is_variant_like_<std::variant<Types...>> : std::true_type {};
// formattable element check.
template <typename T, typename C> class is_variant_formattable_ {
template <std::size_t... Is>
static std::conjunction<
is_formattable<std::variant_alternative_t<Is, T>, C>...>
check(std::index_sequence<Is...>);
public:
static constexpr const bool value =
decltype(check(variant_index_sequence<T>{}))::value;
};
} // namespace detail
template <typename T> struct is_variant_like {
static constexpr const bool value = detail::is_variant_like_<T>::value;
};
template <typename T, typename C> struct is_variant_formattable {
static constexpr const bool value =
detail::is_variant_formattable_<T, C>::value;
};
FMT_EXPORT
template <typename Char> struct formatter<std::monostate, Char> {
template <typename ParseContext>
FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) {
return ctx.begin();
}
template <typename FormatContext>
auto format(const std::monostate&, FormatContext& ctx) const
-> decltype(ctx.out()) {
return detail::write<Char>(ctx.out(), "monostate");
}
};
FMT_EXPORT
template <typename Variant, typename Char>
struct formatter<
Variant, Char,
std::enable_if_t<std::conjunction_v<
is_variant_like<Variant>, is_variant_formattable<Variant, Char>>>> {
template <typename ParseContext>
FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) {
return ctx.begin();
}
template <typename FormatContext>
auto format(const Variant& value, FormatContext& ctx) const
-> decltype(ctx.out()) {
auto out = ctx.out();
out = detail::write<Char>(out, "variant(");
FMT_TRY {
std::visit(
[&](const auto& v) {
out = detail::write_escaped_alternative<Char>(out, v);
},
value);
}
FMT_CATCH(const std::bad_variant_access&) {
detail::write<Char>(out, "valueless by exception");
}
*out++ = ')';
return out;
}
};
FMT_END_NAMESPACE
#endif // FMT_CPP_LIB_VARIANT
FMT_BEGIN_NAMESPACE
FMT_EXPORT
template <typename Char> struct formatter<std::error_code, Char> {
template <typename ParseContext>
FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) {
return ctx.begin();
}
template <typename FormatContext>
FMT_CONSTEXPR auto format(const std::error_code& ec, FormatContext& ctx) const
-> decltype(ctx.out()) {
auto out = ctx.out();
out = detail::write_bytes<Char>(out, ec.category().name(), format_specs());
out = detail::write<Char>(out, Char(':'));
out = detail::write<Char>(out, ec.value());
return out;
}
};
#if FMT_USE_RTTI
namespace detail {
template <typename Char, typename OutputIt>
auto write_demangled_name(OutputIt out, const std::type_info& ti) -> OutputIt {
# ifdef FMT_HAS_ABI_CXA_DEMANGLE
int status = 0;
std::size_t size = 0;
std::unique_ptr<char, void (*)(void*)> demangled_name_ptr(
abi::__cxa_demangle(ti.name(), nullptr, &size, &status), &std::free);
string_view demangled_name_view;
if (demangled_name_ptr) {
demangled_name_view = demangled_name_ptr.get();
// Normalization of stdlib inline namespace names.
// libc++ inline namespaces.
// std::__1::* -> std::*
// std::__1::__fs::* -> std::*
// libstdc++ inline namespaces.
// std::__cxx11::* -> std::*
// std::filesystem::__cxx11::* -> std::filesystem::*
if (demangled_name_view.starts_with("std::")) {
char* begin = demangled_name_ptr.get();
char* to = begin + 5; // std::
for (char *from = to, *end = begin + demangled_name_view.size();
from < end;) {
// This is safe, because demangled_name is NUL-terminated.
if (from[0] == '_' && from[1] == '_') {
char* next = from + 1;
while (next < end && *next != ':') next++;
if (next[0] == ':' && next[1] == ':') {
from = next + 2;
continue;
}
}
*to++ = *from++;
}
demangled_name_view = {begin, detail::to_unsigned(to - begin)};
}
} else {
demangled_name_view = string_view(ti.name());
}
return detail::write_bytes<Char>(out, demangled_name_view);
# elif FMT_MSC_VERSION
const string_view demangled_name(ti.name());
for (std::size_t i = 0; i < demangled_name.size(); ++i) {
auto sub = demangled_name;
sub.remove_prefix(i);
if (sub.starts_with("enum ")) {
i += 4;
continue;
}
if (sub.starts_with("class ") || sub.starts_with("union ")) {
i += 5;
continue;
}
if (sub.starts_with("struct ")) {
i += 6;
continue;
}
if (*sub.begin() != ' ') *out++ = *sub.begin();
}
return out;
# else
return detail::write_bytes<Char>(out, string_view(ti.name()));
# endif
}
} // namespace detail
FMT_EXPORT
template <typename Char>
struct formatter<std::type_info, Char // DEPRECATED! Mixing code unit types.
> {
public:
FMT_CONSTEXPR auto parse(basic_format_parse_context<Char>& ctx)
-> decltype(ctx.begin()) {
return ctx.begin();
}
template <typename Context>
auto format(const std::type_info& ti, Context& ctx) const
-> decltype(ctx.out()) {
return detail::write_demangled_name<Char>(ctx.out(), ti);
}
};
#endif
FMT_EXPORT
template <typename T, typename Char>
struct formatter<
T, Char, // DEPRECATED! Mixing code unit types.
typename std::enable_if<std::is_base_of<std::exception, T>::value>::type> {
private:
bool with_typename_ = false;
public:
FMT_CONSTEXPR auto parse(basic_format_parse_context<Char>& ctx)
-> decltype(ctx.begin()) {
auto it = ctx.begin();
auto end = ctx.end();
if (it == end || *it == '}') return it;
if (*it == 't') {
++it;
with_typename_ = FMT_USE_RTTI != 0;
}
return it;
}
template <typename Context>
auto format(const std::exception& ex, Context& ctx) const
-> decltype(ctx.out()) {
auto out = ctx.out();
#if FMT_USE_RTTI
if (with_typename_) {
out = detail::write_demangled_name<Char>(out, typeid(ex));
*out++ = ':';
*out++ = ' ';
}
#endif
return detail::write_bytes<Char>(out, string_view(ex.what()));
}
};
namespace detail {
template <typename T, typename Enable = void>
struct has_flip : std::false_type {};
template <typename T>
struct has_flip<T, void_t<decltype(std::declval<T>().flip())>>
: std::true_type {};
template <typename T> struct is_bit_reference_like {
static constexpr const bool value =
std::is_convertible<T, bool>::value &&
std::is_nothrow_assignable<T, bool>::value && has_flip<T>::value;
};
#ifdef _LIBCPP_VERSION
// Workaround for libc++ incompatibility with C++ standard.
// According to the Standard, `bitset::operator[] const` returns bool.
template <typename C>
struct is_bit_reference_like<std::__bit_const_reference<C>> {
static constexpr const bool value = true;
};
#endif
} // namespace detail
// We can't use std::vector<bool, Allocator>::reference and
// std::bitset<N>::reference because the compiler can't deduce Allocator and N
// in partial specialization.
FMT_EXPORT
template <typename BitRef, typename Char>
struct formatter<BitRef, Char,
enable_if_t<detail::is_bit_reference_like<BitRef>::value>>
: formatter<bool, Char> {
template <typename FormatContext>
FMT_CONSTEXPR auto format(const BitRef& v, FormatContext& ctx) const
-> decltype(ctx.out()) {
return formatter<bool, Char>::format(v, ctx);
}
};
template <typename T, typename Deleter>
auto ptr(const std::unique_ptr<T, Deleter>& p) -> const void* {
return p.get();
}
template <typename T> auto ptr(const std::shared_ptr<T>& p) -> const void* {
return p.get();
}
FMT_EXPORT
template <typename T, typename Char>
struct formatter<std::atomic<T>, Char,
enable_if_t<is_formattable<T, Char>::value>>
: formatter<T, Char> {
template <typename FormatContext>
auto format(const std::atomic<T>& v, FormatContext& ctx) const
-> decltype(ctx.out()) {
return formatter<T, Char>::format(v.load(), ctx);
}
};
#ifdef __cpp_lib_atomic_flag_test
FMT_EXPORT
template <typename Char>
struct formatter<std::atomic_flag, Char> : formatter<bool, Char> {
template <typename FormatContext>
auto format(const std::atomic_flag& v, FormatContext& ctx) const
-> decltype(ctx.out()) {
return formatter<bool, Char>::format(v.test(), ctx);
}
};
#endif // __cpp_lib_atomic_flag_test
FMT_EXPORT
template <typename T, typename Char> struct formatter<std::complex<T>, Char> {
private:
detail::dynamic_format_specs<Char> specs_;
template <typename FormatContext, typename OutputIt>
FMT_CONSTEXPR auto do_format(const std::complex<T>& c,
detail::dynamic_format_specs<Char>& specs,
FormatContext& ctx, OutputIt out) const
-> OutputIt {
if (c.real() != 0) {
*out++ = Char('(');
out = detail::write<Char>(out, c.real(), specs, ctx.locale());
specs.sign = sign::plus;
out = detail::write<Char>(out, c.imag(), specs, ctx.locale());
if (!detail::isfinite(c.imag())) *out++ = Char(' ');
*out++ = Char('i');
*out++ = Char(')');
return out;
}
out = detail::write<Char>(out, c.imag(), specs, ctx.locale());
if (!detail::isfinite(c.imag())) *out++ = Char(' ');
*out++ = Char('i');
return out;
}
public:
FMT_CONSTEXPR auto parse(basic_format_parse_context<Char>& ctx)
-> decltype(ctx.begin()) {
if (ctx.begin() == ctx.end() || *ctx.begin() == '}') return ctx.begin();
return parse_format_specs(ctx.begin(), ctx.end(), specs_, ctx,
detail::type_constant<T, Char>::value);
}
template <typename FormatContext>
auto format(const std::complex<T>& c, FormatContext& ctx) const
-> decltype(ctx.out()) {
auto specs = specs_;
if (specs.width_ref.kind != detail::arg_id_kind::none ||
specs.precision_ref.kind != detail::arg_id_kind::none) {
detail::handle_dynamic_spec<detail::width_checker>(specs.width,
specs.width_ref, ctx);
detail::handle_dynamic_spec<detail::precision_checker>(
specs.precision, specs.precision_ref, ctx);
}
if (specs.width == 0) return do_format(c, specs, ctx, ctx.out());
auto buf = basic_memory_buffer<Char>();
auto outer_specs = format_specs();
outer_specs.width = specs.width;
outer_specs.fill = specs.fill;
outer_specs.align = specs.align;
specs.width = 0;
specs.fill = {};
specs.align = align::none;
do_format(c, specs, ctx, basic_appender<Char>(buf));
return detail::write<Char>(ctx.out(),
basic_string_view<Char>(buf.data(), buf.size()),
outer_specs);
}
};
FMT_END_NAMESPACE
#endif // FMT_STD_H_

View File

@ -8,52 +8,92 @@
#ifndef FMT_XCHAR_H_ #ifndef FMT_XCHAR_H_
#define FMT_XCHAR_H_ #define FMT_XCHAR_H_
#include <cwchar> #include "color.h"
#include <tuple>
#include "format.h" #include "format.h"
#include "ranges.h"
#ifndef FMT_MODULE
# include <cwchar>
# if !defined(FMT_STATIC_THOUSANDS_SEPARATOR)
# include <locale>
# endif
#endif
FMT_BEGIN_NAMESPACE FMT_BEGIN_NAMESPACE
namespace detail { namespace detail {
template <typename T> template <typename T>
using is_exotic_char = bool_constant<!std::is_same<T, char>::value>; using is_exotic_char = bool_constant<!std::is_same<T, char>::value>;
}
FMT_MODULE_EXPORT_BEGIN template <typename S, typename = void> struct format_string_char {};
template <typename S>
struct format_string_char<
S, void_t<decltype(sizeof(detail::to_string_view(std::declval<S>())))>> {
using type = char_t<S>;
};
template <typename S>
struct format_string_char<S, enable_if_t<is_compile_string<S>::value>> {
using type = typename S::char_type;
};
template <typename S>
using format_string_char_t = typename format_string_char<S>::type;
inline auto write_loc(basic_appender<wchar_t> out, loc_value value,
const format_specs& specs, locale_ref loc) -> bool {
#ifndef FMT_STATIC_THOUSANDS_SEPARATOR
auto& numpunct =
std::use_facet<std::numpunct<wchar_t>>(loc.get<std::locale>());
auto separator = std::wstring();
auto grouping = numpunct.grouping();
if (!grouping.empty()) separator = std::wstring(1, numpunct.thousands_sep());
return value.visit(loc_writer<wchar_t>{out, specs, separator, grouping, {}});
#endif
return false;
}
} // namespace detail
FMT_BEGIN_EXPORT
using wstring_view = basic_string_view<wchar_t>; using wstring_view = basic_string_view<wchar_t>;
using wformat_parse_context = basic_format_parse_context<wchar_t>; using wformat_parse_context = basic_format_parse_context<wchar_t>;
using wformat_context = buffer_context<wchar_t>; using wformat_context = buffered_context<wchar_t>;
using wformat_args = basic_format_args<wformat_context>; using wformat_args = basic_format_args<wformat_context>;
using wmemory_buffer = basic_memory_buffer<wchar_t>; using wmemory_buffer = basic_memory_buffer<wchar_t>;
#if FMT_GCC_VERSION && FMT_GCC_VERSION < 409 #if FMT_GCC_VERSION && FMT_GCC_VERSION < 409
// Workaround broken conversion on older gcc. // Workaround broken conversion on older gcc.
template <typename... Args> using wformat_string = wstring_view; template <typename... Args> using wformat_string = wstring_view;
inline auto runtime(wstring_view s) -> wstring_view { return s; }
#else #else
template <typename... Args> template <typename... Args>
using wformat_string = basic_format_string<wchar_t, type_identity_t<Args>...>; using wformat_string = basic_format_string<wchar_t, type_identity_t<Args>...>;
inline auto runtime(wstring_view s) -> runtime_format_string<wchar_t> {
return {{s}};
}
#endif #endif
template <> struct is_char<wchar_t> : std::true_type {}; template <> struct is_char<wchar_t> : std::true_type {};
template <> struct is_char<detail::char8_type> : std::true_type {};
template <> struct is_char<char16_t> : std::true_type {}; template <> struct is_char<char16_t> : std::true_type {};
template <> struct is_char<char32_t> : std::true_type {}; template <> struct is_char<char32_t> : std::true_type {};
template <typename... Args> #ifdef __cpp_char8_t
constexpr format_arg_store<wformat_context, Args...> make_wformat_args( template <>
const Args&... args) { struct is_char<char8_t> : bool_constant<detail::is_utf8_enabled()> {};
return {args...}; #endif
template <typename... T>
constexpr auto make_wformat_args(T&... args)
-> decltype(fmt::make_format_args<wformat_context>(args...)) {
return fmt::make_format_args<wformat_context>(args...);
} }
inline namespace literals { inline namespace literals {
constexpr auto operator"" _format(const wchar_t* s, size_t n) #if FMT_USE_USER_DEFINED_LITERALS && !FMT_USE_NONTYPE_TEMPLATE_ARGS
-> detail::udl_formatter<wchar_t> { constexpr auto operator""_a(const wchar_t* s, size_t)
return {{s, n}}; -> detail::udl_arg<wchar_t> {
}
#if FMT_USE_USER_DEFINED_LITERALS && !FMT_USE_NONTYPE_TEMPLATE_PARAMETERS
constexpr detail::udl_arg<wchar_t> operator"" _a(const wchar_t* s, size_t) {
return {s}; return {s};
} }
#endif #endif
@ -78,136 +118,150 @@ auto join(std::initializer_list<T> list, wstring_view sep)
return join(std::begin(list), std::end(list), sep); return join(std::begin(list), std::end(list), sep);
} }
template <typename... T>
auto join(const std::tuple<T...>& tuple, basic_string_view<wchar_t> sep)
-> tuple_join_view<wchar_t, T...> {
return {tuple, sep};
}
template <typename Char, FMT_ENABLE_IF(!std::is_same<Char, char>::value)> template <typename Char, FMT_ENABLE_IF(!std::is_same<Char, char>::value)>
auto vformat(basic_string_view<Char> format_str, auto vformat(basic_string_view<Char> format_str,
basic_format_args<buffer_context<type_identity_t<Char>>> args) typename detail::vformat_args<Char>::type args)
-> std::basic_string<Char> { -> std::basic_string<Char> {
basic_memory_buffer<Char> buffer; auto buf = basic_memory_buffer<Char>();
detail::vformat_to(buffer, format_str, args); detail::vformat_to(buf, format_str, args);
return to_string(buffer); return to_string(buf);
}
template <typename... T>
auto format(wformat_string<T...> fmt, T&&... args) -> std::wstring {
return vformat(fmt::wstring_view(fmt), fmt::make_wformat_args(args...));
}
template <typename OutputIt, typename... T>
auto format_to(OutputIt out, wformat_string<T...> fmt, T&&... args)
-> OutputIt {
return vformat_to(out, fmt::wstring_view(fmt),
fmt::make_wformat_args(args...));
} }
// Pass char_t as a default template parameter instead of using // Pass char_t as a default template parameter instead of using
// std::basic_string<char_t<S>> to reduce the symbol size. // std::basic_string<char_t<S>> to reduce the symbol size.
template <typename S, typename... Args, typename Char = char_t<S>, template <typename S, typename... T,
FMT_ENABLE_IF(!std::is_same<Char, char>::value)> typename Char = detail::format_string_char_t<S>,
auto format(const S& format_str, Args&&... args) -> std::basic_string<Char> { FMT_ENABLE_IF(!std::is_same<Char, char>::value &&
return vformat(to_string_view(format_str), !std::is_same<Char, wchar_t>::value)>
fmt::make_format_args<buffer_context<Char>>(args...)); auto format(const S& format_str, T&&... args) -> std::basic_string<Char> {
return vformat(detail::to_string_view(format_str),
fmt::make_format_args<buffered_context<Char>>(args...));
} }
template <typename Locale, typename S, typename Char = char_t<S>, template <typename Locale, typename S,
typename Char = detail::format_string_char_t<S>,
FMT_ENABLE_IF(detail::is_locale<Locale>::value&& FMT_ENABLE_IF(detail::is_locale<Locale>::value&&
detail::is_exotic_char<Char>::value)> detail::is_exotic_char<Char>::value)>
inline auto vformat( inline auto vformat(const Locale& loc, const S& format_str,
const Locale& loc, const S& format_str, typename detail::vformat_args<Char>::type args)
basic_format_args<buffer_context<type_identity_t<Char>>> args)
-> std::basic_string<Char> { -> std::basic_string<Char> {
return detail::vformat(loc, to_string_view(format_str), args); return detail::vformat(loc, detail::to_string_view(format_str), args);
} }
template <typename Locale, typename S, typename... Args, template <typename Locale, typename S, typename... T,
typename Char = char_t<S>, typename Char = detail::format_string_char_t<S>,
FMT_ENABLE_IF(detail::is_locale<Locale>::value&& FMT_ENABLE_IF(detail::is_locale<Locale>::value&&
detail::is_exotic_char<Char>::value)> detail::is_exotic_char<Char>::value)>
inline auto format(const Locale& loc, const S& format_str, Args&&... args) inline auto format(const Locale& loc, const S& format_str, T&&... args)
-> std::basic_string<Char> { -> std::basic_string<Char> {
return detail::vformat(loc, to_string_view(format_str), return detail::vformat(
fmt::make_format_args<buffer_context<Char>>(args...)); loc, detail::to_string_view(format_str),
fmt::make_format_args<buffered_context<Char>>(args...));
} }
template <typename OutputIt, typename S, typename Char = char_t<S>, template <typename OutputIt, typename S,
typename Char = detail::format_string_char_t<S>,
FMT_ENABLE_IF(detail::is_output_iterator<OutputIt, Char>::value&& FMT_ENABLE_IF(detail::is_output_iterator<OutputIt, Char>::value&&
detail::is_exotic_char<Char>::value)> detail::is_exotic_char<Char>::value)>
auto vformat_to(OutputIt out, const S& format_str, auto vformat_to(OutputIt out, const S& format_str,
basic_format_args<buffer_context<type_identity_t<Char>>> args) typename detail::vformat_args<Char>::type args) -> OutputIt {
-> OutputIt {
auto&& buf = detail::get_buffer<Char>(out); auto&& buf = detail::get_buffer<Char>(out);
detail::vformat_to(buf, to_string_view(format_str), args); detail::vformat_to(buf, detail::to_string_view(format_str), args);
return detail::get_iterator(buf); return detail::get_iterator(buf, out);
} }
template <typename OutputIt, typename S, typename... Args, template <typename OutputIt, typename S, typename... T,
typename Char = char_t<S>, typename Char = detail::format_string_char_t<S>,
FMT_ENABLE_IF(detail::is_output_iterator<OutputIt, Char>::value && FMT_ENABLE_IF(detail::is_output_iterator<OutputIt, Char>::value &&
detail::is_exotic_char<Char>::value)> !std::is_same<Char, char>::value &&
inline auto format_to(OutputIt out, const S& fmt, Args&&... args) -> OutputIt { !std::is_same<Char, wchar_t>::value)>
return vformat_to(out, to_string_view(fmt), inline auto format_to(OutputIt out, const S& fmt, T&&... args) -> OutputIt {
fmt::make_format_args<buffer_context<Char>>(args...)); return vformat_to(out, detail::to_string_view(fmt),
} fmt::make_format_args<buffered_context<Char>>(args...));
template <typename S, typename... Args, typename Char, size_t SIZE,
typename Allocator, FMT_ENABLE_IF(detail::is_string<S>::value)>
FMT_DEPRECATED auto format_to(basic_memory_buffer<Char, SIZE, Allocator>& buf,
const S& format_str, Args&&... args) ->
typename buffer_context<Char>::iterator {
detail::vformat_to(buf, to_string_view(format_str),
fmt::make_format_args<buffer_context<Char>>(args...), {});
return detail::buffer_appender<Char>(buf);
} }
template <typename Locale, typename S, typename OutputIt, typename... Args, template <typename Locale, typename S, typename OutputIt, typename... Args,
typename Char = char_t<S>, typename Char = detail::format_string_char_t<S>,
FMT_ENABLE_IF(detail::is_output_iterator<OutputIt, Char>::value&& FMT_ENABLE_IF(detail::is_output_iterator<OutputIt, Char>::value&&
detail::is_locale<Locale>::value&& detail::is_locale<Locale>::value&&
detail::is_exotic_char<Char>::value)> detail::is_exotic_char<Char>::value)>
inline auto vformat_to( inline auto vformat_to(OutputIt out, const Locale& loc, const S& format_str,
OutputIt out, const Locale& loc, const S& format_str, typename detail::vformat_args<Char>::type args)
basic_format_args<buffer_context<type_identity_t<Char>>> args) -> OutputIt { -> OutputIt {
auto&& buf = detail::get_buffer<Char>(out); auto&& buf = detail::get_buffer<Char>(out);
vformat_to(buf, to_string_view(format_str), args, detail::locale_ref(loc)); vformat_to(buf, detail::to_string_view(format_str), args,
return detail::get_iterator(buf); detail::locale_ref(loc));
return detail::get_iterator(buf, out);
} }
template < template <typename OutputIt, typename Locale, typename S, typename... T,
typename OutputIt, typename Locale, typename S, typename... Args, typename Char = detail::format_string_char_t<S>,
typename Char = char_t<S>,
bool enable = detail::is_output_iterator<OutputIt, Char>::value && bool enable = detail::is_output_iterator<OutputIt, Char>::value &&
detail::is_locale<Locale>::value&& detail::is_exotic_char<Char>::value> detail::is_locale<Locale>::value &&
detail::is_exotic_char<Char>::value>
inline auto format_to(OutputIt out, const Locale& loc, const S& format_str, inline auto format_to(OutputIt out, const Locale& loc, const S& format_str,
Args&&... args) -> T&&... args) ->
typename std::enable_if<enable, OutputIt>::type { typename std::enable_if<enable, OutputIt>::type {
return vformat_to(out, loc, to_string_view(format_str), return vformat_to(out, loc, detail::to_string_view(format_str),
fmt::make_format_args<buffer_context<Char>>(args...)); fmt::make_format_args<buffered_context<Char>>(args...));
} }
template <typename OutputIt, typename Char, typename... Args, template <typename OutputIt, typename Char, typename... Args,
FMT_ENABLE_IF(detail::is_output_iterator<OutputIt, Char>::value&& FMT_ENABLE_IF(detail::is_output_iterator<OutputIt, Char>::value&&
detail::is_exotic_char<Char>::value)> detail::is_exotic_char<Char>::value)>
inline auto vformat_to_n( inline auto vformat_to_n(OutputIt out, size_t n,
OutputIt out, size_t n, basic_string_view<Char> format_str, basic_string_view<Char> format_str,
basic_format_args<buffer_context<type_identity_t<Char>>> args) typename detail::vformat_args<Char>::type args)
-> format_to_n_result<OutputIt> { -> format_to_n_result<OutputIt> {
detail::iterator_buffer<OutputIt, Char, detail::fixed_buffer_traits> buf(out, using traits = detail::fixed_buffer_traits;
n); auto buf = detail::iterator_buffer<OutputIt, Char, traits>(out, n);
detail::vformat_to(buf, format_str, args); detail::vformat_to(buf, format_str, args);
return {buf.out(), buf.count()}; return {buf.out(), buf.count()};
} }
template <typename OutputIt, typename S, typename... Args, template <typename OutputIt, typename S, typename... T,
typename Char = char_t<S>, typename Char = detail::format_string_char_t<S>,
FMT_ENABLE_IF(detail::is_output_iterator<OutputIt, Char>::value&& FMT_ENABLE_IF(detail::is_output_iterator<OutputIt, Char>::value&&
detail::is_exotic_char<Char>::value)> detail::is_exotic_char<Char>::value)>
inline auto format_to_n(OutputIt out, size_t n, const S& fmt, inline auto format_to_n(OutputIt out, size_t n, const S& fmt, T&&... args)
const Args&... args) -> format_to_n_result<OutputIt> { -> format_to_n_result<OutputIt> {
return vformat_to_n(out, n, to_string_view(fmt), return vformat_to_n(out, n, fmt::basic_string_view<Char>(fmt),
fmt::make_format_args<buffer_context<Char>>(args...)); fmt::make_format_args<buffered_context<Char>>(args...));
} }
template <typename S, typename... Args, typename Char = char_t<S>, template <typename S, typename... T,
typename Char = detail::format_string_char_t<S>,
FMT_ENABLE_IF(detail::is_exotic_char<Char>::value)> FMT_ENABLE_IF(detail::is_exotic_char<Char>::value)>
inline auto formatted_size(const S& fmt, Args&&... args) -> size_t { inline auto formatted_size(const S& fmt, T&&... args) -> size_t {
detail::counting_buffer<Char> buf; auto buf = detail::counting_buffer<Char>();
detail::vformat_to(buf, to_string_view(fmt), detail::vformat_to(buf, detail::to_string_view(fmt),
fmt::make_format_args<buffer_context<Char>>(args...)); fmt::make_format_args<buffered_context<Char>>(args...));
return buf.count(); return buf.count();
} }
inline void vprint(std::FILE* f, wstring_view fmt, wformat_args args) { inline void vprint(std::FILE* f, wstring_view fmt, wformat_args args) {
wmemory_buffer buffer; auto buf = wmemory_buffer();
detail::vformat_to(buffer, fmt, args); detail::vformat_to(buf, fmt, args);
buffer.push_back(L'\0'); buf.push_back(L'\0');
if (std::fputws(buffer.data(), f) == -1) if (std::fputws(buf.data(), f) == -1)
FMT_THROW(system_error(errno, FMT_STRING("cannot write to file"))); FMT_THROW(system_error(errno, FMT_STRING("cannot write to file")));
} }
@ -224,13 +278,45 @@ template <typename... T> void print(wformat_string<T...> fmt, T&&... args) {
return vprint(wstring_view(fmt), fmt::make_wformat_args(args...)); return vprint(wstring_view(fmt), fmt::make_wformat_args(args...));
} }
/** template <typename... T>
Converts *value* to ``std::wstring`` using the default format for type *T*. void println(std::FILE* f, wformat_string<T...> fmt, T&&... args) {
*/ return print(f, L"{}\n", fmt::format(fmt, std::forward<T>(args)...));
}
template <typename... T> void println(wformat_string<T...> fmt, T&&... args) {
return print(L"{}\n", fmt::format(fmt, std::forward<T>(args)...));
}
inline auto vformat(const text_style& ts, wstring_view fmt, wformat_args args)
-> std::wstring {
auto buf = wmemory_buffer();
detail::vformat_to(buf, ts, fmt, args);
return fmt::to_string(buf);
}
template <typename... T>
inline auto format(const text_style& ts, wformat_string<T...> fmt, T&&... args)
-> std::wstring {
return fmt::vformat(ts, fmt, fmt::make_wformat_args(args...));
}
template <typename... T>
FMT_DEPRECATED void print(std::FILE* f, const text_style& ts,
wformat_string<T...> fmt, const T&... args) {
vprint(f, ts, fmt, fmt::make_wformat_args(args...));
}
template <typename... T>
FMT_DEPRECATED void print(const text_style& ts, wformat_string<T...> fmt,
const T&... args) {
return print(stdout, ts, fmt, args...);
}
/// Converts `value` to `std::wstring` using the default format for type `T`.
template <typename T> inline auto to_wstring(const T& value) -> std::wstring { template <typename T> inline auto to_wstring(const T& value) -> std::wstring {
return format(FMT_STRING(L"{}"), value); return format(FMT_STRING(L"{}"), value);
} }
FMT_MODULE_EXPORT_END FMT_END_EXPORT
FMT_END_NAMESPACE FMT_END_NAMESPACE
#endif // FMT_XCHAR_H_ #endif // FMT_XCHAR_H_

View File

@ -10,96 +10,34 @@
FMT_BEGIN_NAMESPACE FMT_BEGIN_NAMESPACE
namespace detail { namespace detail {
// DEPRECATED! template FMT_API auto dragonbox::to_decimal(float x) noexcept
template <typename T = void> struct basic_data { -> dragonbox::decimal_fp<float>;
FMT_API static constexpr const char digits[100][2] = { template FMT_API auto dragonbox::to_decimal(double x) noexcept
{'0', '0'}, {'0', '1'}, {'0', '2'}, {'0', '3'}, {'0', '4'}, {'0', '5'}, -> dragonbox::decimal_fp<double>;
{'0', '6'}, {'0', '7'}, {'0', '8'}, {'0', '9'}, {'1', '0'}, {'1', '1'},
{'1', '2'}, {'1', '3'}, {'1', '4'}, {'1', '5'}, {'1', '6'}, {'1', '7'},
{'1', '8'}, {'1', '9'}, {'2', '0'}, {'2', '1'}, {'2', '2'}, {'2', '3'},
{'2', '4'}, {'2', '5'}, {'2', '6'}, {'2', '7'}, {'2', '8'}, {'2', '9'},
{'3', '0'}, {'3', '1'}, {'3', '2'}, {'3', '3'}, {'3', '4'}, {'3', '5'},
{'3', '6'}, {'3', '7'}, {'3', '8'}, {'3', '9'}, {'4', '0'}, {'4', '1'},
{'4', '2'}, {'4', '3'}, {'4', '4'}, {'4', '5'}, {'4', '6'}, {'4', '7'},
{'4', '8'}, {'4', '9'}, {'5', '0'}, {'5', '1'}, {'5', '2'}, {'5', '3'},
{'5', '4'}, {'5', '5'}, {'5', '6'}, {'5', '7'}, {'5', '8'}, {'5', '9'},
{'6', '0'}, {'6', '1'}, {'6', '2'}, {'6', '3'}, {'6', '4'}, {'6', '5'},
{'6', '6'}, {'6', '7'}, {'6', '8'}, {'6', '9'}, {'7', '0'}, {'7', '1'},
{'7', '2'}, {'7', '3'}, {'7', '4'}, {'7', '5'}, {'7', '6'}, {'7', '7'},
{'7', '8'}, {'7', '9'}, {'8', '0'}, {'8', '1'}, {'8', '2'}, {'8', '3'},
{'8', '4'}, {'8', '5'}, {'8', '6'}, {'8', '7'}, {'8', '8'}, {'8', '9'},
{'9', '0'}, {'9', '1'}, {'9', '2'}, {'9', '3'}, {'9', '4'}, {'9', '5'},
{'9', '6'}, {'9', '7'}, {'9', '8'}, {'9', '9'}};
FMT_API static constexpr const char hex_digits[] = "0123456789abcdef";
FMT_API static constexpr const char signs[4] = {0, '-', '+', ' '};
FMT_API static constexpr const char left_padding_shifts[5] = {31, 31, 0, 1,
0};
FMT_API static constexpr const char right_padding_shifts[5] = {0, 31, 0, 1,
0};
FMT_API static constexpr const unsigned prefixes[4] = {0, 0, 0x1000000u | '+',
0x1000000u | ' '};
};
#ifdef FMT_SHARED
// Required for -flto, -fivisibility=hidden and -shared to work
extern template struct basic_data<void>;
#endif
#if __cplusplus < 201703L
// DEPRECATED! These are here only for ABI compatiblity.
template <typename T> constexpr const char basic_data<T>::digits[][2];
template <typename T> constexpr const char basic_data<T>::hex_digits[];
template <typename T> constexpr const char basic_data<T>::signs[];
template <typename T> constexpr const char basic_data<T>::left_padding_shifts[];
template <typename T>
constexpr const char basic_data<T>::right_padding_shifts[];
template <typename T> constexpr const unsigned basic_data<T>::prefixes[];
#endif
template FMT_API dragonbox::decimal_fp<float> dragonbox::to_decimal(
float x) noexcept;
template FMT_API dragonbox::decimal_fp<double> dragonbox::to_decimal(
double x) noexcept;
} // namespace detail
// Workaround a bug in MSVC2013 that prevents instantiation of format_float.
int (*instantiate_format_float)(double, int, detail::float_specs,
detail::buffer<char>&) = detail::format_float;
#ifndef FMT_STATIC_THOUSANDS_SEPARATOR #ifndef FMT_STATIC_THOUSANDS_SEPARATOR
template FMT_API detail::locale_ref::locale_ref(const std::locale& loc); template FMT_API locale_ref::locale_ref(const std::locale& loc);
template FMT_API std::locale detail::locale_ref::get<std::locale>() const; template FMT_API auto locale_ref::get<std::locale>() const -> std::locale;
#endif #endif
// Explicit instantiations for char. // Explicit instantiations for char.
template FMT_API auto detail::thousands_sep_impl(locale_ref) template FMT_API auto thousands_sep_impl(locale_ref)
-> thousands_sep_result<char>; -> thousands_sep_result<char>;
template FMT_API char detail::decimal_point_impl(locale_ref); template FMT_API auto decimal_point_impl(locale_ref) -> char;
template FMT_API void detail::buffer<char>::append(const char*, const char*); template FMT_API void buffer<char>::append(const char*, const char*);
// DEPRECATED! template FMT_API void vformat_to(buffer<char>&, string_view,
// There is no correspondent extern template in format.h because of typename vformat_args<>::type, locale_ref);
// incompatibility between clang and gcc (#2377).
template FMT_API void detail::vformat_to(
detail::buffer<char>&, string_view,
basic_format_args<FMT_BUFFER_CONTEXT(char)>, detail::locale_ref);
template FMT_API int detail::format_float(double, int, detail::float_specs,
detail::buffer<char>&);
template FMT_API int detail::format_float(long double, int, detail::float_specs,
detail::buffer<char>&);
// Explicit instantiations for wchar_t. // Explicit instantiations for wchar_t.
template FMT_API auto detail::thousands_sep_impl(locale_ref) template FMT_API auto thousands_sep_impl(locale_ref)
-> thousands_sep_result<wchar_t>; -> thousands_sep_result<wchar_t>;
template FMT_API wchar_t detail::decimal_point_impl(locale_ref); template FMT_API auto decimal_point_impl(locale_ref) -> wchar_t;
template FMT_API void detail::buffer<wchar_t>::append(const wchar_t*, template FMT_API void buffer<wchar_t>::append(const wchar_t*, const wchar_t*);
const wchar_t*);
template struct detail::basic_data<void>;
} // namespace detail
FMT_END_NAMESPACE FMT_END_NAMESPACE

View File

@ -459,9 +459,8 @@ void blendPixel(const Kernel_3x3& ker,
#undef i #undef i
} }
template <class Scaler, class ColorDistance> //scaler policy: see "Scaler2x" reference implementation template <class Scaler, class ColorDistance> //scaler policy: see "Scaler2x" reference implementation
void scaleImage(const uint32_t* src, uint32_t* trg, int srcWidth, int srcHeight, const xbrz::ScalerCfg& cfg, int yFirst, int yLast) alwaysinline void scaleImage(const uint32_t* src, uint32_t* trg, int srcWidth, int srcHeight, const xbrz::ScalerCfg& cfg, int yFirst, int yLast)
{ {
yFirst = std::max(yFirst, 0); yFirst = std::max(yFirst, 0);
yLast = std::min(yLast, srcHeight); yLast = std::min(yLast, srcHeight);
@ -1069,7 +1068,7 @@ struct ColorGradientARGB
}; };
} }
__attribute__((target_clones("default", "avx2")))
void xbrz::scale(size_t factor, const uint32_t* src, uint32_t* trg, int srcWidth, int srcHeight, ColorFormat colFmt, const xbrz::ScalerCfg& cfg, int yFirst, int yLast) void xbrz::scale(size_t factor, const uint32_t* src, uint32_t* trg, int srcWidth, int srcHeight, ColorFormat colFmt, const xbrz::ScalerCfg& cfg, int yFirst, int yLast)
{ {
static_assert(SCALE_FACTOR_MAX == 6, ""); static_assert(SCALE_FACTOR_MAX == 6, "");