@@ -32,7 +32,7 @@ | |||
| fastutf8 | ? | MIT | YES | many changes | | |||
| expected | v1.0 | Public Domain / CC0 | NO | | | |||
| frozen | 1.0.1 | Apache 2 | NO | | | |||
| fmt | 8.1.1 | MIT | NO | | | |||
| fmt | 10.0.0 | MIT | NO | | | |||
| doctest | 2.4.6 | MIT | NO | | | |||
| function2 | 4.1.0 | Boost | NO | | | |||
| ankerl/svector | 1.0.2 | MIT | NO | | |
@@ -0,0 +1,234 @@ | |||
// Formatting library for C++ - dynamic format arguments | |||
// | |||
// Copyright (c) 2012 - present, Victor Zverovich | |||
// All rights reserved. | |||
// | |||
// For the license information refer to format.h. | |||
#ifndef FMT_ARGS_H_ | |||
#define FMT_ARGS_H_ | |||
#include <functional> // std::reference_wrapper | |||
#include <memory> // std::unique_ptr | |||
#include <vector> | |||
#include "core.h" | |||
FMT_BEGIN_NAMESPACE | |||
namespace detail { | |||
template <typename T> struct is_reference_wrapper : std::false_type {}; | |||
template <typename T> | |||
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> const T& unwrap(const std::reference_wrapper<T>& v) { | |||
return static_cast<const T&>(v); | |||
} | |||
class dynamic_arg_list { | |||
// Workaround for clang's -Wweak-vtables. Unlike for regular classes, for | |||
// templates it doesn't complain about inability to deduce single translation | |||
// unit for placing vtable. So storage_node_base is made a fake template. | |||
template <typename = void> struct node { | |||
virtual ~node() = default; | |||
std::unique_ptr<node<>> next; | |||
}; | |||
template <typename T> struct typed_node : node<> { | |||
T value; | |||
template <typename Arg> | |||
FMT_CONSTEXPR typed_node(const Arg& arg) : value(arg) {} | |||
template <typename Char> | |||
FMT_CONSTEXPR typed_node(const basic_string_view<Char>& arg) | |||
: value(arg.data(), arg.size()) {} | |||
}; | |||
std::unique_ptr<node<>> head_; | |||
public: | |||
template <typename T, typename Arg> const T& push(const Arg& arg) { | |||
auto new_node = std::unique_ptr<typed_node<T>>(new typed_node<T>(arg)); | |||
auto& value = new_node->value; | |||
new_node->next = std::move(head_); | |||
head_ = std::move(new_node); | |||
return value; | |||
} | |||
}; | |||
} // namespace detail | |||
/** | |||
\rst | |||
A dynamic version of `fmt::format_arg_store`. | |||
It's equipped with a storage to potentially temporary objects which lifetimes | |||
could be shorter than the format arguments object. | |||
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> | |||
class dynamic_format_arg_store | |||
#if FMT_GCC_VERSION && FMT_GCC_VERSION < 409 | |||
// Workaround a GCC template argument substitution bug. | |||
: public basic_format_args<Context> | |||
#endif | |||
{ | |||
private: | |||
using char_type = typename Context::char_type; | |||
template <typename T> struct need_copy { | |||
static constexpr detail::type mapped_type = | |||
detail::mapped_type_constant<T, Context>::value; | |||
enum { | |||
value = !(detail::is_reference_wrapper<T>::value || | |||
std::is_same<T, basic_string_view<char_type>>::value || | |||
std::is_same<T, detail::std_string_view<char_type>>::value || | |||
(mapped_type != detail::type::cstring_type && | |||
mapped_type != detail::type::string_type && | |||
mapped_type != detail::type::custom_type)) | |||
}; | |||
}; | |||
template <typename T> | |||
using stored_type = conditional_t< | |||
std::is_convertible<T, std::basic_string<char_type>>::value && | |||
!detail::is_reference_wrapper<T>::value, | |||
std::basic_string<char_type>, T>; | |||
// Storage of basic_format_arg must be contiguous. | |||
std::vector<basic_format_arg<Context>> data_; | |||
std::vector<detail::named_arg_info<char_type>> named_info_; | |||
// Storage of arguments not fitting into basic_format_arg must grow | |||
// without relocation because items in data_ refer to it. | |||
detail::dynamic_arg_list dynamic_args_; | |||
friend class basic_format_args<Context>; | |||
unsigned long long get_types() const { | |||
return detail::is_unpacked_bit | data_.size() | | |||
(named_info_.empty() | |||
? 0ULL | |||
: static_cast<unsigned long long>(detail::has_named_args_bit)); | |||
} | |||
const basic_format_arg<Context>* data() const { | |||
return named_info_.empty() ? data_.data() : data_.data() + 1; | |||
} | |||
template <typename T> void emplace_arg(const T& arg) { | |||
data_.emplace_back(detail::make_arg<Context>(arg)); | |||
} | |||
template <typename T> | |||
void emplace_arg(const detail::named_arg<char_type, T>& arg) { | |||
if (named_info_.empty()) { | |||
constexpr const detail::named_arg_info<char_type>* zero_ptr{nullptr}; | |||
data_.insert(data_.begin(), {zero_ptr, 0}); | |||
} | |||
data_.emplace_back(detail::make_arg<Context>(detail::unwrap(arg.value))); | |||
auto pop_one = [](std::vector<basic_format_arg<Context>>* data) { | |||
data->pop_back(); | |||
}; | |||
std::unique_ptr<std::vector<basic_format_arg<Context>>, decltype(pop_one)> | |||
guard{&data_, pop_one}; | |||
named_info_.push_back({arg.name, static_cast<int>(data_.size() - 2u)}); | |||
data_[0].value_.named_args = {named_info_.data(), named_info_.size()}; | |||
guard.release(); | |||
} | |||
public: | |||
constexpr dynamic_format_arg_store() = default; | |||
/** | |||
\rst | |||
Adds an argument into the dynamic store for later passing to a formatting | |||
function. | |||
Note that custom types and string types (but not string views) are copied | |||
into the store dynamically allocating memory if necessary. | |||
**Example**:: | |||
fmt::dynamic_format_arg_store<fmt::format_context> store; | |||
store.push_back(42); | |||
store.push_back("abc"); | |||
store.push_back(1.5f); | |||
std::string result = fmt::vformat("{} and {} and {}", store); | |||
\endrst | |||
*/ | |||
template <typename T> void push_back(const T& arg) { | |||
if (detail::const_check(need_copy<T>::value)) | |||
emplace_arg(dynamic_args_.push<stored_type<T>>(arg)); | |||
else | |||
emplace_arg(detail::unwrap(arg)); | |||
} | |||
/** | |||
\rst | |||
Adds a reference to the argument into the dynamic store for later passing to | |||
a formatting function. | |||
**Example**:: | |||
fmt::dynamic_format_arg_store<fmt::format_context> store; | |||
char band[] = "Rolling Stones"; | |||
store.push_back(std::cref(band)); | |||
band[9] = 'c'; // Changing str affects the output. | |||
std::string result = fmt::vformat("{}", store); | |||
// result == "Rolling Scones" | |||
\endrst | |||
*/ | |||
template <typename T> void push_back(std::reference_wrapper<T> arg) { | |||
static_assert( | |||
need_copy<T>::value, | |||
"objects of built-in types and string views are always copied"); | |||
emplace_arg(arg.get()); | |||
} | |||
/** | |||
Adds named argument into the dynamic store for later passing to a formatting | |||
function. ``std::reference_wrapper`` is supported to avoid copying of the | |||
argument. The name is always copied into the store. | |||
*/ | |||
template <typename T> | |||
void push_back(const detail::named_arg<char_type, T>& arg) { | |||
const char_type* arg_name = | |||
dynamic_args_.push<std::basic_string<char_type>>(arg.name).c_str(); | |||
if (detail::const_check(need_copy<T>::value)) { | |||
emplace_arg( | |||
fmt::arg(arg_name, dynamic_args_.push<stored_type<T>>(arg.value))); | |||
} else { | |||
emplace_arg(fmt::arg(arg_name, arg.value)); | |||
} | |||
} | |||
/** Erase all elements from the store */ | |||
void clear() { | |||
data_.clear(); | |||
named_info_.clear(); | |||
dynamic_args_ = detail::dynamic_arg_list(); | |||
} | |||
/** | |||
\rst | |||
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) { | |||
FMT_ASSERT(new_cap >= new_cap_named, | |||
"Set of arguments includes set of named arguments"); | |||
data_.reserve(new_cap); | |||
named_info_.reserve(new_cap_named); | |||
} | |||
}; | |||
FMT_END_NAMESPACE | |||
#endif // FMT_ARGS_H_ |
@@ -10,15 +10,8 @@ | |||
#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_MODULE_EXPORT_BEGIN | |||
FMT_BEGIN_EXPORT | |||
enum class color : uint32_t { | |||
alice_blue = 0xF0F8FF, // rgb(240,248,255) | |||
@@ -214,17 +207,16 @@ FMT_BEGIN_DETAIL_NAMESPACE | |||
// color is a struct of either a rgb color or a terminal color. | |||
struct color_type { | |||
FMT_CONSTEXPR color_type() FMT_NOEXCEPT : is_rgb(), value{} {} | |||
FMT_CONSTEXPR color_type(color rgb_color) FMT_NOEXCEPT : is_rgb(true), | |||
value{} { | |||
FMT_CONSTEXPR color_type() noexcept : is_rgb(), value{} {} | |||
FMT_CONSTEXPR color_type(color rgb_color) noexcept : is_rgb(true), value{} { | |||
value.rgb_color = static_cast<uint32_t>(rgb_color); | |||
} | |||
FMT_CONSTEXPR color_type(rgb rgb_color) FMT_NOEXCEPT : is_rgb(true), value{} { | |||
FMT_CONSTEXPR color_type(rgb rgb_color) noexcept : is_rgb(true), value{} { | |||
value.rgb_color = (static_cast<uint32_t>(rgb_color.r) << 16) | | |||
(static_cast<uint32_t>(rgb_color.g) << 8) | rgb_color.b; | |||
} | |||
FMT_CONSTEXPR color_type(terminal_color term_color) FMT_NOEXCEPT : is_rgb(), | |||
value{} { | |||
FMT_CONSTEXPR color_type(terminal_color term_color) noexcept | |||
: is_rgb(), value{} { | |||
value.term_color = static_cast<uint8_t>(term_color); | |||
} | |||
bool is_rgb; | |||
@@ -239,10 +231,8 @@ FMT_END_DETAIL_NAMESPACE | |||
/** A text style consisting of foreground and background colors and emphasis. */ | |||
class text_style { | |||
public: | |||
FMT_CONSTEXPR text_style(emphasis em = emphasis()) FMT_NOEXCEPT | |||
: set_foreground_color(), | |||
set_background_color(), | |||
ems(em) {} | |||
FMT_CONSTEXPR text_style(emphasis em = emphasis()) noexcept | |||
: set_foreground_color(), set_background_color(), ems(em) {} | |||
FMT_CONSTEXPR text_style& operator|=(const text_style& rhs) { | |||
if (!set_foreground_color) { | |||
@@ -273,44 +263,32 @@ class text_style { | |||
return lhs |= rhs; | |||
} | |||
FMT_DEPRECATED_NONMSVC FMT_CONSTEXPR text_style& operator&=( | |||
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 FMT_NOEXCEPT { | |||
FMT_CONSTEXPR bool has_foreground() const noexcept { | |||
return set_foreground_color; | |||
} | |||
FMT_CONSTEXPR bool has_background() const FMT_NOEXCEPT { | |||
FMT_CONSTEXPR bool has_background() const noexcept { | |||
return set_background_color; | |||
} | |||
FMT_CONSTEXPR bool has_emphasis() const FMT_NOEXCEPT { | |||
FMT_CONSTEXPR bool has_emphasis() const noexcept { | |||
return static_cast<uint8_t>(ems) != 0; | |||
} | |||
FMT_CONSTEXPR detail::color_type get_foreground() const FMT_NOEXCEPT { | |||
FMT_CONSTEXPR detail::color_type get_foreground() const noexcept { | |||
FMT_ASSERT(has_foreground(), "no foreground specified for this style"); | |||
return foreground_color; | |||
} | |||
FMT_CONSTEXPR detail::color_type get_background() const FMT_NOEXCEPT { | |||
FMT_CONSTEXPR detail::color_type get_background() const noexcept { | |||
FMT_ASSERT(has_background(), "no background specified for this style"); | |||
return background_color; | |||
} | |||
FMT_CONSTEXPR emphasis get_emphasis() const FMT_NOEXCEPT { | |||
FMT_CONSTEXPR emphasis get_emphasis() const noexcept { | |||
FMT_ASSERT(has_emphasis(), "no emphasis specified for this style"); | |||
return ems; | |||
} | |||
private: | |||
FMT_CONSTEXPR text_style(bool is_foreground, | |||
detail::color_type text_color) FMT_NOEXCEPT | |||
: set_foreground_color(), | |||
set_background_color(), | |||
ems() { | |||
detail::color_type text_color) noexcept | |||
: set_foreground_color(), set_background_color(), ems() { | |||
if (is_foreground) { | |||
foreground_color = text_color; | |||
set_foreground_color = true; | |||
@@ -320,36 +298,9 @@ class text_style { | |||
} | |||
} | |||
// DEPRECATED! | |||
FMT_CONSTEXPR text_style& and_assign(const text_style& rhs) { | |||
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; | |||
} | |||
friend FMT_CONSTEXPR text_style fg(detail::color_type foreground) noexcept; | |||
if (!set_background_color) { | |||
set_background_color = rhs.set_background_color; | |||
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) | |||
FMT_NOEXCEPT; | |||
friend FMT_CONSTEXPR_DECL text_style bg(detail::color_type background) | |||
FMT_NOEXCEPT; | |||
friend FMT_CONSTEXPR text_style bg(detail::color_type background) noexcept; | |||
detail::color_type foreground_color; | |||
detail::color_type background_color; | |||
@@ -359,17 +310,16 @@ class text_style { | |||
}; | |||
/** Creates a text style from the foreground (text) color. */ | |||
FMT_CONSTEXPR inline text_style fg(detail::color_type foreground) FMT_NOEXCEPT { | |||
FMT_CONSTEXPR inline text_style fg(detail::color_type foreground) noexcept { | |||
return text_style(true, foreground); | |||
} | |||
/** Creates a text style from the background color. */ | |||
FMT_CONSTEXPR inline text_style bg(detail::color_type background) FMT_NOEXCEPT { | |||
FMT_CONSTEXPR inline text_style bg(detail::color_type background) noexcept { | |||
return text_style(false, background); | |||
} | |||
FMT_CONSTEXPR inline text_style operator|(emphasis lhs, | |||
emphasis rhs) FMT_NOEXCEPT { | |||
FMT_CONSTEXPR inline text_style operator|(emphasis lhs, emphasis rhs) noexcept { | |||
return text_style(lhs) | rhs; | |||
} | |||
@@ -377,7 +327,7 @@ FMT_BEGIN_DETAIL_NAMESPACE | |||
template <typename Char> struct ansi_color_escape { | |||
FMT_CONSTEXPR ansi_color_escape(detail::color_type text_color, | |||
const char* esc) FMT_NOEXCEPT { | |||
const char* esc) noexcept { | |||
// If we have a terminal color, we need to output another escape code | |||
// sequence. | |||
if (!text_color.is_rgb) { | |||
@@ -412,7 +362,7 @@ template <typename Char> struct ansi_color_escape { | |||
to_esc(color.b, buffer + 15, 'm'); | |||
buffer[19] = static_cast<Char>(0); | |||
} | |||
FMT_CONSTEXPR ansi_color_escape(emphasis em) FMT_NOEXCEPT { | |||
FMT_CONSTEXPR ansi_color_escape(emphasis em) noexcept { | |||
uint8_t em_codes[num_emphases] = {}; | |||
if (has_emphasis(em, emphasis::bold)) em_codes[0] = 1; | |||
if (has_emphasis(em, emphasis::faint)) em_codes[1] = 2; | |||
@@ -433,10 +383,10 @@ template <typename Char> struct ansi_color_escape { | |||
} | |||
buffer[index++] = static_cast<Char>(0); | |||
} | |||
FMT_CONSTEXPR operator const Char*() const FMT_NOEXCEPT { return buffer; } | |||
FMT_CONSTEXPR operator const Char*() const noexcept { return buffer; } | |||
FMT_CONSTEXPR const Char* begin() const FMT_NOEXCEPT { return buffer; } | |||
FMT_CONSTEXPR_CHAR_TRAITS const Char* end() const FMT_NOEXCEPT { | |||
FMT_CONSTEXPR const Char* begin() const noexcept { return buffer; } | |||
FMT_CONSTEXPR_CHAR_TRAITS const Char* end() const noexcept { | |||
return buffer + std::char_traits<Char>::length(buffer); | |||
} | |||
@@ -445,59 +395,44 @@ template <typename Char> struct ansi_color_escape { | |||
Char buffer[7u + 3u * num_emphases + 1u]; | |||
static FMT_CONSTEXPR void to_esc(uint8_t c, Char* out, | |||
char delimiter) FMT_NOEXCEPT { | |||
char delimiter) noexcept { | |||
out[0] = static_cast<Char>('0' + c / 100); | |||
out[1] = static_cast<Char>('0' + c / 10 % 10); | |||
out[2] = static_cast<Char>('0' + c % 10); | |||
out[3] = static_cast<Char>(delimiter); | |||
} | |||
static FMT_CONSTEXPR bool has_emphasis(emphasis em, | |||
emphasis mask) FMT_NOEXCEPT { | |||
static FMT_CONSTEXPR bool has_emphasis(emphasis em, emphasis mask) noexcept { | |||
return static_cast<uint8_t>(em) & static_cast<uint8_t>(mask); | |||
} | |||
}; | |||
template <typename Char> | |||
FMT_CONSTEXPR ansi_color_escape<Char> make_foreground_color( | |||
detail::color_type foreground) FMT_NOEXCEPT { | |||
detail::color_type foreground) noexcept { | |||
return ansi_color_escape<Char>(foreground, "\x1b[38;2;"); | |||
} | |||
template <typename Char> | |||
FMT_CONSTEXPR ansi_color_escape<Char> make_background_color( | |||
detail::color_type background) FMT_NOEXCEPT { | |||
detail::color_type background) noexcept { | |||
return ansi_color_escape<Char>(background, "\x1b[48;2;"); | |||
} | |||
template <typename Char> | |||
FMT_CONSTEXPR ansi_color_escape<Char> make_emphasis(emphasis em) FMT_NOEXCEPT { | |||
FMT_CONSTEXPR ansi_color_escape<Char> make_emphasis(emphasis em) noexcept { | |||
return ansi_color_escape<Char>(em); | |||
} | |||
template <typename Char> | |||
inline void fputs(const Char* chars, FILE* stream) FMT_NOEXCEPT { | |||
std::fputs(chars, stream); | |||
} | |||
template <> | |||
inline void fputs<wchar_t>(const wchar_t* chars, FILE* stream) FMT_NOEXCEPT { | |||
std::fputws(chars, stream); | |||
} | |||
template <typename Char> inline void reset_color(FILE* stream) FMT_NOEXCEPT { | |||
fputs("\x1b[0m", stream); | |||
} | |||
template <> inline void reset_color<wchar_t>(FILE* stream) FMT_NOEXCEPT { | |||
fputs(L"\x1b[0m", stream); | |||
} | |||
template <typename Char> | |||
inline void reset_color(buffer<Char>& buffer) FMT_NOEXCEPT { | |||
template <typename Char> inline void reset_color(buffer<Char>& buffer) { | |||
auto reset_color = string_view("\x1b[0m"); | |||
buffer.append(reset_color.begin(), reset_color.end()); | |||
} | |||
template <typename T> struct styled_arg { | |||
const T& value; | |||
text_style style; | |||
}; | |||
template <typename Char> | |||
void vformat_to(buffer<Char>& buf, const text_style& ts, | |||
basic_string_view<Char> format_str, | |||
@@ -524,13 +459,19 @@ void vformat_to(buffer<Char>& buf, const text_style& ts, | |||
FMT_END_DETAIL_NAMESPACE | |||
template <typename S, typename Char = char_t<S>> | |||
void vprint(std::FILE* f, const text_style& ts, const S& format, | |||
basic_format_args<buffer_context<type_identity_t<Char>>> args) { | |||
basic_memory_buffer<Char> buf; | |||
detail::vformat_to(buf, ts, to_string_view(format), args); | |||
buf.push_back(Char(0)); | |||
detail::fputs(buf.data(), f); | |||
inline void vprint(std::FILE* f, const text_style& ts, string_view fmt, | |||
format_args args) { | |||
// Legacy wide streams are not supported. | |||
auto buf = memory_buffer(); | |||
detail::vformat_to(buf, ts, fmt, args); | |||
if (detail::is_utf8()) { | |||
detail::print(f, string_view(buf.begin(), buf.size())); | |||
return; | |||
} | |||
buf.push_back('\0'); | |||
int result = std::fputs(buf.data(), f); | |||
if (result < 0) | |||
FMT_THROW(system_error(errno, FMT_STRING("cannot write to file"))); | |||
} | |||
/** | |||
@@ -549,7 +490,7 @@ template <typename S, typename... Args, | |||
void print(std::FILE* f, const text_style& ts, const S& format_str, | |||
const Args&... args) { | |||
vprint(f, ts, format_str, | |||
fmt::make_args_checked<Args...>(format_str, args...)); | |||
fmt::make_format_args<buffer_context<char_t<S>>>(args...)); | |||
} | |||
/** | |||
@@ -574,7 +515,7 @@ inline std::basic_string<Char> vformat( | |||
const text_style& ts, const S& format_str, | |||
basic_format_args<buffer_context<type_identity_t<Char>>> args) { | |||
basic_memory_buffer<Char> buf; | |||
detail::vformat_to(buf, ts, to_string_view(format_str), args); | |||
detail::vformat_to(buf, ts, detail::to_string_view(format_str), args); | |||
return fmt::to_string(buf); | |||
} | |||
@@ -593,8 +534,8 @@ inline std::basic_string<Char> vformat( | |||
template <typename S, typename... Args, typename Char = char_t<S>> | |||
inline std::basic_string<Char> format(const text_style& ts, const S& format_str, | |||
const Args&... args) { | |||
return fmt::vformat(ts, to_string_view(format_str), | |||
fmt::make_args_checked<Args...>(format_str, args...)); | |||
return fmt::vformat(ts, detail::to_string_view(format_str), | |||
fmt::make_format_args<buffer_context<Char>>(args...)); | |||
} | |||
/** | |||
@@ -607,7 +548,7 @@ OutputIt vformat_to( | |||
basic_format_args<buffer_context<type_identity_t<Char>>> args) { | |||
auto&& buf = detail::get_buffer<Char>(out); | |||
detail::vformat_to(buf, ts, format_str, args); | |||
return detail::get_iterator(buf); | |||
return detail::get_iterator(buf, out); | |||
} | |||
/** | |||
@@ -628,11 +569,65 @@ template <typename OutputIt, typename S, typename... Args, | |||
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_args_checked<Args...>(format_str, args...)); | |||
return vformat_to(out, ts, detail::to_string_view(format_str), | |||
fmt::make_format_args<buffer_context<char_t<S>>>(args...)); | |||
} | |||
template <typename T, typename Char> | |||
struct formatter<detail::styled_arg<T>, Char> : formatter<T, Char> { | |||
template <typename FormatContext> | |||
auto format(const detail::styled_arg<T>& arg, FormatContext& ctx) const | |||
-> decltype(ctx.out()) { | |||
const auto& ts = arg.style; | |||
const auto& value = arg.value; | |||
auto out = ctx.out(); | |||
bool has_style = false; | |||
if (ts.has_emphasis()) { | |||
has_style = true; | |||
auto emphasis = detail::make_emphasis<Char>(ts.get_emphasis()); | |||
out = std::copy(emphasis.begin(), emphasis.end(), out); | |||
} | |||
if (ts.has_foreground()) { | |||
has_style = true; | |||
auto foreground = | |||
detail::make_foreground_color<Char>(ts.get_foreground()); | |||
out = std::copy(foreground.begin(), foreground.end(), out); | |||
} | |||
if (ts.has_background()) { | |||
has_style = true; | |||
auto background = | |||
detail::make_background_color<Char>(ts.get_background()); | |||
out = std::copy(background.begin(), background.end(), out); | |||
} | |||
out = formatter<T, Char>::format(value, ctx); | |||
if (has_style) { | |||
auto reset_color = string_view("\x1b[0m"); | |||
out = std::copy(reset_color.begin(), reset_color.end(), out); | |||
} | |||
return out; | |||
} | |||
}; | |||
/** | |||
\rst | |||
Returns an argument that will be formatted using ANSI escape sequences, | |||
to be used in a formatting function. | |||
**Example**:: | |||
fmt::print("Elapsed time: {0:.2f} seconds", | |||
fmt::styled(1.23, fmt::fg(fmt::color::green) | | |||
fmt::bg(fmt::color::blue))); | |||
\endrst | |||
*/ | |||
template <typename T> | |||
FMT_CONSTEXPR auto styled(const T& value, text_style ts) | |||
-> detail::styled_arg<remove_cvref_t<T>> { | |||
return detail::styled_arg<remove_cvref_t<T>>{value, ts}; | |||
} | |||
FMT_MODULE_EXPORT_END | |||
FMT_END_EXPORT | |||
FMT_END_NAMESPACE | |||
#endif // FMT_COLOR_H_ |
@@ -13,48 +13,9 @@ | |||
FMT_BEGIN_NAMESPACE | |||
namespace detail { | |||
// An output iterator that counts the number of objects written to it and | |||
// discards them. | |||
class counting_iterator { | |||
private: | |||
size_t count_; | |||
public: | |||
using iterator_category = std::output_iterator_tag; | |||
using difference_type = std::ptrdiff_t; | |||
using pointer = void; | |||
using reference = void; | |||
using _Unchecked_type = counting_iterator; // Mark iterator as checked. | |||
struct value_type { | |||
template <typename T> void operator=(const T&) {} | |||
}; | |||
counting_iterator() : count_(0) {} | |||
size_t count() const { return count_; } | |||
counting_iterator& operator++() { | |||
++count_; | |||
return *this; | |||
} | |||
counting_iterator operator++(int) { | |||
auto it = *this; | |||
++*this; | |||
return it; | |||
} | |||
friend counting_iterator operator+(counting_iterator it, difference_type n) { | |||
it.count_ += static_cast<size_t>(n); | |||
return it; | |||
} | |||
value_type operator*() const { return {}; } | |||
}; | |||
template <typename Char, typename InputIt> | |||
inline counting_iterator copy_str(InputIt begin, InputIt end, | |||
counting_iterator it) { | |||
FMT_CONSTEXPR inline counting_iterator copy_str(InputIt begin, InputIt end, | |||
counting_iterator it) { | |||
return it + (end - begin); | |||
} | |||
@@ -75,8 +36,7 @@ template <typename OutputIt> class truncating_iterator_base { | |||
using difference_type = std::ptrdiff_t; | |||
using pointer = void; | |||
using reference = void; | |||
using _Unchecked_type = | |||
truncating_iterator_base; // Mark iterator as checked. | |||
FMT_UNCHECKED_ITERATOR(truncating_iterator_base); | |||
OutputIt base() const { return out_; } | |||
size_t count() const { return count_; } | |||
@@ -163,12 +123,12 @@ struct is_compiled_string : std::is_base_of<compiled_string, S> {}; | |||
# define FMT_COMPILE(s) FMT_STRING(s) | |||
#endif | |||
#if FMT_USE_NONTYPE_TEMPLATE_PARAMETERS | |||
#if FMT_USE_NONTYPE_TEMPLATE_ARGS | |||
template <typename Char, size_t N, | |||
fmt::detail_exported::fixed_string<Char, N> Str> | |||
struct udl_compiled_string : compiled_string { | |||
using char_type = Char; | |||
constexpr operator basic_string_view<char_type>() const { | |||
explicit constexpr operator basic_string_view<char_type>() const { | |||
return {Str.data, N - 1}; | |||
} | |||
}; | |||
@@ -371,38 +331,35 @@ template <typename T, typename Char> struct parse_specs_result { | |||
int next_arg_id; | |||
}; | |||
constexpr int manual_indexing_id = -1; | |||
enum { manual_indexing_id = -1 }; | |||
template <typename T, typename Char> | |||
constexpr parse_specs_result<T, Char> parse_specs(basic_string_view<Char> str, | |||
size_t pos, int next_arg_id) { | |||
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 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()}; | |||
} | |||
template <typename Char> struct arg_id_handler { | |||
arg_ref<Char> arg_id; | |||
constexpr int operator()() { | |||
constexpr int on_auto() { | |||
FMT_ASSERT(false, "handler cannot be used with automatic indexing"); | |||
return 0; | |||
} | |||
constexpr int operator()(int id) { | |||
constexpr int on_index(int id) { | |||
arg_id = arg_ref<Char>(id); | |||
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); | |||
return 0; | |||
} | |||
constexpr void on_error(const char* message) { | |||
FMT_THROW(format_error(message)); | |||
} | |||
}; | |||
template <typename Char> struct parse_arg_id_result { | |||
@@ -436,13 +393,20 @@ constexpr auto parse_replacement_field_then_tail(S format_str) { | |||
return parse_tail<Args, END_POS + 1, NEXT_ID>( | |||
field<char_type, typename field_type<T>::type, ARG_INDEX>(), | |||
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>( | |||
str, END_POS + 1, NEXT_ID == manual_indexing_id ? 0 : NEXT_ID); | |||
return parse_tail<Args, result.end, result.next_arg_id>( | |||
spec_field<char_type, typename field_type<T>::type, ARG_INDEX>{ | |||
result.fmt}, | |||
format_str); | |||
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>{ | |||
result.fmt}, | |||
format_str); | |||
} | |||
} | |||
} | |||
@@ -533,7 +497,7 @@ constexpr auto compile(S format_str) { | |||
#endif // defined(__cpp_if_constexpr) && defined(__cpp_return_type_deduction) | |||
} // namespace detail | |||
FMT_MODULE_EXPORT_BEGIN | |||
FMT_BEGIN_EXPORT | |||
#if defined(__cpp_if_constexpr) && defined(__cpp_return_type_deduction) | |||
@@ -573,10 +537,11 @@ FMT_INLINE std::basic_string<typename S::char_type> format(const S&, | |||
constexpr auto compiled = detail::compile<Args...>(S()); | |||
if constexpr (std::is_same<remove_cvref_t<decltype(compiled)>, | |||
detail::unknown_format>()) { | |||
return format(static_cast<basic_string_view<typename S::char_type>>(S()), | |||
std::forward<Args>(args)...); | |||
return fmt::format( | |||
static_cast<basic_string_view<typename S::char_type>>(S()), | |||
std::forward<Args>(args)...); | |||
} else { | |||
return format(compiled, std::forward<Args>(args)...); | |||
return fmt::format(compiled, std::forward<Args>(args)...); | |||
} | |||
} | |||
@@ -586,11 +551,11 @@ FMT_CONSTEXPR OutputIt format_to(OutputIt out, const S&, Args&&... args) { | |||
constexpr auto compiled = detail::compile<Args...>(S()); | |||
if constexpr (std::is_same<remove_cvref_t<decltype(compiled)>, | |||
detail::unknown_format>()) { | |||
return format_to(out, | |||
static_cast<basic_string_view<typename S::char_type>>(S()), | |||
std::forward<Args>(args)...); | |||
return fmt::format_to( | |||
out, static_cast<basic_string_view<typename S::char_type>>(S()), | |||
std::forward<Args>(args)...); | |||
} else { | |||
return format_to(out, compiled, std::forward<Args>(args)...); | |||
return fmt::format_to(out, compiled, std::forward<Args>(args)...); | |||
} | |||
} | |||
#endif | |||
@@ -599,22 +564,24 @@ template <typename OutputIt, typename S, typename... Args, | |||
FMT_ENABLE_IF(detail::is_compiled_string<S>::value)> | |||
format_to_n_result<OutputIt> format_to_n(OutputIt out, size_t n, | |||
const S& format_str, Args&&... args) { | |||
auto it = format_to(detail::truncating_iterator<OutputIt>(out, n), format_str, | |||
std::forward<Args>(args)...); | |||
auto it = fmt::format_to(detail::truncating_iterator<OutputIt>(out, n), | |||
format_str, std::forward<Args>(args)...); | |||
return {it.base(), it.count()}; | |||
} | |||
template <typename S, typename... Args, | |||
FMT_ENABLE_IF(detail::is_compiled_string<S>::value)> | |||
size_t formatted_size(const S& format_str, const Args&... args) { | |||
return format_to(detail::counting_iterator(), format_str, args...).count(); | |||
FMT_CONSTEXPR20 size_t formatted_size(const S& format_str, | |||
const Args&... args) { | |||
return fmt::format_to(detail::counting_iterator(), format_str, args...) | |||
.count(); | |||
} | |||
template <typename S, typename... Args, | |||
FMT_ENABLE_IF(detail::is_compiled_string<S>::value)> | |||
void print(std::FILE* f, const S& format_str, const Args&... args) { | |||
memory_buffer buffer; | |||
format_to(std::back_inserter(buffer), format_str, args...); | |||
fmt::format_to(std::back_inserter(buffer), format_str, args...); | |||
detail::print(f, {buffer.data(), buffer.size()}); | |||
} | |||
@@ -624,19 +591,17 @@ void print(const S& format_str, const Args&... args) { | |||
print(stdout, format_str, args...); | |||
} | |||
#if FMT_USE_NONTYPE_TEMPLATE_PARAMETERS | |||
#if FMT_USE_NONTYPE_TEMPLATE_ARGS | |||
inline namespace literals { | |||
template <detail_exported::fixed_string Str> | |||
constexpr detail::udl_compiled_string< | |||
remove_cvref_t<decltype(Str.data[0])>, | |||
sizeof(Str.data) / sizeof(decltype(Str.data[0])), Str> | |||
operator""_cf() { | |||
return {}; | |||
template <detail_exported::fixed_string Str> constexpr auto operator""_cf() { | |||
using char_t = remove_cvref_t<decltype(Str.data[0])>; | |||
return detail::udl_compiled_string<char_t, sizeof(Str.data) / sizeof(char_t), | |||
Str>(); | |||
} | |||
} // namespace literals | |||
#endif | |||
FMT_MODULE_EXPORT_END | |||
FMT_END_EXPORT | |||
FMT_END_NAMESPACE | |||
#endif // FMT_COMPILE_H_ |
@@ -9,10 +9,8 @@ | |||
#define FMT_OS_H_ | |||
#include <cerrno> | |||
#include <clocale> // locale_t | |||
#include <cstddef> | |||
#include <cstdio> | |||
#include <cstdlib> // strtod_l | |||
#include <system_error> // std::system_error | |||
#if defined __APPLE__ || defined(__FreeBSD__) | |||
@@ -73,7 +71,7 @@ | |||
#define FMT_RETRY(result, expression) FMT_RETRY_VAL(result, expression, -1) | |||
FMT_BEGIN_NAMESPACE | |||
FMT_MODULE_EXPORT_BEGIN | |||
FMT_BEGIN_EXPORT | |||
/** | |||
\rst | |||
@@ -122,50 +120,12 @@ template <typename Char> class basic_cstring_view { | |||
using cstring_view = basic_cstring_view<char>; | |||
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 | |||
FMT_API const std::error_category& system_category() FMT_NOEXCEPT; | |||
FMT_API const std::error_category& system_category() noexcept; | |||
FMT_BEGIN_DETAIL_NAMESPACE | |||
// 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, | |||
const char* message) FMT_NOEXCEPT; | |||
const char* message) noexcept; | |||
FMT_END_DETAIL_NAMESPACE | |||
FMT_API std::system_error vwindows_error(int error_code, string_view format_str, | |||
@@ -207,10 +167,9 @@ std::system_error windows_error(int error_code, string_view message, | |||
// Reports a Windows error without throwing an exception. | |||
// Can be used to report errors from destructors. | |||
FMT_API void report_windows_error(int error_code, | |||
const char* message) FMT_NOEXCEPT; | |||
FMT_API void report_windows_error(int error_code, const char* message) noexcept; | |||
#else | |||
inline const std::error_category& system_category() FMT_NOEXCEPT { | |||
inline const std::error_category& system_category() noexcept { | |||
return std::system_category(); | |||
} | |||
#endif // _WIN32 | |||
@@ -237,13 +196,13 @@ class buffered_file { | |||
void operator=(const buffered_file&) = delete; | |||
// Constructs a buffered_file object which doesn't represent any file. | |||
buffered_file() FMT_NOEXCEPT : file_(nullptr) {} | |||
buffered_file() noexcept : file_(nullptr) {} | |||
// Destroys the object closing the file it represents if any. | |||
FMT_API ~buffered_file() FMT_NOEXCEPT; | |||
FMT_API ~buffered_file() noexcept; | |||
public: | |||
buffered_file(buffered_file&& other) FMT_NOEXCEPT : file_(other.file_) { | |||
buffered_file(buffered_file&& other) noexcept : file_(other.file_) { | |||
other.file_ = nullptr; | |||
} | |||
@@ -261,11 +220,9 @@ class buffered_file { | |||
FMT_API void close(); | |||
// Returns the pointer to a FILE object representing this file. | |||
FILE* get() const FMT_NOEXCEPT { return file_; } | |||
FILE* get() const noexcept { return file_; } | |||
// We place parentheses around fileno to workaround a bug in some versions | |||
// of MinGW that define fileno as a macro. | |||
FMT_API int(fileno)() const; | |||
FMT_API int descriptor() const; | |||
void vprint(string_view format_str, format_args args) { | |||
fmt::vprint(file_, format_str, args); | |||
@@ -279,12 +236,12 @@ class buffered_file { | |||
#if FMT_USE_FCNTL | |||
// A file. Closed file is represented by a file object with descriptor -1. | |||
// Methods that are not declared with FMT_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 | |||
// closing the file multiple times will cause a crash on Windows rather | |||
// than an exception. You can get standard behavior by overriding the | |||
// invalid parameter handler with _set_invalid_parameter_handler. | |||
class file { | |||
class FMT_API file { | |||
private: | |||
int fd_; // File descriptor. | |||
@@ -303,16 +260,16 @@ class file { | |||
}; | |||
// Constructs a file object which doesn't represent any file. | |||
file() FMT_NOEXCEPT : fd_(-1) {} | |||
file() noexcept : fd_(-1) {} | |||
// Opens a file and constructs a file object representing this file. | |||
FMT_API file(cstring_view path, int oflag); | |||
file(cstring_view path, int oflag); | |||
public: | |||
file(const file&) = delete; | |||
void operator=(const file&) = delete; | |||
file(file&& other) FMT_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. | |||
file& operator=(file&& other) { | |||
@@ -323,43 +280,49 @@ class file { | |||
} | |||
// Destroys the object closing the file it represents if any. | |||
FMT_API ~file() FMT_NOEXCEPT; | |||
~file() noexcept; | |||
// Returns the file descriptor. | |||
int descriptor() const FMT_NOEXCEPT { return fd_; } | |||
int descriptor() const noexcept { return fd_; } | |||
// Closes the file. | |||
FMT_API void close(); | |||
void close(); | |||
// Returns the file size. The size has signed type for consistency with | |||
// stat::st_size. | |||
FMT_API long long size() const; | |||
long long size() const; | |||
// Attempts to read count bytes from the file into the specified buffer. | |||
FMT_API size_t read(void* buffer, size_t count); | |||
size_t read(void* buffer, size_t count); | |||
// Attempts to write count bytes from the specified buffer to the file. | |||
FMT_API size_t write(const void* buffer, size_t count); | |||
size_t write(const void* buffer, size_t count); | |||
// Duplicates a file descriptor with the dup function and returns | |||
// the duplicate as a file object. | |||
FMT_API static file dup(int fd); | |||
static file dup(int fd); | |||
// Makes fd be the copy of this file descriptor, closing fd first if | |||
// necessary. | |||
FMT_API void dup2(int fd); | |||
void dup2(int fd); | |||
// Makes fd be the copy of this file descriptor, closing fd first if | |||
// necessary. | |||
FMT_API void dup2(int fd, std::error_code& ec) FMT_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. | |||
FMT_API static void pipe(file& read_end, file& write_end); | |||
static void pipe(file& read_end, file& write_end); | |||
// Creates a buffered_file object associated with this file and detaches | |||
// this file object from the file. | |||
FMT_API buffered_file fdopen(const char* mode); | |||
buffered_file fdopen(const char* mode); | |||
# 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 | |||
}; | |||
// Returns the memory page size. | |||
@@ -402,6 +365,28 @@ struct ostream_params { | |||
# endif | |||
}; | |||
class file_buffer final : public buffer<char> { | |||
file file_; | |||
FMT_API void grow(size_t) override; | |||
public: | |||
FMT_API file_buffer(cstring_view path, const ostream_params& params); | |||
FMT_API file_buffer(file_buffer&& other); | |||
FMT_API ~file_buffer(); | |||
void flush() { | |||
if (size() == 0) return; | |||
file_.write(data(), size() * sizeof(data()[0])); | |||
clear(); | |||
} | |||
void close() { | |||
flush(); | |||
file_.close(); | |||
} | |||
}; | |||
FMT_END_DETAIL_NAMESPACE | |||
// Added {} below to work around default constructor error known to | |||
@@ -409,49 +394,32 @@ FMT_END_DETAIL_NAMESPACE | |||
constexpr detail::buffer_size buffer_size{}; | |||
/** A fast output stream which is not thread-safe. */ | |||
class FMT_API ostream final : private detail::buffer<char> { | |||
class FMT_API ostream { | |||
private: | |||
file file_; | |||
void grow(size_t) override; | |||
FMT_MSC_WARNING(suppress : 4251) | |||
detail::file_buffer buffer_; | |||
ostream(cstring_view path, const detail::ostream_params& params) | |||
: file_(path, params.oflag) { | |||
set(new char[params.buffer_size], params.buffer_size); | |||
} | |||
: buffer_(path, params) {} | |||
public: | |||
ostream(ostream&& other) | |||
: detail::buffer<char>(other.data(), other.size(), other.capacity()), | |||
file_(std::move(other.file_)) { | |||
other.clear(); | |||
other.set(nullptr, 0); | |||
} | |||
~ostream() { | |||
flush(); | |||
delete[] data(); | |||
} | |||
ostream(ostream&& other) : buffer_(std::move(other.buffer_)) {} | |||
void flush() { | |||
if (size() == 0) return; | |||
file_.write(data(), size()); | |||
clear(); | |||
} | |||
~ostream(); | |||
void flush() { buffer_.flush(); } | |||
template <typename... T> | |||
friend ostream output_file(cstring_view path, T... params); | |||
void close() { | |||
flush(); | |||
file_.close(); | |||
} | |||
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) { | |||
vformat_to(detail::buffer_appender<char>(*this), fmt, | |||
vformat_to(detail::buffer_appender<char>(buffer_), fmt, | |||
fmt::make_format_args(args...)); | |||
} | |||
}; | |||
@@ -462,7 +430,7 @@ class FMT_API ostream final : private detail::buffer<char> { | |||
* ``<integer>``: Flags passed to `open | |||
<https://pubs.opengroup.org/onlinepubs/007904875/functions/open.html>`_ | |||
(``file::WRONLY | file::CREATE`` by default) | |||
(``file::WRONLY | file::CREATE | file::TRUNC`` by default) | |||
* ``buffer_size=<integer>``: Output buffer size | |||
**Example**:: | |||
@@ -477,51 +445,7 @@ inline ostream output_file(cstring_view path, T... params) { | |||
} | |||
#endif // FMT_USE_FCNTL | |||
#ifdef FMT_LOCALE | |||
// A "C" numeric locale. | |||
class locale { | |||
private: | |||
# ifdef _WIN32 | |||
using locale_t = _locale_t; | |||
static void freelocale(locale_t loc) { _free_locale(loc); } | |||
static double strtod_l(const char* nptr, char** endptr, _locale_t loc) { | |||
return _strtod_l(nptr, endptr, loc); | |||
} | |||
# endif | |||
locale_t locale_; | |||
public: | |||
using type = locale_t; | |||
locale(const locale&) = delete; | |||
void operator=(const locale&) = delete; | |||
locale() { | |||
# ifndef _WIN32 | |||
locale_ = FMT_SYSTEM(newlocale(LC_NUMERIC_MASK, "C", nullptr)); | |||
# else | |||
locale_ = _create_locale(LC_NUMERIC, "C"); | |||
# endif | |||
if (!locale_) FMT_THROW(system_error(errno, "cannot create locale")); | |||
} | |||
~locale() { freelocale(locale_); } | |||
type get() const { return locale_; } | |||
// Converts string to floating-point number and advances str past the end | |||
// of the parsed input. | |||
FMT_DEPRECATED double strtod(const char*& str) const { | |||
char* end = nullptr; | |||
double result = strtod_l(str, &end, locale_); | |||
str = end; | |||
return result; | |||
} | |||
}; | |||
using Locale FMT_DEPRECATED_ALIAS = locale; | |||
#endif // FMT_LOCALE | |||
FMT_MODULE_EXPORT_END | |||
FMT_END_EXPORT | |||
FMT_END_NAMESPACE | |||
#endif // FMT_OS_H_ |
@@ -8,47 +8,67 @@ | |||
#ifndef FMT_OSTREAM_H_ | |||
#define FMT_OSTREAM_H_ | |||
#include <ostream> | |||
#include <fstream> // std::filebuf | |||
#if defined(_WIN32) && defined(__GLIBCXX__) | |||
# include <ext/stdio_filebuf.h> | |||
# include <ext/stdio_sync_filebuf.h> | |||
#elif defined(_WIN32) && defined(_LIBCPP_VERSION) | |||
# include <__std_stream> | |||
#endif | |||
#include "format.h" | |||
FMT_BEGIN_NAMESPACE | |||
template <typename OutputIt, typename Char> class basic_printf_context; | |||
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; | |||
// Generate a unique explicit instantion in every translation unit using a tag | |||
// type in an anonymous namespace. | |||
namespace { | |||
struct file_access_tag {}; | |||
} // namespace | |||
template <typename Tag, typename BufType, FILE* BufType::*FileMemberPtr> | |||
class file_access { | |||
friend auto get_file(BufType& obj) -> FILE* { return obj.*FileMemberPtr; } | |||
}; | |||
// 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_same<T, std::basic_string<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 {}; | |||
#if FMT_MSC_VERSION | |||
template class file_access<file_access_tag, std::filebuf, | |||
&std::filebuf::_Myfile>; | |||
auto get_file(std::filebuf&) -> FILE*; | |||
#elif defined(_WIN32) && defined(_LIBCPP_VERSION) | |||
template class file_access<file_access_tag, std::__stdoutbuf<char>, | |||
&std::__stdoutbuf<char>::__file_>; | |||
auto get_file(std::__stdoutbuf<char>&) -> FILE*; | |||
#endif | |||
inline bool write_ostream_unicode(std::ostream& os, fmt::string_view data) { | |||
#if FMT_MSC_VERSION | |||
if (auto* buf = dynamic_cast<std::filebuf*>(os.rdbuf())) | |||
if (FILE* f = get_file(*buf)) return write_console(f, data); | |||
#elif defined(_WIN32) && defined(__GLIBCXX__) | |||
auto* rdbuf = os.rdbuf(); | |||
FILE* c_file; | |||
if (auto* sfbuf = dynamic_cast<__gnu_cxx::stdio_sync_filebuf<char>*>(rdbuf)) | |||
c_file = sfbuf->file(); | |||
else if (auto* fbuf = dynamic_cast<__gnu_cxx::stdio_filebuf<char>*>(rdbuf)) | |||
c_file = fbuf->file(); | |||
else | |||
return false; | |||
if (c_file) return write_console(c_file, data); | |||
#elif defined(_WIN32) && defined(_LIBCPP_VERSION) | |||
if (auto* buf = dynamic_cast<std::__stdoutbuf<char>*>(os.rdbuf())) | |||
if (FILE* f = get_file(*buf)) return write_console(f, data); | |||
#else | |||
ignore_unused(os, data); | |||
#endif | |||
return false; | |||
} | |||
inline bool write_ostream_unicode(std::wostream&, | |||
fmt::basic_string_view<wchar_t>) { | |||
return false; | |||
} | |||
// Write the content of buf to os. | |||
// It is a separate function rather than a part of vprint to simplify testing. | |||
@@ -76,41 +96,72 @@ void format_value(buffer<Char>& buf, const T& value, | |||
#endif | |||
output << value; | |||
output.exceptions(std::ios_base::failbit | std::ios_base::badbit); | |||
buf.try_resize(buf.size()); | |||
} | |||
template <typename T> struct streamed_view { const T& value; }; | |||
} // namespace detail | |||
// Formats an object of type T that has an overloaded ostream operator<<. | |||
template <typename T, typename Char> | |||
struct fallback_formatter<T, Char, enable_if_t<is_streamable<T, Char>::value>> | |||
: private formatter<basic_string_view<Char>, Char> { | |||
using formatter<basic_string_view<Char>, Char>::parse; | |||
template <typename Char> | |||
struct basic_ostream_formatter : formatter<basic_string_view<Char>, Char> { | |||
void set_debug_format() = delete; | |||
template <typename OutputIt> | |||
auto format(const T& value, basic_format_context<OutputIt, Char>& ctx) | |||
template <typename T, typename OutputIt> | |||
auto format(const T& value, basic_format_context<OutputIt, Char>& ctx) const | |||
-> OutputIt { | |||
auto buffer = basic_memory_buffer<Char>(); | |||
format_value(buffer, value, ctx.locale()); | |||
detail::format_value(buffer, value, ctx.locale()); | |||
return formatter<basic_string_view<Char>, Char>::format( | |||
{buffer.data(), buffer.size()}, ctx); | |||
} | |||
}; | |||
using ostream_formatter = basic_ostream_formatter<char>; | |||
// DEPRECATED! | |||
template <typename T, typename Char> | |||
struct formatter<detail::streamed_view<T>, Char> | |||
: basic_ostream_formatter<Char> { | |||
template <typename OutputIt> | |||
auto format(const T& value, basic_printf_context<OutputIt, Char>& ctx) | |||
-> OutputIt { | |||
auto buffer = basic_memory_buffer<Char>(); | |||
format_value(buffer, value, ctx.locale()); | |||
return std::copy(buffer.begin(), buffer.end(), ctx.out()); | |||
auto format(detail::streamed_view<T> view, | |||
basic_format_context<OutputIt, Char>& ctx) const -> OutputIt { | |||
return basic_ostream_formatter<Char>::format(view.value, ctx); | |||
} | |||
}; | |||
/** | |||
\rst | |||
Returns a view that formats `value` via an ostream ``operator<<``. | |||
**Example**:: | |||
fmt::print("Current thread id: {}\n", | |||
fmt::streamed(std::this_thread::get_id())); | |||
\endrst | |||
*/ | |||
template <typename T> | |||
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::write_buffer(os, buffer); | |||
} | |||
} // namespace detail | |||
FMT_MODULE_EXPORT | |||
template <typename Char> | |||
void vprint(std::basic_ostream<Char>& os, basic_string_view<Char> format_str, | |||
FMT_MODULE_EXPORT template <typename Char> | |||
void vprint(std::basic_ostream<Char>& os, | |||
basic_string_view<type_identity_t<Char>> format_str, | |||
basic_format_args<buffer_context<type_identity_t<Char>>> args) { | |||
auto buffer = basic_memory_buffer<Char>(); | |||
detail::vformat_to(buffer, format_str, args); | |||
if (detail::write_ostream_unicode(os, {buffer.data(), buffer.size()})) return; | |||
detail::write_buffer(os, buffer); | |||
} | |||
@@ -123,13 +174,36 @@ void vprint(std::basic_ostream<Char>& os, basic_string_view<Char> format_str, | |||
fmt::print(cerr, "Don't {}!", "panic"); | |||
\endrst | |||
*/ | |||
FMT_MODULE_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::is_utf8()) | |||
vprint(os, fmt, vargs); | |||
else | |||
detail::vprint_directly(os, fmt, vargs); | |||
} | |||
FMT_MODULE_EXPORT | |||
template <typename S, typename... Args, | |||
typename Char = enable_if_t<detail::is_string<S>::value, char_t<S>>> | |||
void print(std::basic_ostream<Char>& os, const S& format_str, Args&&... args) { | |||
vprint(os, to_string_view(format_str), | |||
fmt::make_args_checked<Args...>(format_str, args...)); | |||
template <typename... Args> | |||
void print(std::wostream& os, | |||
basic_format_string<wchar_t, type_identity_t<Args>...> fmt, | |||
Args&&... args) { | |||
vprint(os, fmt, fmt::make_format_args<buffer_context<wchar_t>>(args...)); | |||
} | |||
FMT_MODULE_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_MODULE_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 | |||
#endif // FMT_OSTREAM_H_ |
@@ -10,12 +10,11 @@ | |||
#include <algorithm> // std::max | |||
#include <limits> // std::numeric_limits | |||
#include <ostream> | |||
#include "format.h" | |||
FMT_BEGIN_NAMESPACE | |||
FMT_MODULE_EXPORT_BEGIN | |||
FMT_BEGIN_EXPORT | |||
template <typename T> struct printf_formatter { printf_formatter() = delete; }; | |||
@@ -82,13 +81,13 @@ class printf_precision_handler { | |||
template <typename T, FMT_ENABLE_IF(std::is_integral<T>::value)> | |||
int operator()(T value) { | |||
if (!int_checker<std::numeric_limits<T>::is_signed>::fits_in_int(value)) | |||
FMT_THROW(format_error("number is too big")); | |||
throw_format_error("number is too big"); | |||
return (std::max)(static_cast<int>(value), 0); | |||
} | |||
template <typename T, FMT_ENABLE_IF(!std::is_integral<T>::value)> | |||
int operator()(T) { | |||
FMT_THROW(format_error("precision is not integer")); | |||
throw_format_error("precision is not integer"); | |||
return 0; | |||
} | |||
}; | |||
@@ -195,12 +194,10 @@ template <typename Char> struct get_cstring { | |||
// left alignment if it is negative. | |||
template <typename Char> class printf_width_handler { | |||
private: | |||
using format_specs = basic_format_specs<Char>; | |||
format_specs& specs_; | |||
format_specs<Char>& specs_; | |||
public: | |||
explicit printf_width_handler(format_specs& specs) : specs_(specs) {} | |||
explicit printf_width_handler(format_specs<Char>& specs) : specs_(specs) {} | |||
template <typename T, FMT_ENABLE_IF(std::is_integral<T>::value)> | |||
unsigned operator()(T value) { | |||
@@ -210,24 +207,31 @@ template <typename Char> class printf_width_handler { | |||
width = 0 - width; | |||
} | |||
unsigned int_max = max_value<int>(); | |||
if (width > int_max) FMT_THROW(format_error("number is too big")); | |||
if (width > int_max) throw_format_error("number is too big"); | |||
return static_cast<unsigned>(width); | |||
} | |||
template <typename T, FMT_ENABLE_IF(!std::is_integral<T>::value)> | |||
unsigned operator()(T) { | |||
FMT_THROW(format_error("width is not integer")); | |||
throw_format_error("width is not integer"); | |||
return 0; | |||
} | |||
}; | |||
// Workaround for a bug with the XL compiler when initializing | |||
// printf_arg_formatter's base class. | |||
template <typename Char> | |||
auto make_arg_formatter(buffer_appender<Char> iter, format_specs<Char>& s) | |||
-> arg_formatter<Char> { | |||
return {iter, s, locale_ref()}; | |||
} | |||
// The ``printf`` argument formatter. | |||
template <typename OutputIt, typename Char> | |||
class printf_arg_formatter : public arg_formatter<Char> { | |||
private: | |||
using base = arg_formatter<Char>; | |||
using context_type = basic_printf_context<OutputIt, Char>; | |||
using format_specs = basic_format_specs<Char>; | |||
context_type& context_; | |||
@@ -238,8 +242,8 @@ class printf_arg_formatter : public arg_formatter<Char> { | |||
} | |||
public: | |||
printf_arg_formatter(OutputIt iter, format_specs& s, context_type& ctx) | |||
: base{iter, s, locale_ref()}, context_(ctx) {} | |||
printf_arg_formatter(OutputIt iter, format_specs<Char>& s, context_type& ctx) | |||
: base(make_arg_formatter(iter, s)), context_(ctx) {} | |||
OutputIt operator()(monostate value) { return base::operator()(value); } | |||
@@ -248,7 +252,7 @@ class printf_arg_formatter : public arg_formatter<Char> { | |||
// MSVC2013 fails to compile separate overloads for bool and Char so use | |||
// std::is_same instead. | |||
if (std::is_same<T, Char>::value) { | |||
format_specs fmt_specs = this->specs; | |||
format_specs<Char> fmt_specs = this->specs; | |||
if (fmt_specs.type != presentation_type::none && | |||
fmt_specs.type != presentation_type::chr) { | |||
return (*this)(static_cast<int>(value)); | |||
@@ -301,8 +305,7 @@ class printf_arg_formatter : public arg_formatter<Char> { | |||
}; | |||
template <typename Char> | |||
void parse_flags(basic_format_specs<Char>& specs, const Char*& it, | |||
const Char* end) { | |||
void parse_flags(format_specs<Char>& specs, const Char*& it, const Char* end) { | |||
for (; it != end; ++it) { | |||
switch (*it) { | |||
case '-': | |||
@@ -329,8 +332,8 @@ void parse_flags(basic_format_specs<Char>& specs, const Char*& it, | |||
} | |||
template <typename Char, typename GetArg> | |||
int parse_header(const Char*& it, const Char* end, | |||
basic_format_specs<Char>& specs, GetArg get_arg) { | |||
int parse_header(const Char*& it, const Char* end, format_specs<Char>& specs, | |||
GetArg get_arg) { | |||
int arg_index = -1; | |||
Char c = *it; | |||
if (c >= '0' && c <= '9') { | |||
@@ -345,7 +348,7 @@ int parse_header(const Char*& it, const Char* end, | |||
if (value != 0) { | |||
// Nonzero value means that we parsed width and don't need to | |||
// parse it or flags again, so return now. | |||
if (value == -1) FMT_THROW(format_error("number is too big")); | |||
if (value == -1) throw_format_error("number is too big"); | |||
specs.width = value; | |||
return arg_index; | |||
} | |||
@@ -356,7 +359,7 @@ int parse_header(const Char*& it, const Char* end, | |||
if (it != end) { | |||
if (*it >= '0' && *it <= '9') { | |||
specs.width = parse_nonnegative_int(it, end, -1); | |||
if (specs.width == -1) FMT_THROW(format_error("number is too big")); | |||
if (specs.width == -1) throw_format_error("number is too big"); | |||
} else if (*it == '*') { | |||
++it; | |||
specs.width = static_cast<int>(visit_format_arg( | |||
@@ -366,12 +369,52 @@ int parse_header(const Char*& it, const Char* end, | |||
return arg_index; | |||
} | |||
inline auto parse_printf_presentation_type(char c, type t) | |||
-> 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': | |||
return in(t, integral_set) ? pt::hex_lower : pt::none; | |||
case 'X': | |||
return in(t, integral_set) ? pt::hex_upper : pt::none; | |||
case 'a': | |||
return in(t, float_set) ? pt::hexfloat_lower : pt::none; | |||
case 'A': | |||
return in(t, float_set) ? pt::hexfloat_upper : pt::none; | |||
case 'e': | |||
return in(t, float_set) ? pt::exp_lower : pt::none; | |||
case 'E': | |||
return in(t, float_set) ? pt::exp_upper : pt::none; | |||
case 'f': | |||
return in(t, float_set) ? pt::fixed_lower : pt::none; | |||
case 'F': | |||
return in(t, float_set) ? pt::fixed_upper : pt::none; | |||
case 'g': | |||
return in(t, float_set) ? pt::general_lower : pt::none; | |||
case 'G': | |||
return in(t, float_set) ? pt::general_upper : 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> | |||
void vprintf(buffer<Char>& buf, basic_string_view<Char> format, | |||
basic_format_args<Context> args) { | |||
using OutputIt = buffer_appender<Char>; | |||
auto out = OutputIt(buf); | |||
auto context = basic_printf_context<OutputIt, Char>(out, args); | |||
using iterator = buffer_appender<Char>; | |||
auto out = iterator(buf); | |||
auto context = basic_printf_context<iterator, Char>(out, args); | |||
auto parse_ctx = basic_printf_parse_context<Char>(format); | |||
// Returns the argument with specified index or, if arg_index is -1, the next | |||
@@ -388,26 +431,25 @@ void vprintf(buffer<Char>& buf, basic_string_view<Char> format, | |||
const Char* end = parse_ctx.end(); | |||
auto it = start; | |||
while (it != end) { | |||
if (!detail::find<false, Char>(it, end, '%', it)) { | |||
it = end; // detail::find leaves it == nullptr if it doesn't find '%' | |||
if (!find<false, Char>(it, end, '%', it)) { | |||
it = end; // find leaves it == nullptr if it doesn't find '%'. | |||
break; | |||
} | |||
Char c = *it++; | |||
if (it != end && *it == c) { | |||
out = detail::write( | |||
out, basic_string_view<Char>(start, detail::to_unsigned(it - start))); | |||
out = write(out, basic_string_view<Char>(start, to_unsigned(it - start))); | |||
start = ++it; | |||
continue; | |||
} | |||
out = detail::write(out, basic_string_view<Char>( | |||
start, detail::to_unsigned(it - 1 - start))); | |||
out = | |||
write(out, basic_string_view<Char>(start, to_unsigned(it - 1 - start))); | |||
basic_format_specs<Char> specs; | |||
auto specs = format_specs<Char>(); | |||
specs.align = align::right; | |||
// Parse argument index, flags and width. | |||
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) throw_format_error("argument not found"); | |||
// Parse precision. | |||
if (it != end && *it == '.') { | |||
@@ -418,7 +460,7 @@ void vprintf(buffer<Char>& buf, basic_string_view<Char> format, | |||
} else if (c == '*') { | |||
++it; | |||
specs.precision = static_cast<int>( | |||
visit_format_arg(detail::printf_precision_handler(), get_arg(-1))); | |||
visit_format_arg(printf_precision_handler(), get_arg(-1))); | |||
} else { | |||
specs.precision = 0; | |||
} | |||
@@ -430,17 +472,15 @@ void vprintf(buffer<Char>& buf, basic_string_view<Char> format, | |||
if (specs.precision >= 0 && arg.is_integral()) | |||
specs.fill[0] = | |||
' '; // Ignore '0' flag for non-numeric types or if '-' present. | |||
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 = visit_format_arg(get_cstring<Char>(), arg); | |||
auto str_end = str + specs.precision; | |||
auto nul = std::find(str, str_end, Char()); | |||
arg = detail::make_arg<basic_printf_context<OutputIt, Char>>( | |||
arg = make_arg<basic_printf_context<iterator, Char>>( | |||
basic_string_view<Char>( | |||
str, detail::to_unsigned(nul != str_end ? nul - str | |||
: specs.precision))); | |||
str, to_unsigned(nul != str_end ? nul - str : specs.precision))); | |||
} | |||
if (specs.alt && visit_format_arg(detail::is_zero_int(), arg)) | |||
specs.alt = false; | |||
if (specs.alt && visit_format_arg(is_zero_int(), arg)) specs.alt = false; | |||
if (specs.fill[0] == '0') { | |||
if (arg.is_arithmetic() && specs.align != align::left) | |||
specs.align = align::numeric; | |||
@@ -452,7 +492,6 @@ void vprintf(buffer<Char>& buf, basic_string_view<Char> format, | |||
// Parse length and convert the argument to the required type. | |||
c = it != end ? *it++ : 0; | |||
Char t = it != end ? *it : 0; | |||
using detail::convert_arg; | |||
switch (c) { | |||
case 'h': | |||
if (t == 'h') { | |||
@@ -491,7 +530,7 @@ void vprintf(buffer<Char>& buf, basic_string_view<Char> format, | |||
} | |||
// Parse type. | |||
if (it == end) FMT_THROW(format_error("invalid format string")); | |||
if (it == end) throw_format_error("invalid format string"); | |||
char type = static_cast<char>(*it++); | |||
if (arg.is_integral()) { | |||
// Normalize type. | |||
@@ -502,22 +541,21 @@ void vprintf(buffer<Char>& buf, basic_string_view<Char> format, | |||
break; | |||
case 'c': | |||
visit_format_arg( | |||
detail::char_converter<basic_printf_context<OutputIt, Char>>(arg), | |||
arg); | |||
char_converter<basic_printf_context<iterator, Char>>(arg), arg); | |||
break; | |||
} | |||
} | |||
specs.type = parse_presentation_type(type); | |||
specs.type = parse_printf_presentation_type(type, arg.type()); | |||
if (specs.type == presentation_type::none) | |||
parse_ctx.on_error("invalid type specifier"); | |||
throw_format_error("invalid format specifier"); | |||
start = it; | |||
// Format argument. | |||
out = visit_format_arg( | |||
detail::printf_arg_formatter<OutputIt, Char>(out, specs, context), arg); | |||
printf_arg_formatter<iterator, 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 | |||
@@ -560,9 +598,9 @@ inline auto vsprintf( | |||
const S& fmt, | |||
basic_format_args<basic_printf_context_t<type_identity_t<Char>>> args) | |||
-> std::basic_string<Char> { | |||
basic_memory_buffer<Char> buffer; | |||
vprintf(buffer, to_string_view(fmt), args); | |||
return to_string(buffer); | |||
auto buf = basic_memory_buffer<Char>(); | |||
detail::vprintf(buf, detail::to_string_view(fmt), args); | |||
return to_string(buf); | |||
} | |||
/** | |||
@@ -578,7 +616,8 @@ template <typename S, typename... T, | |||
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> { | |||
using context = basic_printf_context_t<Char>; | |||
return vsprintf(to_string_view(fmt), fmt::make_format_args<context>(args...)); | |||
return vsprintf(detail::to_string_view(fmt), | |||
fmt::make_format_args<context>(args...)); | |||
} | |||
template <typename S, typename Char = char_t<S>> | |||
@@ -586,10 +625,10 @@ inline auto vfprintf( | |||
std::FILE* f, 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); | |||
size_t size = buffer.size(); | |||
return std::fwrite(buffer.data(), sizeof(Char), size, f) < size | |||
auto buf = basic_memory_buffer<Char>(); | |||
detail::vprintf(buf, detail::to_string_view(fmt), args); | |||
size_t size = buf.size(); | |||
return std::fwrite(buf.data(), sizeof(Char), size, f) < size | |||
? -1 | |||
: static_cast<int>(size); | |||
} | |||
@@ -606,7 +645,7 @@ inline auto vfprintf( | |||
template <typename S, typename... T, typename Char = char_t<S>> | |||
inline auto fprintf(std::FILE* f, const S& fmt, const T&... args) -> int { | |||
using context = basic_printf_context_t<Char>; | |||
return vfprintf(f, to_string_view(fmt), | |||
return vfprintf(f, detail::to_string_view(fmt), | |||
fmt::make_format_args<context>(args...)); | |||
} | |||
@@ -615,7 +654,7 @@ inline auto vprintf( | |||
const S& fmt, | |||
basic_format_args<basic_printf_context_t<type_identity_t<Char>>> args) | |||
-> int { | |||
return vfprintf(stdout, to_string_view(fmt), args); | |||
return vfprintf(stdout, detail::to_string_view(fmt), args); | |||
} | |||
/** | |||
@@ -630,28 +669,11 @@ inline auto vprintf( | |||
template <typename S, typename... T, FMT_ENABLE_IF(detail::is_string<S>::value)> | |||
inline auto printf(const S& fmt, const T&... args) -> int { | |||
return vprintf( | |||
to_string_view(fmt), | |||
detail::to_string_view(fmt), | |||
fmt::make_format_args<basic_printf_context_t<char_t<S>>>(args...)); | |||
} | |||
template <typename S, typename Char = char_t<S>> | |||
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 { | |||
return vfprintf(os, to_string_view(fmt), | |||
fmt::make_format_args<basic_printf_context_t<Char>>(args...)); | |||
} | |||
FMT_MODULE_EXPORT_END | |||
FMT_END_EXPORT | |||
FMT_END_NAMESPACE | |||
#endif // FMT_PRINTF_H_ |
@@ -22,27 +22,25 @@ FMT_BEGIN_NAMESPACE | |||
namespace detail { | |||
template <typename RangeT, typename OutputIterator> | |||
OutputIterator copy(const RangeT& range, OutputIterator out) { | |||
template <typename Range, typename OutputIt> | |||
auto copy(const Range& range, OutputIt out) -> OutputIt { | |||
for (auto it = range.begin(), end = range.end(); it != end; ++it) | |||
*out++ = *it; | |||
return out; | |||
} | |||
template <typename OutputIterator> | |||
OutputIterator copy(const char* str, OutputIterator out) { | |||
template <typename OutputIt> | |||
auto copy(const char* str, OutputIt out) -> OutputIt { | |||
while (*str) *out++ = *str++; | |||
return out; | |||
} | |||
template <typename OutputIterator> | |||
OutputIterator copy(char ch, OutputIterator out) { | |||
template <typename OutputIt> auto copy(char ch, OutputIt out) -> OutputIt { | |||
*out++ = ch; | |||
return out; | |||
} | |||
template <typename OutputIterator> | |||
OutputIterator copy(wchar_t ch, OutputIterator out) { | |||
template <typename OutputIt> auto copy(wchar_t ch, OutputIt out) -> OutputIt { | |||
*out++ = ch; | |||
return out; | |||
} | |||
@@ -55,7 +53,7 @@ template <typename T> class is_std_string_like { | |||
template <typename> static void check(...); | |||
public: | |||
static FMT_CONSTEXPR_DECL const bool value = | |||
static constexpr const bool value = | |||
is_string<T>::value || | |||
std::is_convertible<T, std_string_view<char>>::value || | |||
!std::is_void<decltype(check<T>(nullptr))>::value; | |||
@@ -69,10 +67,10 @@ template <typename T> class is_map { | |||
template <typename> static void check(...); | |||
public: | |||
#ifdef FMT_FORMAT_MAP_AS_LIST | |||
static FMT_CONSTEXPR_DECL const bool value = false; | |||
#ifdef FMT_FORMAT_MAP_AS_LIST // DEPRECATED! | |||
static constexpr const bool value = false; | |||
#else | |||
static FMT_CONSTEXPR_DECL const bool value = | |||
static constexpr const bool value = | |||
!std::is_void<decltype(check<T>(nullptr))>::value; | |||
#endif | |||
}; | |||
@@ -82,10 +80,10 @@ template <typename T> class is_set { | |||
template <typename> static void check(...); | |||
public: | |||
#ifdef FMT_FORMAT_SET_AS_LIST | |||
static FMT_CONSTEXPR_DECL const bool value = false; | |||
#ifdef FMT_FORMAT_SET_AS_LIST // DEPRECATED! | |||
static constexpr const bool value = false; | |||
#else | |||
static FMT_CONSTEXPR_DECL const bool value = | |||
static constexpr const bool value = | |||
!std::is_void<decltype(check<T>(nullptr))>::value && !is_map<T>::value; | |||
#endif | |||
}; | |||
@@ -94,7 +92,7 @@ template <typename... Ts> struct conditional_helper {}; | |||
template <typename T, typename _ = void> struct is_range_ : std::false_type {}; | |||
#if !FMT_MSC_VER || FMT_MSC_VER > 1800 | |||
#if !FMT_MSC_VERSION || FMT_MSC_VERSION > 1800 | |||
# define FMT_DECLTYPE_RETURN(val) \ | |||
->decltype(val) { return val; } \ | |||
@@ -157,8 +155,9 @@ template <typename T> | |||
struct has_mutable_begin_end< | |||
T, void_t<decltype(detail::range_begin(std::declval<T>())), | |||
decltype(detail::range_end(std::declval<T>())), | |||
enable_if_t<std::is_copy_constructible<T>::value>>> | |||
: std::true_type {}; | |||
// the extra int here is because older versions of MSVC don't | |||
// SFINAE properly unless there are distinct types | |||
int>> : std::true_type {}; | |||
template <typename T> | |||
struct is_range_<T, void> | |||
@@ -174,12 +173,12 @@ template <typename T> class is_tuple_like_ { | |||
template <typename> static void check(...); | |||
public: | |||
static FMT_CONSTEXPR_DECL const bool value = | |||
static constexpr const bool value = | |||
!std::is_void<decltype(check<T>(nullptr))>::value; | |||
}; | |||
// Check for integer_sequence | |||
#if defined(__cpp_lib_integer_sequence) || FMT_MSC_VER >= 1900 | |||
#if defined(__cpp_lib_integer_sequence) || FMT_MSC_VERSION >= 1900 | |||
template <typename T, T... N> | |||
using integer_sequence = std::integer_sequence<T, N...>; | |||
template <size_t... N> using index_sequence = std::index_sequence<N...>; | |||
@@ -202,467 +201,375 @@ template <size_t N> | |||
using make_index_sequence = make_integer_sequence<size_t, N>; | |||
#endif | |||
template <class Tuple, class F, size_t... Is> | |||
void for_each(index_sequence<Is...>, Tuple&& tup, F&& f) FMT_NOEXCEPT { | |||
using std::get; | |||
// using free function get<I>(T) now. | |||
const int _[] = {0, ((void)f(get<Is>(tup)), 0)...}; | |||
(void)_; // blocks warnings | |||
} | |||
template <class T> | |||
FMT_CONSTEXPR make_index_sequence<std::tuple_size<T>::value> get_indexes( | |||
T const&) { | |||
return {}; | |||
} | |||
template <class Tuple, class F> void for_each(Tuple&& tup, F&& f) { | |||
const auto indexes = get_indexes(tup); | |||
for_each(indexes, std::forward<Tuple>(tup), std::forward<F>(f)); | |||
} | |||
template <typename Range> | |||
using value_type = | |||
remove_cvref_t<decltype(*detail::range_begin(std::declval<Range>()))>; | |||
template <typename T> | |||
using tuple_index_sequence = make_index_sequence<std::tuple_size<T>::value>; | |||
template <typename OutputIt> OutputIt write_delimiter(OutputIt out) { | |||
*out++ = ','; | |||
*out++ = ' '; | |||
return out; | |||
} | |||
template <typename T, typename C, bool = is_tuple_like_<T>::value> | |||
class is_tuple_formattable_ { | |||
public: | |||
static constexpr const bool value = false; | |||
}; | |||
template <typename T, typename C> class is_tuple_formattable_<T, C, true> { | |||
template <std::size_t... Is> | |||
static std::true_type check2(index_sequence<Is...>, | |||
integer_sequence<bool, (Is == Is)...>); | |||
static std::false_type check2(...); | |||
template <std::size_t... Is> | |||
static decltype(check2( | |||
index_sequence<Is...>{}, | |||
integer_sequence< | |||
bool, (is_formattable<typename std::tuple_element<Is, T>::type, | |||
C>::value)...>{})) check(index_sequence<Is...>); | |||
struct singleton { | |||
unsigned char upper; | |||
unsigned char lower_count; | |||
public: | |||
static constexpr const bool value = | |||
decltype(check(tuple_index_sequence<T>{}))::value; | |||
}; | |||
inline auto is_printable(uint16_t x, const singleton* singletons, | |||
size_t singletons_size, | |||
const unsigned char* singleton_lowers, | |||
const unsigned char* normal, size_t normal_size) | |||
-> bool { | |||
auto upper = x >> 8; | |||
auto lower_start = 0; | |||
for (size_t i = 0; i < singletons_size; ++i) { | |||
auto s = singletons[i]; | |||
auto lower_end = lower_start + s.lower_count; | |||
if (upper < s.upper) break; | |||
if (upper == s.upper) { | |||
for (auto j = lower_start; j < lower_end; ++j) { | |||
if (singleton_lowers[j] == (x & 0xff)) return false; | |||
} | |||
} | |||
lower_start = lower_end; | |||
} | |||
template <typename Tuple, typename F, size_t... Is> | |||
FMT_CONSTEXPR void for_each(index_sequence<Is...>, Tuple&& t, F&& f) { | |||
using std::get; | |||
// Using a free function get<Is>(Tuple) now. | |||
const int unused[] = {0, ((void)f(get<Is>(t)), 0)...}; | |||
ignore_unused(unused); | |||
} | |||
auto xsigned = static_cast<int>(x); | |||
auto current = true; | |||
for (size_t i = 0; i < normal_size; ++i) { | |||
auto v = static_cast<int>(normal[i]); | |||
auto len = (v & 0x80) != 0 ? (v & 0x7f) << 8 | normal[++i] : v; | |||
xsigned -= len; | |||
if (xsigned < 0) break; | |||
current = !current; | |||
} | |||
return current; | |||
template <typename Tuple, typename F> | |||
FMT_CONSTEXPR void for_each(Tuple&& t, F&& f) { | |||
for_each(tuple_index_sequence<remove_cvref_t<Tuple>>(), | |||
std::forward<Tuple>(t), std::forward<F>(f)); | |||
} | |||
// Returns true iff the code point cp is printable. | |||
// This code is generated by support/printable.py. | |||
inline auto is_printable(uint32_t cp) -> bool { | |||
static constexpr singleton singletons0[] = { | |||
{0x00, 1}, {0x03, 5}, {0x05, 6}, {0x06, 3}, {0x07, 6}, {0x08, 8}, | |||
{0x09, 17}, {0x0a, 28}, {0x0b, 25}, {0x0c, 20}, {0x0d, 16}, {0x0e, 13}, | |||
{0x0f, 4}, {0x10, 3}, {0x12, 18}, {0x13, 9}, {0x16, 1}, {0x17, 5}, | |||
{0x18, 2}, {0x19, 3}, {0x1a, 7}, {0x1c, 2}, {0x1d, 1}, {0x1f, 22}, | |||
{0x20, 3}, {0x2b, 3}, {0x2c, 2}, {0x2d, 11}, {0x2e, 1}, {0x30, 3}, | |||
{0x31, 2}, {0x32, 1}, {0xa7, 2}, {0xa9, 2}, {0xaa, 4}, {0xab, 8}, | |||
{0xfa, 2}, {0xfb, 5}, {0xfd, 4}, {0xfe, 3}, {0xff, 9}, | |||
}; | |||
static constexpr unsigned char singletons0_lower[] = { | |||
0xad, 0x78, 0x79, 0x8b, 0x8d, 0xa2, 0x30, 0x57, 0x58, 0x8b, 0x8c, 0x90, | |||
0x1c, 0x1d, 0xdd, 0x0e, 0x0f, 0x4b, 0x4c, 0xfb, 0xfc, 0x2e, 0x2f, 0x3f, | |||
0x5c, 0x5d, 0x5f, 0xb5, 0xe2, 0x84, 0x8d, 0x8e, 0x91, 0x92, 0xa9, 0xb1, | |||
0xba, 0xbb, 0xc5, 0xc6, 0xc9, 0xca, 0xde, 0xe4, 0xe5, 0xff, 0x00, 0x04, | |||
0x11, 0x12, 0x29, 0x31, 0x34, 0x37, 0x3a, 0x3b, 0x3d, 0x49, 0x4a, 0x5d, | |||
0x84, 0x8e, 0x92, 0xa9, 0xb1, 0xb4, 0xba, 0xbb, 0xc6, 0xca, 0xce, 0xcf, | |||
0xe4, 0xe5, 0x00, 0x04, 0x0d, 0x0e, 0x11, 0x12, 0x29, 0x31, 0x34, 0x3a, | |||
0x3b, 0x45, 0x46, 0x49, 0x4a, 0x5e, 0x64, 0x65, 0x84, 0x91, 0x9b, 0x9d, | |||
0xc9, 0xce, 0xcf, 0x0d, 0x11, 0x29, 0x45, 0x49, 0x57, 0x64, 0x65, 0x8d, | |||
0x91, 0xa9, 0xb4, 0xba, 0xbb, 0xc5, 0xc9, 0xdf, 0xe4, 0xe5, 0xf0, 0x0d, | |||
0x11, 0x45, 0x49, 0x64, 0x65, 0x80, 0x84, 0xb2, 0xbc, 0xbe, 0xbf, 0xd5, | |||
0xd7, 0xf0, 0xf1, 0x83, 0x85, 0x8b, 0xa4, 0xa6, 0xbe, 0xbf, 0xc5, 0xc7, | |||
0xce, 0xcf, 0xda, 0xdb, 0x48, 0x98, 0xbd, 0xcd, 0xc6, 0xce, 0xcf, 0x49, | |||
0x4e, 0x4f, 0x57, 0x59, 0x5e, 0x5f, 0x89, 0x8e, 0x8f, 0xb1, 0xb6, 0xb7, | |||
0xbf, 0xc1, 0xc6, 0xc7, 0xd7, 0x11, 0x16, 0x17, 0x5b, 0x5c, 0xf6, 0xf7, | |||
0xfe, 0xff, 0x80, 0x0d, 0x6d, 0x71, 0xde, 0xdf, 0x0e, 0x0f, 0x1f, 0x6e, | |||
0x6f, 0x1c, 0x1d, 0x5f, 0x7d, 0x7e, 0xae, 0xaf, 0xbb, 0xbc, 0xfa, 0x16, | |||
0x17, 0x1e, 0x1f, 0x46, 0x47, 0x4e, 0x4f, 0x58, 0x5a, 0x5c, 0x5e, 0x7e, | |||
0x7f, 0xb5, 0xc5, 0xd4, 0xd5, 0xdc, 0xf0, 0xf1, 0xf5, 0x72, 0x73, 0x8f, | |||
0x74, 0x75, 0x96, 0x2f, 0x5f, 0x26, 0x2e, 0x2f, 0xa7, 0xaf, 0xb7, 0xbf, | |||
0xc7, 0xcf, 0xd7, 0xdf, 0x9a, 0x40, 0x97, 0x98, 0x30, 0x8f, 0x1f, 0xc0, | |||
0xc1, 0xce, 0xff, 0x4e, 0x4f, 0x5a, 0x5b, 0x07, 0x08, 0x0f, 0x10, 0x27, | |||
0x2f, 0xee, 0xef, 0x6e, 0x6f, 0x37, 0x3d, 0x3f, 0x42, 0x45, 0x90, 0x91, | |||
0xfe, 0xff, 0x53, 0x67, 0x75, 0xc8, 0xc9, 0xd0, 0xd1, 0xd8, 0xd9, 0xe7, | |||
0xfe, 0xff, | |||
}; | |||
static constexpr singleton singletons1[] = { | |||
{0x00, 6}, {0x01, 1}, {0x03, 1}, {0x04, 2}, {0x08, 8}, {0x09, 2}, | |||
{0x0a, 5}, {0x0b, 2}, {0x0e, 4}, {0x10, 1}, {0x11, 2}, {0x12, 5}, | |||
{0x13, 17}, {0x14, 1}, {0x15, 2}, {0x17, 2}, {0x19, 13}, {0x1c, 5}, | |||
{0x1d, 8}, {0x24, 1}, {0x6a, 3}, {0x6b, 2}, {0xbc, 2}, {0xd1, 2}, | |||
{0xd4, 12}, {0xd5, 9}, {0xd6, 2}, {0xd7, 2}, {0xda, 1}, {0xe0, 5}, | |||
{0xe1, 2}, {0xe8, 2}, {0xee, 32}, {0xf0, 4}, {0xf8, 2}, {0xf9, 2}, | |||
{0xfa, 2}, {0xfb, 1}, | |||
}; | |||
static constexpr unsigned char singletons1_lower[] = { | |||
0x0c, 0x27, 0x3b, 0x3e, 0x4e, 0x4f, 0x8f, 0x9e, 0x9e, 0x9f, 0x06, 0x07, | |||
0x09, 0x36, 0x3d, 0x3e, 0x56, 0xf3, 0xd0, 0xd1, 0x04, 0x14, 0x18, 0x36, | |||
0x37, 0x56, 0x57, 0x7f, 0xaa, 0xae, 0xaf, 0xbd, 0x35, 0xe0, 0x12, 0x87, | |||
0x89, 0x8e, 0x9e, 0x04, 0x0d, 0x0e, 0x11, 0x12, 0x29, 0x31, 0x34, 0x3a, | |||
0x45, 0x46, 0x49, 0x4a, 0x4e, 0x4f, 0x64, 0x65, 0x5c, 0xb6, 0xb7, 0x1b, | |||
0x1c, 0x07, 0x08, 0x0a, 0x0b, 0x14, 0x17, 0x36, 0x39, 0x3a, 0xa8, 0xa9, | |||
0xd8, 0xd9, 0x09, 0x37, 0x90, 0x91, 0xa8, 0x07, 0x0a, 0x3b, 0x3e, 0x66, | |||
0x69, 0x8f, 0x92, 0x6f, 0x5f, 0xee, 0xef, 0x5a, 0x62, 0x9a, 0x9b, 0x27, | |||
0x28, 0x55, 0x9d, 0xa0, 0xa1, 0xa3, 0xa4, 0xa7, 0xa8, 0xad, 0xba, 0xbc, | |||
0xc4, 0x06, 0x0b, 0x0c, 0x15, 0x1d, 0x3a, 0x3f, 0x45, 0x51, 0xa6, 0xa7, | |||
0xcc, 0xcd, 0xa0, 0x07, 0x19, 0x1a, 0x22, 0x25, 0x3e, 0x3f, 0xc5, 0xc6, | |||
0x04, 0x20, 0x23, 0x25, 0x26, 0x28, 0x33, 0x38, 0x3a, 0x48, 0x4a, 0x4c, | |||
0x50, 0x53, 0x55, 0x56, 0x58, 0x5a, 0x5c, 0x5e, 0x60, 0x63, 0x65, 0x66, | |||
0x6b, 0x73, 0x78, 0x7d, 0x7f, 0x8a, 0xa4, 0xaa, 0xaf, 0xb0, 0xc0, 0xd0, | |||
0xae, 0xaf, 0x79, 0xcc, 0x6e, 0x6f, 0x93, | |||
}; | |||
static constexpr unsigned char normal0[] = { | |||
0x00, 0x20, 0x5f, 0x22, 0x82, 0xdf, 0x04, 0x82, 0x44, 0x08, 0x1b, 0x04, | |||
0x06, 0x11, 0x81, 0xac, 0x0e, 0x80, 0xab, 0x35, 0x28, 0x0b, 0x80, 0xe0, | |||
0x03, 0x19, 0x08, 0x01, 0x04, 0x2f, 0x04, 0x34, 0x04, 0x07, 0x03, 0x01, | |||
0x07, 0x06, 0x07, 0x11, 0x0a, 0x50, 0x0f, 0x12, 0x07, 0x55, 0x07, 0x03, | |||
0x04, 0x1c, 0x0a, 0x09, 0x03, 0x08, 0x03, 0x07, 0x03, 0x02, 0x03, 0x03, | |||
0x03, 0x0c, 0x04, 0x05, 0x03, 0x0b, 0x06, 0x01, 0x0e, 0x15, 0x05, 0x3a, | |||
0x03, 0x11, 0x07, 0x06, 0x05, 0x10, 0x07, 0x57, 0x07, 0x02, 0x07, 0x15, | |||
0x0d, 0x50, 0x04, 0x43, 0x03, 0x2d, 0x03, 0x01, 0x04, 0x11, 0x06, 0x0f, | |||
0x0c, 0x3a, 0x04, 0x1d, 0x25, 0x5f, 0x20, 0x6d, 0x04, 0x6a, 0x25, 0x80, | |||
0xc8, 0x05, 0x82, 0xb0, 0x03, 0x1a, 0x06, 0x82, 0xfd, 0x03, 0x59, 0x07, | |||
0x15, 0x0b, 0x17, 0x09, 0x14, 0x0c, 0x14, 0x0c, 0x6a, 0x06, 0x0a, 0x06, | |||
0x1a, 0x06, 0x59, 0x07, 0x2b, 0x05, 0x46, 0x0a, 0x2c, 0x04, 0x0c, 0x04, | |||
0x01, 0x03, 0x31, 0x0b, 0x2c, 0x04, 0x1a, 0x06, 0x0b, 0x03, 0x80, 0xac, | |||
0x06, 0x0a, 0x06, 0x21, 0x3f, 0x4c, 0x04, 0x2d, 0x03, 0x74, 0x08, 0x3c, | |||
0x03, 0x0f, 0x03, 0x3c, 0x07, 0x38, 0x08, 0x2b, 0x05, 0x82, 0xff, 0x11, | |||
0x18, 0x08, 0x2f, 0x11, 0x2d, 0x03, 0x20, 0x10, 0x21, 0x0f, 0x80, 0x8c, | |||
0x04, 0x82, 0x97, 0x19, 0x0b, 0x15, 0x88, 0x94, 0x05, 0x2f, 0x05, 0x3b, | |||
0x07, 0x02, 0x0e, 0x18, 0x09, 0x80, 0xb3, 0x2d, 0x74, 0x0c, 0x80, 0xd6, | |||
0x1a, 0x0c, 0x05, 0x80, 0xff, 0x05, 0x80, 0xdf, 0x0c, 0xee, 0x0d, 0x03, | |||
0x84, 0x8d, 0x03, 0x37, 0x09, 0x81, 0x5c, 0x14, 0x80, 0xb8, 0x08, 0x80, | |||
0xcb, 0x2a, 0x38, 0x03, 0x0a, 0x06, 0x38, 0x08, 0x46, 0x08, 0x0c, 0x06, | |||
0x74, 0x0b, 0x1e, 0x03, 0x5a, 0x04, 0x59, 0x09, 0x80, 0x83, 0x18, 0x1c, | |||
0x0a, 0x16, 0x09, 0x4c, 0x04, 0x80, 0x8a, 0x06, 0xab, 0xa4, 0x0c, 0x17, | |||
0x04, 0x31, 0xa1, 0x04, 0x81, 0xda, 0x26, 0x07, 0x0c, 0x05, 0x05, 0x80, | |||
0xa5, 0x11, 0x81, 0x6d, 0x10, 0x78, 0x28, 0x2a, 0x06, 0x4c, 0x04, 0x80, | |||
0x8d, 0x04, 0x80, 0xbe, 0x03, 0x1b, 0x03, 0x0f, 0x0d, | |||
}; | |||
static constexpr unsigned char normal1[] = { | |||
0x5e, 0x22, 0x7b, 0x05, 0x03, 0x04, 0x2d, 0x03, 0x66, 0x03, 0x01, 0x2f, | |||
0x2e, 0x80, 0x82, 0x1d, 0x03, 0x31, 0x0f, 0x1c, 0x04, 0x24, 0x09, 0x1e, | |||
0x05, 0x2b, 0x05, 0x44, 0x04, 0x0e, 0x2a, 0x80, 0xaa, 0x06, 0x24, 0x04, | |||
0x24, 0x04, 0x28, 0x08, 0x34, 0x0b, 0x01, 0x80, 0x90, 0x81, 0x37, 0x09, | |||
0x16, 0x0a, 0x08, 0x80, 0x98, 0x39, 0x03, 0x63, 0x08, 0x09, 0x30, 0x16, | |||
0x05, 0x21, 0x03, 0x1b, 0x05, 0x01, 0x40, 0x38, 0x04, 0x4b, 0x05, 0x2f, | |||
0x04, 0x0a, 0x07, 0x09, 0x07, 0x40, 0x20, 0x27, 0x04, 0x0c, 0x09, 0x36, | |||
0x03, 0x3a, 0x05, 0x1a, 0x07, 0x04, 0x0c, 0x07, 0x50, 0x49, 0x37, 0x33, | |||
0x0d, 0x33, 0x07, 0x2e, 0x08, 0x0a, 0x81, 0x26, 0x52, 0x4e, 0x28, 0x08, | |||
0x2a, 0x56, 0x1c, 0x14, 0x17, 0x09, 0x4e, 0x04, 0x1e, 0x0f, 0x43, 0x0e, | |||
0x19, 0x07, 0x0a, 0x06, 0x48, 0x08, 0x27, 0x09, 0x75, 0x0b, 0x3f, 0x41, | |||
0x2a, 0x06, 0x3b, 0x05, 0x0a, 0x06, 0x51, 0x06, 0x01, 0x05, 0x10, 0x03, | |||
0x05, 0x80, 0x8b, 0x62, 0x1e, 0x48, 0x08, 0x0a, 0x80, 0xa6, 0x5e, 0x22, | |||
0x45, 0x0b, 0x0a, 0x06, 0x0d, 0x13, 0x39, 0x07, 0x0a, 0x36, 0x2c, 0x04, | |||
0x10, 0x80, 0xc0, 0x3c, 0x64, 0x53, 0x0c, 0x48, 0x09, 0x0a, 0x46, 0x45, | |||
0x1b, 0x48, 0x08, 0x53, 0x1d, 0x39, 0x81, 0x07, 0x46, 0x0a, 0x1d, 0x03, | |||
0x47, 0x49, 0x37, 0x03, 0x0e, 0x08, 0x0a, 0x06, 0x39, 0x07, 0x0a, 0x81, | |||
0x36, 0x19, 0x80, 0xb7, 0x01, 0x0f, 0x32, 0x0d, 0x83, 0x9b, 0x66, 0x75, | |||
0x0b, 0x80, 0xc4, 0x8a, 0xbc, 0x84, 0x2f, 0x8f, 0xd1, 0x82, 0x47, 0xa1, | |||
0xb9, 0x82, 0x39, 0x07, 0x2a, 0x04, 0x02, 0x60, 0x26, 0x0a, 0x46, 0x0a, | |||
0x28, 0x05, 0x13, 0x82, 0xb0, 0x5b, 0x65, 0x4b, 0x04, 0x39, 0x07, 0x11, | |||
0x40, 0x05, 0x0b, 0x02, 0x0e, 0x97, 0xf8, 0x08, 0x84, 0xd6, 0x2a, 0x09, | |||
0xa2, 0xf7, 0x81, 0x1f, 0x31, 0x03, 0x11, 0x04, 0x08, 0x81, 0x8c, 0x89, | |||
0x04, 0x6b, 0x05, 0x0d, 0x03, 0x09, 0x07, 0x10, 0x93, 0x60, 0x80, 0xf6, | |||
0x0a, 0x73, 0x08, 0x6e, 0x17, 0x46, 0x80, 0x9a, 0x14, 0x0c, 0x57, 0x09, | |||
0x19, 0x80, 0x87, 0x81, 0x47, 0x03, 0x85, 0x42, 0x0f, 0x15, 0x85, 0x50, | |||
0x2b, 0x80, 0xd5, 0x2d, 0x03, 0x1a, 0x04, 0x02, 0x81, 0x70, 0x3a, 0x05, | |||
0x01, 0x85, 0x00, 0x80, 0xd7, 0x29, 0x4c, 0x04, 0x0a, 0x04, 0x02, 0x83, | |||
0x11, 0x44, 0x4c, 0x3d, 0x80, 0xc2, 0x3c, 0x06, 0x01, 0x04, 0x55, 0x05, | |||
0x1b, 0x34, 0x02, 0x81, 0x0e, 0x2c, 0x04, 0x64, 0x0c, 0x56, 0x0a, 0x80, | |||
0xae, 0x38, 0x1d, 0x0d, 0x2c, 0x04, 0x09, 0x07, 0x02, 0x0e, 0x06, 0x80, | |||
0x9a, 0x83, 0xd8, 0x08, 0x0d, 0x03, 0x0d, 0x03, 0x74, 0x0c, 0x59, 0x07, | |||
0x0c, 0x14, 0x0c, 0x04, 0x38, 0x08, 0x0a, 0x06, 0x28, 0x08, 0x22, 0x4e, | |||
0x81, 0x54, 0x0c, 0x15, 0x03, 0x03, 0x05, 0x07, 0x09, 0x19, 0x07, 0x07, | |||
0x09, 0x03, 0x0d, 0x07, 0x29, 0x80, 0xcb, 0x25, 0x0a, 0x84, 0x06, | |||
}; | |||
auto lower = static_cast<uint16_t>(cp); | |||
if (cp < 0x10000) { | |||
return is_printable(lower, singletons0, | |||
sizeof(singletons0) / sizeof(*singletons0), | |||
singletons0_lower, normal0, sizeof(normal0)); | |||
} | |||
if (cp < 0x20000) { | |||
return is_printable(lower, singletons1, | |||
sizeof(singletons1) / sizeof(*singletons1), | |||
singletons1_lower, normal1, sizeof(normal1)); | |||
} | |||
if (0x2a6de <= cp && cp < 0x2a700) return false; | |||
if (0x2b735 <= cp && cp < 0x2b740) return false; | |||
if (0x2b81e <= cp && cp < 0x2b820) return false; | |||
if (0x2cea2 <= cp && cp < 0x2ceb0) return false; | |||
if (0x2ebe1 <= cp && cp < 0x2f800) return false; | |||
if (0x2fa1e <= cp && cp < 0x30000) return false; | |||
if (0x3134b <= cp && cp < 0xe0100) return false; | |||
if (0xe01f0 <= cp && cp < 0x110000) return false; | |||
return cp < 0x110000; | |||
template <typename Tuple1, typename Tuple2, typename F, size_t... Is> | |||
void for_each2(index_sequence<Is...>, Tuple1&& t1, Tuple2&& t2, F&& f) { | |||
using std::get; | |||
const int unused[] = {0, ((void)f(get<Is>(t1), get<Is>(t2)), 0)...}; | |||
ignore_unused(unused); | |||
} | |||
inline auto needs_escape(uint32_t cp) -> bool { | |||
return cp < 0x20 || cp == 0x7f || cp == '"' || cp == '\\' || | |||
!is_printable(cp); | |||
template <typename Tuple1, typename Tuple2, typename F> | |||
void for_each2(Tuple1&& t1, Tuple2&& t2, F&& f) { | |||
for_each2(tuple_index_sequence<remove_cvref_t<Tuple1>>(), | |||
std::forward<Tuple1>(t1), std::forward<Tuple2>(t2), | |||
std::forward<F>(f)); | |||
} | |||
template <typename Char> struct find_escape_result { | |||
const Char* begin; | |||
const Char* end; | |||
uint32_t cp; | |||
namespace tuple { | |||
// Workaround a bug in MSVC 2019 (v140). | |||
template <typename Char, typename... T> | |||
using result_t = std::tuple<formatter<remove_cvref_t<T>, Char>...>; | |||
using std::get; | |||
template <typename Tuple, typename Char, std::size_t... Is> | |||
auto get_formatters(index_sequence<Is...>) | |||
-> result_t<Char, decltype(get<Is>(std::declval<Tuple>()))...>; | |||
} // namespace tuple | |||
#if FMT_MSC_VERSION && FMT_MSC_VERSION < 1920 | |||
// Older MSVC doesn't get the reference type correctly for arrays. | |||
template <typename R> struct range_reference_type_impl { | |||
using type = decltype(*detail::range_begin(std::declval<R&>())); | |||
}; | |||
template <typename Char> | |||
auto find_escape(const Char* begin, const Char* end) | |||
-> find_escape_result<Char> { | |||
for (; begin != end; ++begin) { | |||
auto cp = static_cast<typename std::make_unsigned<Char>::type>(*begin); | |||
if (sizeof(Char) == 1 && cp >= 0x80) continue; | |||
if (needs_escape(cp)) return {begin, begin + 1, cp}; | |||
} | |||
return {begin, nullptr, 0}; | |||
} | |||
inline auto find_escape(const char* begin, const char* end) | |||
-> find_escape_result<char> { | |||
if (!is_utf8()) return find_escape<char>(begin, end); | |||
auto result = find_escape_result<char>{end, nullptr, 0}; | |||
for_each_codepoint(string_view(begin, to_unsigned(end - begin)), | |||
[&](uint32_t cp, string_view sv) { | |||
if (needs_escape(cp)) { | |||
result = {sv.begin(), sv.end(), cp}; | |||
return false; | |||
} | |||
return true; | |||
}); | |||
return result; | |||
} | |||
template <typename T, std::size_t N> struct range_reference_type_impl<T[N]> { | |||
using type = T&; | |||
}; | |||
template <typename Char, typename OutputIt> | |||
auto write_range_entry(OutputIt out, basic_string_view<Char> str) -> OutputIt { | |||
*out++ = '"'; | |||
auto begin = str.begin(), end = str.end(); | |||
do { | |||
auto escape = find_escape(begin, end); | |||
out = copy_str<Char>(begin, escape.begin, out); | |||
begin = escape.end; | |||
if (!begin) break; | |||
auto c = static_cast<Char>(escape.cp); | |||
switch (escape.cp) { | |||
case '\n': | |||
*out++ = '\\'; | |||
c = 'n'; | |||
break; | |||
case '\r': | |||
*out++ = '\\'; | |||
c = 'r'; | |||
break; | |||
case '\t': | |||
*out++ = '\\'; | |||
c = 't'; | |||
break; | |||
case '"': | |||
FMT_FALLTHROUGH; | |||
case '\\': | |||
*out++ = '\\'; | |||
break; | |||
default: | |||
if (is_utf8()) { | |||
if (escape.cp < 0x100) { | |||
out = format_to(out, "\\x{:02x}", escape.cp); | |||
continue; | |||
} | |||
if (escape.cp < 0x10000) { | |||
out = format_to(out, "\\u{:04x}", escape.cp); | |||
continue; | |||
} | |||
if (escape.cp < 0x110000) { | |||
out = format_to(out, "\\U{:08x}", escape.cp); | |||
continue; | |||
} | |||
} | |||
for (Char escape_char : basic_string_view<Char>( | |||
escape.begin, to_unsigned(escape.end - escape.begin))) { | |||
out = format_to( | |||
out, "\\x{:02x}", | |||
static_cast<typename std::make_unsigned<Char>::type>(escape_char)); | |||
} | |||
continue; | |||
} | |||
*out++ = c; | |||
} while (begin != end); | |||
*out++ = '"'; | |||
return out; | |||
} | |||
template <typename T> | |||
using range_reference_type = typename range_reference_type_impl<T>::type; | |||
#else | |||
template <typename Range> | |||
using range_reference_type = | |||
decltype(*detail::range_begin(std::declval<Range&>())); | |||
#endif | |||
template <typename Char, typename OutputIt, typename T, | |||
FMT_ENABLE_IF(std::is_convertible<T, std_string_view<char>>::value)> | |||
inline auto write_range_entry(OutputIt out, const T& str) -> OutputIt { | |||
auto sv = std_string_view<Char>(str); | |||
return write_range_entry<Char>(out, basic_string_view<Char>(sv)); | |||
} | |||
// We don't use the Range's value_type for anything, but we do need the Range's | |||
// reference type, with cv-ref stripped. | |||
template <typename Range> | |||
using uncvref_type = remove_cvref_t<range_reference_type<Range>>; | |||
template <typename Char, typename OutputIt, typename Arg, | |||
FMT_ENABLE_IF(std::is_same<Arg, Char>::value)> | |||
OutputIt write_range_entry(OutputIt out, const Arg v) { | |||
*out++ = '\''; | |||
*out++ = v; | |||
*out++ = '\''; | |||
return out; | |||
template <typename Formatter> | |||
FMT_CONSTEXPR auto maybe_set_debug_format(Formatter& f, bool set) | |||
-> decltype(f.set_debug_format(set)) { | |||
f.set_debug_format(set); | |||
} | |||
template <typename Formatter> | |||
FMT_CONSTEXPR void maybe_set_debug_format(Formatter&, ...) {} | |||
// These are not generic lambdas for compatibility with C++11. | |||
template <typename ParseContext> struct parse_empty_specs { | |||
template <typename Formatter> FMT_CONSTEXPR void operator()(Formatter& f) { | |||
f.parse(ctx); | |||
detail::maybe_set_debug_format(f, true); | |||
} | |||
ParseContext& ctx; | |||
}; | |||
template <typename FormatContext> struct format_tuple_element { | |||
using char_type = typename FormatContext::char_type; | |||
template <typename T> | |||
void operator()(const formatter<T, char_type>& f, const T& v) { | |||
if (i > 0) | |||
ctx.advance_to(detail::copy_str<char_type>(separator, ctx.out())); | |||
ctx.advance_to(f.format(v, ctx)); | |||
++i; | |||
} | |||
template < | |||
typename Char, typename OutputIt, typename Arg, | |||
FMT_ENABLE_IF(!is_std_string_like<typename std::decay<Arg>::type>::value && | |||
!std::is_same<Arg, Char>::value)> | |||
OutputIt write_range_entry(OutputIt out, const Arg& v) { | |||
return write<Char>(out, v); | |||
} | |||
int i; | |||
FormatContext& ctx; | |||
basic_string_view<char_type> separator; | |||
}; | |||
} // namespace detail | |||
template <typename T> struct is_tuple_like { | |||
static FMT_CONSTEXPR_DECL const bool value = | |||
static constexpr const bool value = | |||
detail::is_tuple_like_<T>::value && !detail::is_range_<T>::value; | |||
}; | |||
template <typename TupleT, typename Char> | |||
struct formatter<TupleT, Char, enable_if_t<fmt::is_tuple_like<TupleT>::value>> { | |||
template <typename T, typename C> struct is_tuple_formattable { | |||
static constexpr const bool value = | |||
detail::is_tuple_formattable_<T, C>::value; | |||
}; | |||
template <typename Tuple, typename Char> | |||
struct formatter<Tuple, Char, | |||
enable_if_t<fmt::is_tuple_like<Tuple>::value && | |||
fmt::is_tuple_formattable<Tuple, Char>::value>> { | |||
private: | |||
// C++11 generic lambda for format(). | |||
template <typename FormatContext> struct format_each { | |||
template <typename T> void operator()(const T& v) { | |||
if (i > 0) out = detail::write_delimiter(out); | |||
out = detail::write_range_entry<Char>(out, v); | |||
++i; | |||
} | |||
int i; | |||
typename FormatContext::iterator& out; | |||
}; | |||
decltype(detail::tuple::get_formatters<Tuple, Char>( | |||
detail::tuple_index_sequence<Tuple>())) formatters_; | |||
basic_string_view<Char> separator_ = detail::string_literal<Char, ',', ' '>{}; | |||
basic_string_view<Char> opening_bracket_ = | |||
detail::string_literal<Char, '('>{}; | |||
basic_string_view<Char> closing_bracket_ = | |||
detail::string_literal<Char, ')'>{}; | |||
public: | |||
FMT_CONSTEXPR formatter() {} | |||
FMT_CONSTEXPR void set_separator(basic_string_view<Char> sep) { | |||
separator_ = sep; | |||
} | |||
FMT_CONSTEXPR void set_brackets(basic_string_view<Char> open, | |||
basic_string_view<Char> close) { | |||
opening_bracket_ = open; | |||
closing_bracket_ = close; | |||
} | |||
template <typename ParseContext> | |||
FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) { | |||
return ctx.begin(); | |||
auto it = ctx.begin(); | |||
if (it != ctx.end() && *it != '}') | |||
FMT_THROW(format_error("invalid format specifier")); | |||
detail::for_each(formatters_, detail::parse_empty_specs<ParseContext>{ctx}); | |||
return it; | |||
} | |||
template <typename FormatContext = format_context> | |||
auto format(const TupleT& values, FormatContext& ctx) -> decltype(ctx.out()) { | |||
auto out = ctx.out(); | |||
*out++ = '('; | |||
detail::for_each(values, format_each<FormatContext>{0, out}); | |||
*out++ = ')'; | |||
return out; | |||
template <typename FormatContext> | |||
auto format(const Tuple& value, FormatContext& ctx) const | |||
-> decltype(ctx.out()) { | |||
ctx.advance_to(detail::copy_str<Char>(opening_bracket_, ctx.out())); | |||
detail::for_each2( | |||
formatters_, value, | |||
detail::format_tuple_element<FormatContext>{0, ctx, separator_}); | |||
return detail::copy_str<Char>(closing_bracket_, ctx.out()); | |||
} | |||
}; | |||
template <typename T, typename Char> struct is_range { | |||
static FMT_CONSTEXPR_DECL const bool value = | |||
static constexpr const bool value = | |||
detail::is_range_<T>::value && !detail::is_std_string_like<T>::value && | |||
!detail::is_map<T>::value && | |||
!std::is_convertible<T, std::basic_string<Char>>::value && | |||
!std::is_constructible<detail::std_string_view<Char>, T>::value; | |||
!std::is_convertible<T, detail::std_string_view<Char>>::value; | |||
}; | |||
namespace detail { | |||
template <typename Context> struct range_mapper { | |||
using mapper = arg_mapper<Context>; | |||
template <typename T, | |||
FMT_ENABLE_IF(has_formatter<remove_cvref_t<T>, Context>::value)> | |||
static auto map(T&& value) -> T&& { | |||
return static_cast<T&&>(value); | |||
} | |||
template <typename T, | |||
FMT_ENABLE_IF(!has_formatter<remove_cvref_t<T>, Context>::value)> | |||
static auto map(T&& value) | |||
-> decltype(mapper().map(static_cast<T&&>(value))) { | |||
return mapper().map(static_cast<T&&>(value)); | |||
} | |||
}; | |||
template <typename Char, typename Element> | |||
using range_formatter_type = | |||
formatter<remove_cvref_t<decltype(range_mapper<buffer_context<Char>>{}.map( | |||
std::declval<Element>()))>, | |||
Char>; | |||
template <typename R> | |||
using maybe_const_range = | |||
conditional_t<has_const_begin_end<R>::value, const R, R>; | |||
// Workaround a bug in MSVC 2015 and earlier. | |||
#if !FMT_MSC_VERSION || FMT_MSC_VERSION >= 1910 | |||
template <typename R, typename Char> | |||
struct is_formattable_delayed | |||
: is_formattable<uncvref_type<maybe_const_range<R>>, Char> {}; | |||
#endif | |||
} // namespace detail | |||
template <typename T, typename Char, typename Enable = void> | |||
struct range_formatter; | |||
template <typename T, typename Char> | |||
struct formatter< | |||
struct range_formatter< | |||
T, Char, | |||
enable_if_t< | |||
fmt::is_range<T, Char>::value | |||
// Workaround a bug in MSVC 2019 and earlier. | |||
#if !FMT_MSC_VER | |||
&& (is_formattable<detail::value_type<T>, Char>::value || | |||
detail::has_fallback_formatter<detail::value_type<T>, Char>::value) | |||
#endif | |||
>> { | |||
enable_if_t<conjunction<std::is_same<T, remove_cvref_t<T>>, | |||
is_formattable<T, Char>>::value>> { | |||
private: | |||
detail::range_formatter_type<Char, T> underlying_; | |||
basic_string_view<Char> separator_ = detail::string_literal<Char, ',', ' '>{}; | |||
basic_string_view<Char> opening_bracket_ = | |||
detail::string_literal<Char, '['>{}; | |||
basic_string_view<Char> closing_bracket_ = | |||
detail::string_literal<Char, ']'>{}; | |||
public: | |||
FMT_CONSTEXPR range_formatter() {} | |||
FMT_CONSTEXPR auto underlying() -> detail::range_formatter_type<Char, T>& { | |||
return underlying_; | |||
} | |||
FMT_CONSTEXPR void set_separator(basic_string_view<Char> sep) { | |||
separator_ = sep; | |||
} | |||
FMT_CONSTEXPR void set_brackets(basic_string_view<Char> open, | |||
basic_string_view<Char> close) { | |||
opening_bracket_ = open; | |||
closing_bracket_ = close; | |||
} | |||
template <typename ParseContext> | |||
FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) { | |||
return ctx.begin(); | |||
auto it = ctx.begin(); | |||
auto end = ctx.end(); | |||
if (it != end && *it == 'n') { | |||
set_brackets({}, {}); | |||
++it; | |||
} | |||
if (it != end && *it != '}') { | |||
if (*it != ':') FMT_THROW(format_error("invalid format specifier")); | |||
++it; | |||
} else { | |||
detail::maybe_set_debug_format(underlying_, true); | |||
} | |||
ctx.advance_to(it); | |||
return underlying_.parse(ctx); | |||
} | |||
template < | |||
typename FormatContext, typename U, | |||
FMT_ENABLE_IF( | |||
std::is_same<U, conditional_t<detail::has_const_begin_end<T>::value, | |||
const T, T>>::value)> | |||
auto format(U& range, FormatContext& ctx) -> decltype(ctx.out()) { | |||
#ifdef FMT_DEPRECATED_BRACED_RANGES | |||
Char prefix = '{'; | |||
Char postfix = '}'; | |||
#else | |||
Char prefix = detail::is_set<T>::value ? '{' : '['; | |||
Char postfix = detail::is_set<T>::value ? '}' : ']'; | |||
#endif | |||
template <typename R, typename FormatContext> | |||
auto format(R&& range, FormatContext& ctx) const -> decltype(ctx.out()) { | |||
detail::range_mapper<buffer_context<Char>> mapper; | |||
auto out = ctx.out(); | |||
*out++ = prefix; | |||
out = detail::copy_str<Char>(opening_bracket_, out); | |||
int i = 0; | |||
auto it = std::begin(range); | |||
auto end = std::end(range); | |||
auto it = detail::range_begin(range); | |||
auto end = detail::range_end(range); | |||
for (; it != end; ++it) { | |||
if (i > 0) out = detail::write_delimiter(out); | |||
out = detail::write_range_entry<Char>(out, *it); | |||
if (i > 0) out = detail::copy_str<Char>(separator_, out); | |||
ctx.advance_to(out); | |||
out = underlying_.format(mapper.map(*it), ctx); | |||
++i; | |||
} | |||
*out++ = postfix; | |||
out = detail::copy_str<Char>(closing_bracket_, out); | |||
return out; | |||
} | |||
}; | |||
template <typename T, typename Char> | |||
struct formatter< | |||
T, Char, | |||
enable_if_t< | |||
detail::is_map<T>::value | |||
// Workaround a bug in MSVC 2019 and earlier. | |||
#if !FMT_MSC_VER | |||
&& (is_formattable<detail::value_type<T>, Char>::value || | |||
detail::has_fallback_formatter<detail::value_type<T>, Char>::value) | |||
#endif | |||
>> { | |||
enum class range_format { disabled, map, set, sequence, string, debug_string }; | |||
namespace detail { | |||
template <typename T> | |||
struct range_format_kind_ | |||
: std::integral_constant<range_format, | |||
std::is_same<uncvref_type<T>, T>::value | |||
? range_format::disabled | |||
: is_map<T>::value ? range_format::map | |||
: is_set<T>::value ? range_format::set | |||
: range_format::sequence> {}; | |||
template <range_format K, typename R, typename Char, typename Enable = void> | |||
struct range_default_formatter; | |||
template <range_format K> | |||
using range_format_constant = std::integral_constant<range_format, K>; | |||
template <range_format K, typename R, typename Char> | |||
struct range_default_formatter< | |||
K, R, Char, | |||
enable_if_t<(K == range_format::sequence || K == range_format::map || | |||
K == range_format::set)>> { | |||
using range_type = detail::maybe_const_range<R>; | |||
range_formatter<detail::uncvref_type<range_type>, Char> underlying_; | |||
FMT_CONSTEXPR range_default_formatter() { init(range_format_constant<K>()); } | |||
FMT_CONSTEXPR void init(range_format_constant<range_format::set>) { | |||
underlying_.set_brackets(detail::string_literal<Char, '{'>{}, | |||
detail::string_literal<Char, '}'>{}); | |||
} | |||
FMT_CONSTEXPR void init(range_format_constant<range_format::map>) { | |||
underlying_.set_brackets(detail::string_literal<Char, '{'>{}, | |||
detail::string_literal<Char, '}'>{}); | |||
underlying_.underlying().set_brackets({}, {}); | |||
underlying_.underlying().set_separator( | |||
detail::string_literal<Char, ':', ' '>{}); | |||
} | |||
FMT_CONSTEXPR void init(range_format_constant<range_format::sequence>) {} | |||
template <typename ParseContext> | |||
FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) { | |||
return ctx.begin(); | |||
return underlying_.parse(ctx); | |||
} | |||
template < | |||
typename FormatContext, typename U, | |||
FMT_ENABLE_IF( | |||
std::is_same<U, conditional_t<detail::has_const_begin_end<T>::value, | |||
const T, T>>::value)> | |||
auto format(U& map, FormatContext& ctx) -> decltype(ctx.out()) { | |||
auto out = ctx.out(); | |||
*out++ = '{'; | |||
int i = 0; | |||
for (const auto& item : map) { | |||
if (i > 0) out = detail::write_delimiter(out); | |||
out = detail::write_range_entry<Char>(out, item.first); | |||
*out++ = ':'; | |||
*out++ = ' '; | |||
out = detail::write_range_entry<Char>(out, item.second); | |||
++i; | |||
} | |||
*out++ = '}'; | |||
return out; | |||
template <typename FormatContext> | |||
auto format(range_type& range, FormatContext& ctx) const | |||
-> decltype(ctx.out()) { | |||
return underlying_.format(range, ctx); | |||
} | |||
}; | |||
} // namespace detail | |||
template <typename T, typename Char, typename Enable = void> | |||
struct range_format_kind | |||
: conditional_t< | |||
is_range<T, Char>::value, detail::range_format_kind_<T>, | |||
std::integral_constant<range_format, range_format::disabled>> {}; | |||
template <typename R, typename Char> | |||
struct formatter< | |||
R, Char, | |||
enable_if_t<conjunction<bool_constant<range_format_kind<R, Char>::value != | |||
range_format::disabled> | |||
// Workaround a bug in MSVC 2015 and earlier. | |||
#if !FMT_MSC_VERSION || FMT_MSC_VERSION >= 1910 | |||
, | |||
detail::is_formattable_delayed<R, Char> | |||
#endif | |||
>::value>> | |||
: detail::range_default_formatter<range_format_kind<R, Char>::value, R, | |||
Char> { | |||
}; | |||
template <typename Char, typename... T> struct tuple_join_view : detail::view { | |||
const std::tuple<T...>& tuple; | |||
@@ -672,9 +579,6 @@ template <typename Char, typename... T> struct tuple_join_view : detail::view { | |||
: tuple(t), sep{s} {} | |||
}; | |||
template <typename Char, typename... T> | |||
using tuple_arg_join = tuple_join_view<Char, T...>; | |||
// Define FMT_TUPLE_JOIN_SPECIFIERS to enable experimental format specifiers | |||
// support in tuple_join. It is disabled by default because of issues with | |||
// the dynamic width and precision. | |||
@@ -744,7 +648,42 @@ struct formatter<tuple_join_view<Char, T...>, Char> { | |||
} | |||
}; | |||
FMT_MODULE_EXPORT_BEGIN | |||
namespace detail { | |||
// Check if T has an interface like a container adaptor (e.g. std::stack, | |||
// std::queue, std::priority_queue). | |||
template <typename T> class is_container_adaptor_like { | |||
template <typename U> static auto check(U* p) -> typename U::container_type; | |||
template <typename> static void check(...); | |||
public: | |||
static constexpr const bool value = | |||
!std::is_void<decltype(check<T>(nullptr))>::value; | |||
}; | |||
template <typename Container> struct all { | |||
const Container& c; | |||
auto begin() const -> typename Container::const_iterator { return c.begin(); } | |||
auto end() const -> typename Container::const_iterator { return c.end(); } | |||
}; | |||
} // namespace detail | |||
template <typename T, typename Char> | |||
struct formatter<T, Char, | |||
enable_if_t<detail::is_container_adaptor_like<T>::value>> | |||
: formatter<detail::all<typename T::container_type>, Char> { | |||
using all = detail::all<typename T::container_type>; | |||
template <typename FormatContext> | |||
auto format(const T& t, FormatContext& ctx) const -> decltype(ctx.out()) { | |||
struct getter : T { | |||
static auto get(const T& t) -> all { | |||
return {t.*(&getter::c)}; // Access c through the derived class. | |||
} | |||
}; | |||
return formatter<all>::format(getter::get(t), ctx); | |||
} | |||
}; | |||
FMT_BEGIN_EXPORT | |||
/** | |||
\rst | |||
@@ -787,7 +726,7 @@ auto join(std::initializer_list<T> list, string_view sep) | |||
return join(std::begin(list), std::end(list), sep); | |||
} | |||
FMT_MODULE_EXPORT_END | |||
FMT_END_EXPORT | |||
FMT_END_NAMESPACE | |||
#endif // FMT_RANGES_H_ |
@@ -0,0 +1,349 @@ | |||
// 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 <cstdlib> | |||
#include <exception> | |||
#include <memory> | |||
#include <thread> | |||
#include <type_traits> | |||
#include <typeinfo> | |||
#include <utility> | |||
#include "ostream.h" | |||
#if FMT_HAS_INCLUDE(<version>) | |||
# include <version> | |||
#endif | |||
// Checking FMT_CPLUSPLUS for warning suppression 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 | |||
// 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 | |||
#ifdef __cpp_lib_filesystem | |||
FMT_BEGIN_NAMESPACE | |||
namespace detail { | |||
template <typename Char> | |||
void write_escaped_path(basic_memory_buffer<Char>& quoted, | |||
const std::filesystem::path& p) { | |||
write_escaped_string<Char>(std::back_inserter(quoted), p.string<Char>()); | |||
} | |||
# ifdef _WIN32 | |||
template <> | |||
inline void write_escaped_path<char>(memory_buffer& quoted, | |||
const std::filesystem::path& p) { | |||
auto buf = basic_memory_buffer<wchar_t>(); | |||
write_escaped_string<wchar_t>(std::back_inserter(buf), p.native()); | |||
// Convert UTF-16 to UTF-8. | |||
if (!unicode_to_utf8<wchar_t>::convert(quoted, {buf.data(), buf.size()})) | |||
FMT_THROW(std::runtime_error("invalid utf16")); | |||
} | |||
# endif | |||
template <> | |||
inline void write_escaped_path<std::filesystem::path::value_type>( | |||
basic_memory_buffer<std::filesystem::path::value_type>& quoted, | |||
const std::filesystem::path& p) { | |||
write_escaped_string<std::filesystem::path::value_type>( | |||
std::back_inserter(quoted), p.native()); | |||
} | |||
} // namespace detail | |||
FMT_MODULE_EXPORT | |||
template <typename Char> | |||
struct formatter<std::filesystem::path, Char> | |||
: formatter<basic_string_view<Char>> { | |||
template <typename ParseContext> FMT_CONSTEXPR auto parse(ParseContext& ctx) { | |||
auto out = formatter<basic_string_view<Char>>::parse(ctx); | |||
this->set_debug_format(false); | |||
return out; | |||
} | |||
template <typename FormatContext> | |||
auto format(const std::filesystem::path& p, FormatContext& ctx) const -> | |||
typename FormatContext::iterator { | |||
auto quoted = basic_memory_buffer<Char>(); | |||
detail::write_escaped_path(quoted, p); | |||
return formatter<basic_string_view<Char>>::format( | |||
basic_string_view<Char>(quoted.data(), quoted.size()), ctx); | |||
} | |||
}; | |||
FMT_END_NAMESPACE | |||
#endif | |||
FMT_BEGIN_NAMESPACE | |||
FMT_MODULE_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_MODULE_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(std::optional<T> const& 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 | |||
#ifdef __cpp_lib_variant | |||
FMT_BEGIN_NAMESPACE | |||
FMT_MODULE_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()) { | |||
auto out = ctx.out(); | |||
out = detail::write<Char>(out, "monostate"); | |||
return out; | |||
} | |||
}; | |||
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; | |||
}; | |||
template <typename Char, typename OutputIt, typename T> | |||
auto write_variant_alternative(OutputIt out, const T& v) -> OutputIt { | |||
if constexpr (is_string<T>::value) | |||
return write_escaped_string<Char>(out, detail::to_string_view(v)); | |||
else if constexpr (std::is_same_v<T, Char>) | |||
return write_escaped_char(out, v); | |||
else | |||
return write<Char>(out, v); | |||
} | |||
} // 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_MODULE_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("); | |||
try { | |||
std::visit( | |||
[&](const auto& v) { | |||
out = detail::write_variant_alternative<Char>(out, v); | |||
}, | |||
value); | |||
} catch (const std::bad_variant_access&) { | |||
detail::write<Char>(out, "valueless by exception"); | |||
} | |||
*out++ = ')'; | |||
return out; | |||
} | |||
}; | |||
FMT_END_NAMESPACE | |||
#endif // __cpp_lib_variant | |||
FMT_BEGIN_NAMESPACE | |||
FMT_MODULE_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(out, ec.category().name(), format_specs<Char>()); | |||
out = detail::write<Char>(out, Char(':')); | |||
out = detail::write<Char>(out, ec.value()); | |||
return out; | |||
} | |||
}; | |||
FMT_MODULE_EXPORT | |||
template <typename T, typename Char> | |||
struct formatter< | |||
T, Char, | |||
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_ = true; | |||
} | |||
return it; | |||
} | |||
template <typename OutputIt> | |||
auto format(const std::exception& ex, | |||
basic_format_context<OutputIt, Char>& ctx) const -> OutputIt { | |||
format_specs<Char> spec; | |||
auto out = ctx.out(); | |||
if (!with_typename_) | |||
return detail::write_bytes(out, string_view(ex.what()), spec); | |||
const std::type_info& ti = typeid(ex); | |||
#ifdef FMT_HAS_ABI_CXA_DEMANGLE | |||
int status = 0; | |||
std::size_t size = 0; | |||
std::unique_ptr<char, decltype(&std::free)> 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()); | |||
} | |||
out = detail::write_bytes(out, demangled_name_view, spec); | |||
#elif FMT_MSC_VERSION | |||
string_view demangled_name_view(ti.name()); | |||
if (demangled_name_view.starts_with("class ")) | |||
demangled_name_view.remove_prefix(6); | |||
else if (demangled_name_view.starts_with("struct ")) | |||
demangled_name_view.remove_prefix(7); | |||
out = detail::write_bytes(out, demangled_name_view, spec); | |||
#else | |||
out = detail::write_bytes(out, string_view(ti.name()), spec); | |||
#endif | |||
out = detail::write<Char>(out, Char(':')); | |||
out = detail::write<Char>(out, Char(' ')); | |||
out = detail::write_bytes(out, string_view(ex.what()), spec); | |||
return out; | |||
} | |||
}; | |||
FMT_END_NAMESPACE | |||
#endif // FMT_STD_H_ |
@@ -0,0 +1,259 @@ | |||
// Formatting library for C++ - optional wchar_t and exotic character support | |||
// | |||
// Copyright (c) 2012 - present, Victor Zverovich | |||
// All rights reserved. | |||
// | |||
// For the license information refer to format.h. | |||
#ifndef FMT_XCHAR_H_ | |||
#define FMT_XCHAR_H_ | |||
#include <cwchar> | |||
#include "format.h" | |||
#ifndef FMT_STATIC_THOUSANDS_SEPARATOR | |||
# include <locale> | |||
#endif | |||
FMT_BEGIN_NAMESPACE | |||
namespace detail { | |||
template <typename T> | |||
using is_exotic_char = bool_constant<!std::is_same<T, char>::value>; | |||
inline auto write_loc(std::back_insert_iterator<detail::buffer<wchar_t>> out, | |||
loc_value value, const format_specs<wchar_t>& 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 wformat_parse_context = basic_format_parse_context<wchar_t>; | |||
using wformat_context = buffer_context<wchar_t>; | |||
using wformat_args = basic_format_args<wformat_context>; | |||
using wmemory_buffer = basic_memory_buffer<wchar_t>; | |||
#if FMT_GCC_VERSION && FMT_GCC_VERSION < 409 | |||
// Workaround broken conversion on older gcc. | |||
template <typename... Args> using wformat_string = wstring_view; | |||
inline auto runtime(wstring_view s) -> wstring_view { return s; } | |||
#else | |||
template <typename... 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 | |||
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<char32_t> : std::true_type {}; | |||
template <typename... Args> | |||
constexpr format_arg_store<wformat_context, Args...> make_wformat_args( | |||
const Args&... args) { | |||
return {args...}; | |||
} | |||
inline namespace literals { | |||
#if FMT_USE_USER_DEFINED_LITERALS && !FMT_USE_NONTYPE_TEMPLATE_ARGS | |||
constexpr detail::udl_arg<wchar_t> operator"" _a(const wchar_t* s, size_t) { | |||
return {s}; | |||
} | |||
#endif | |||
} // namespace literals | |||
template <typename It, typename Sentinel> | |||
auto join(It begin, Sentinel end, wstring_view sep) | |||
-> join_view<It, Sentinel, wchar_t> { | |||
return {begin, end, sep}; | |||
} | |||
template <typename Range> | |||
auto join(Range&& range, wstring_view sep) | |||
-> join_view<detail::iterator_t<Range>, detail::sentinel_t<Range>, | |||
wchar_t> { | |||
return join(std::begin(range), std::end(range), sep); | |||
} | |||
template <typename T> | |||
auto join(std::initializer_list<T> list, wstring_view sep) | |||
-> join_view<const T*, const T*, wchar_t> { | |||
return join(std::begin(list), std::end(list), sep); | |||
} | |||
template <typename Char, FMT_ENABLE_IF(!std::is_same<Char, char>::value)> | |||
auto vformat(basic_string_view<Char> format_str, | |||
basic_format_args<buffer_context<type_identity_t<Char>>> args) | |||
-> std::basic_string<Char> { | |||
basic_memory_buffer<Char> buffer; | |||
detail::vformat_to(buffer, format_str, args); | |||
return to_string(buffer); | |||
} | |||
template <typename... T> | |||
auto format(wformat_string<T...> fmt, T&&... args) -> std::wstring { | |||
return vformat(fmt::wstring_view(fmt), fmt::make_wformat_args(args...)); | |||
} | |||
// Pass char_t as a default template parameter instead of using | |||
// std::basic_string<char_t<S>> to reduce the symbol size. | |||
template <typename S, typename... Args, typename Char = char_t<S>, | |||
FMT_ENABLE_IF(!std::is_same<Char, char>::value && | |||
!std::is_same<Char, wchar_t>::value)> | |||
auto format(const S& format_str, Args&&... args) -> std::basic_string<Char> { | |||
return vformat(detail::to_string_view(format_str), | |||
fmt::make_format_args<buffer_context<Char>>(args...)); | |||
} | |||
template <typename Locale, typename S, typename Char = char_t<S>, | |||
FMT_ENABLE_IF(detail::is_locale<Locale>::value&& | |||
detail::is_exotic_char<Char>::value)> | |||
inline auto vformat( | |||
const Locale& loc, const S& format_str, | |||
basic_format_args<buffer_context<type_identity_t<Char>>> args) | |||
-> std::basic_string<Char> { | |||
return detail::vformat(loc, detail::to_string_view(format_str), args); | |||
} | |||
template <typename Locale, typename S, typename... Args, | |||
typename Char = char_t<S>, | |||
FMT_ENABLE_IF(detail::is_locale<Locale>::value&& | |||
detail::is_exotic_char<Char>::value)> | |||
inline auto format(const Locale& loc, const S& format_str, Args&&... args) | |||
-> std::basic_string<Char> { | |||
return detail::vformat(loc, detail::to_string_view(format_str), | |||
fmt::make_format_args<buffer_context<Char>>(args...)); | |||
} | |||
template <typename OutputIt, typename S, typename Char = char_t<S>, | |||
FMT_ENABLE_IF(detail::is_output_iterator<OutputIt, Char>::value&& | |||
detail::is_exotic_char<Char>::value)> | |||
auto vformat_to(OutputIt out, const S& format_str, | |||
basic_format_args<buffer_context<type_identity_t<Char>>> args) | |||
-> OutputIt { | |||
auto&& buf = detail::get_buffer<Char>(out); | |||
detail::vformat_to(buf, detail::to_string_view(format_str), args); | |||
return detail::get_iterator(buf, out); | |||
} | |||
template <typename OutputIt, typename S, typename... Args, | |||
typename Char = char_t<S>, | |||
FMT_ENABLE_IF(detail::is_output_iterator<OutputIt, Char>::value&& | |||
detail::is_exotic_char<Char>::value)> | |||
inline auto format_to(OutputIt out, const S& fmt, Args&&... args) -> OutputIt { | |||
return vformat_to(out, detail::to_string_view(fmt), | |||
fmt::make_format_args<buffer_context<Char>>(args...)); | |||
} | |||
template <typename Locale, typename S, typename OutputIt, typename... Args, | |||
typename Char = char_t<S>, | |||
FMT_ENABLE_IF(detail::is_output_iterator<OutputIt, Char>::value&& | |||
detail::is_locale<Locale>::value&& | |||
detail::is_exotic_char<Char>::value)> | |||
inline auto vformat_to( | |||
OutputIt out, const Locale& loc, const S& format_str, | |||
basic_format_args<buffer_context<type_identity_t<Char>>> args) -> OutputIt { | |||
auto&& buf = detail::get_buffer<Char>(out); | |||
vformat_to(buf, detail::to_string_view(format_str), args, | |||
detail::locale_ref(loc)); | |||
return detail::get_iterator(buf, out); | |||
} | |||
template < | |||
typename OutputIt, typename Locale, typename S, typename... Args, | |||
typename Char = char_t<S>, | |||
bool enable = detail::is_output_iterator<OutputIt, 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, | |||
Args&&... args) -> | |||
typename std::enable_if<enable, OutputIt>::type { | |||
return vformat_to(out, loc, detail::to_string_view(format_str), | |||
fmt::make_format_args<buffer_context<Char>>(args...)); | |||
} | |||
template <typename OutputIt, typename Char, typename... Args, | |||
FMT_ENABLE_IF(detail::is_output_iterator<OutputIt, Char>::value&& | |||
detail::is_exotic_char<Char>::value)> | |||
inline auto vformat_to_n( | |||
OutputIt out, size_t n, basic_string_view<Char> format_str, | |||
basic_format_args<buffer_context<type_identity_t<Char>>> args) | |||
-> format_to_n_result<OutputIt> { | |||
detail::iterator_buffer<OutputIt, Char, detail::fixed_buffer_traits> buf(out, | |||
n); | |||
detail::vformat_to(buf, format_str, args); | |||
return {buf.out(), buf.count()}; | |||
} | |||
template <typename OutputIt, typename S, typename... Args, | |||
typename Char = char_t<S>, | |||
FMT_ENABLE_IF(detail::is_output_iterator<OutputIt, Char>::value&& | |||
detail::is_exotic_char<Char>::value)> | |||
inline auto format_to_n(OutputIt out, size_t n, const S& fmt, | |||
const Args&... args) -> format_to_n_result<OutputIt> { | |||
return vformat_to_n(out, n, detail::to_string_view(fmt), | |||
fmt::make_format_args<buffer_context<Char>>(args...)); | |||
} | |||
template <typename S, typename... Args, typename Char = char_t<S>, | |||
FMT_ENABLE_IF(detail::is_exotic_char<Char>::value)> | |||
inline auto formatted_size(const S& fmt, Args&&... args) -> size_t { | |||
detail::counting_buffer<Char> buf; | |||
detail::vformat_to(buf, detail::to_string_view(fmt), | |||
fmt::make_format_args<buffer_context<Char>>(args...)); | |||
return buf.count(); | |||
} | |||
inline void vprint(std::FILE* f, wstring_view fmt, wformat_args args) { | |||
wmemory_buffer buffer; | |||
detail::vformat_to(buffer, fmt, args); | |||
buffer.push_back(L'\0'); | |||
if (std::fputws(buffer.data(), f) == -1) | |||
FMT_THROW(system_error(errno, FMT_STRING("cannot write to file"))); | |||
} | |||
inline void vprint(wstring_view fmt, wformat_args args) { | |||
vprint(stdout, fmt, args); | |||
} | |||
template <typename... T> | |||
void print(std::FILE* f, wformat_string<T...> fmt, T&&... args) { | |||
return vprint(f, wstring_view(fmt), fmt::make_wformat_args(args...)); | |||
} | |||
template <typename... T> void print(wformat_string<T...> fmt, T&&... args) { | |||
return vprint(wstring_view(fmt), fmt::make_wformat_args(args...)); | |||
} | |||
template <typename... 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)...)); | |||
} | |||
/** | |||
Converts *value* to ``std::wstring`` using the default format for type *T*. | |||
*/ | |||
template <typename T> inline auto to_wstring(const T& value) -> std::wstring { | |||
return format(FMT_STRING(L"{}"), value); | |||
} | |||
FMT_END_EXPORT | |||
FMT_END_NAMESPACE | |||
#endif // FMT_XCHAR_H_ |
@@ -1,11 +1,11 @@ | |||
/*- | |||
* Copyright 2022 Vsevolod Stakhov | |||
/* | |||
* Copyright 2023 Vsevolod Stakhov | |||
* | |||
* Licensed under the Apache License, Version 2.0 (the "License"); | |||
* you may not use this file except in compliance with the License. | |||
* You may obtain a copy of the License at | |||
* | |||
* http://www.apache.org/licenses/LICENSE-2.0 | |||
* http://www.apache.org/licenses/LICENSE-2.0 | |||
* | |||
* Unless required by applicable law or agreed to in writing, software | |||
* distributed under the License is distributed on an "AS IS" BASIS, | |||
@@ -13,6 +13,7 @@ | |||
* See the License for the specific language governing permissions and | |||
* limitations under the License. | |||
*/ | |||
#include "config.h" | |||
#ifdef WITH_HYPERSCAN |