1
0
mirror of https://github.com/VCMP-SqMod/SqMod.git synced 2026-04-21 11:37:23 +02:00

Update cpp fmt library.

This commit is contained in:
Sandu Liviu Catalin
2021-07-02 21:18:22 +03:00
parent 8db8ee4f33
commit ab1f65b53f
16 changed files with 4406 additions and 4633 deletions
+215 -97
View File
@@ -15,7 +15,6 @@
#include <sstream>
#include "format.h"
#include "locale.h"
FMT_BEGIN_NAMESPACE
@@ -283,12 +282,84 @@ To safe_duration_cast(std::chrono::duration<FromRep, FromPeriod> from,
#define FMT_NOMACRO
namespace detail {
template <typename T = void> struct null {};
inline null<> localtime_r FMT_NOMACRO(...) { return null<>(); }
inline null<> localtime_s(...) { return null<>(); }
inline null<> gmtime_r(...) { return null<>(); }
inline null<> gmtime_s(...) { return null<>(); }
inline auto do_write(const std::tm& time, const std::locale& loc, char format,
char modifier) -> std::string {
auto&& os = std::ostringstream();
os.imbue(loc);
using iterator = std::ostreambuf_iterator<char>;
const auto& facet = std::use_facet<std::time_put<char, iterator>>(loc);
auto end = facet.put(os, os, ' ', &time, format, modifier);
if (end.failed()) FMT_THROW(format_error("failed to format time"));
auto str = os.str();
if (!detail::is_utf8() || loc == std::locale::classic()) return str;
// char16_t and char32_t codecvts are broken in MSVC (linkage errors) and
// gcc-4.
#if FMT_MSC_VER != 0 || \
(defined(__GLIBCXX__) && !defined(_GLIBCXX_USE_DUAL_ABI))
// The _GLIBCXX_USE_DUAL_ABI macro is always defined in libstdc++ from gcc-5
// and newer.
using code_unit = wchar_t;
#else
using code_unit = char32_t;
#endif
auto& f = std::use_facet<std::codecvt<code_unit, char, std::mbstate_t>>(loc);
auto mb = std::mbstate_t();
const char* from_next = nullptr;
code_unit* to_next = nullptr;
constexpr size_t buf_size = 32;
code_unit buf[buf_size] = {};
auto result = f.in(mb, str.data(), str.data() + str.size(), from_next, buf,
buf + buf_size, to_next);
if (result != std::codecvt_base::ok)
FMT_THROW(format_error("failed to format time"));
str.clear();
for (code_unit* p = buf; p != to_next; ++p) {
uint32_t c = static_cast<uint32_t>(*p);
if (sizeof(code_unit) == 2 && c >= 0xd800 && c <= 0xdfff) {
// surrogate pair
++p;
if (p == to_next || (c & 0xfc00) != 0xd800 || (*p & 0xfc00) != 0xdc00) {
FMT_THROW(format_error("failed to format time"));
}
c = (c << 10) + static_cast<uint32_t>(*p) - 0x35fdc00;
}
if (c < 0x80) {
str.push_back(static_cast<char>(c));
} else if (c < 0x800) {
str.push_back(static_cast<char>(0xc0 | (c >> 6)));
str.push_back(static_cast<char>(0x80 | (c & 0x3f)));
} else if ((c >= 0x800 && c <= 0xd7ff) || (c >= 0xe000 && c <= 0xffff)) {
str.push_back(static_cast<char>(0xe0 | (c >> 12)));
str.push_back(static_cast<char>(0x80 | ((c & 0xfff) >> 6)));
str.push_back(static_cast<char>(0x80 | (c & 0x3f)));
} else if (c >= 0x10000 && c <= 0x10ffff) {
str.push_back(static_cast<char>(0xf0 | (c >> 18)));
str.push_back(static_cast<char>(0x80 | ((c & 0x3ffff) >> 12)));
str.push_back(static_cast<char>(0x80 | ((c & 0xfff) >> 6)));
str.push_back(static_cast<char>(0x80 | (c & 0x3f)));
} else {
FMT_THROW(format_error("failed to format time"));
}
}
return str;
}
template <typename OutputIt>
auto write(OutputIt out, const std::tm& time, const std::locale& loc,
char format, char modifier = 0) -> OutputIt {
auto str = do_write(time, loc, format, modifier);
return std::copy(str.begin(), str.end(), out);
}
} // namespace detail
FMT_MODULE_EXPORT_BEGIN
/**
Converts given time since epoch as ``std::time_t`` value into calendar time,
expressed in local time. Unlike ``std::localtime``, this function is
@@ -380,7 +451,8 @@ inline std::tm gmtime(
return gmtime(std::chrono::system_clock::to_time_t(time_point));
}
namespace detail {
FMT_BEGIN_DETAIL_NAMESPACE
inline size_t strftime(char* str, size_t count, const char* format,
const std::tm* time) {
// Assign to a pointer to suppress GCCs -Wformat-nonliteral
@@ -399,19 +471,43 @@ inline size_t strftime(wchar_t* str, size_t count, const wchar_t* format,
wcsftime = std::wcsftime;
return wcsftime(str, count, format, time);
}
} // namespace detail
template <typename Char>
struct formatter<std::chrono::time_point<std::chrono::system_clock>, Char>
: formatter<std::tm, Char> {
FMT_END_DETAIL_NAMESPACE
template <typename Char, typename Duration>
struct formatter<std::chrono::time_point<std::chrono::system_clock, Duration>,
Char> : formatter<std::tm, Char> {
FMT_CONSTEXPR formatter() {
this->specs = {default_specs, sizeof(default_specs) / sizeof(Char)};
}
template <typename ParseContext>
FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) {
auto it = ctx.begin();
if (it != ctx.end() && *it == ':') ++it;
auto end = it;
while (end != ctx.end() && *end != '}') ++end;
if (end != it) this->specs = {it, detail::to_unsigned(end - it)};
return end;
}
template <typename FormatContext>
auto format(std::chrono::time_point<std::chrono::system_clock> val,
FormatContext& ctx) -> decltype(ctx.out()) {
std::tm time = localtime(val);
return formatter<std::tm, Char>::format(time, ctx);
}
static constexpr Char default_specs[] = {'%', 'Y', '-', '%', 'm', '-',
'%', 'd', ' ', '%', 'H', ':',
'%', 'M', ':', '%', 'S'};
};
template <typename Char, typename Duration>
constexpr Char
formatter<std::chrono::time_point<std::chrono::system_clock, Duration>,
Char>::default_specs[];
template <typename Char> struct formatter<std::tm, Char> {
template <typename ParseContext>
FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) {
@@ -428,6 +524,10 @@ template <typename Char> struct formatter<std::tm, Char> {
-> decltype(ctx.out()) {
basic_memory_buffer<Char> tm_format;
tm_format.append(specs.begin(), specs.end());
// By appending an extra space we can distinguish an empty result that
// indicates insufficient buffer size from a guaranteed non-empty result
// https://github.com/fmtlib/fmt/issues/2238
tm_format.push_back(' ');
tm_format.push_back('\0');
basic_memory_buffer<Char> buf;
size_t start = buf.size();
@@ -438,83 +538,40 @@ template <typename Char> struct formatter<std::tm, Char> {
buf.resize(start + count);
break;
}
if (size >= tm_format.size() * 256) {
// If the buffer is 256 times larger than the format string, assume
// that `strftime` gives an empty result. There doesn't seem to be a
// better way to distinguish the two cases:
// https://github.com/fmtlib/fmt/issues/367
break;
}
const size_t MIN_GROWTH = 10;
buf.reserve(buf.capacity() + (size > MIN_GROWTH ? size : MIN_GROWTH));
}
return std::copy(buf.begin(), buf.end(), ctx.out());
// Remove the extra space.
return std::copy(buf.begin(), buf.end() - 1, ctx.out());
}
basic_string_view<Char> specs;
};
namespace detail {
template <typename Period> FMT_CONSTEXPR const char* get_units() {
FMT_BEGIN_DETAIL_NAMESPACE
template <typename Period> FMT_CONSTEXPR inline const char* get_units() {
if (std::is_same<Period, std::atto>::value) return "as";
if (std::is_same<Period, std::femto>::value) return "fs";
if (std::is_same<Period, std::pico>::value) return "ps";
if (std::is_same<Period, std::nano>::value) return "ns";
if (std::is_same<Period, std::micro>::value) return "µs";
if (std::is_same<Period, std::milli>::value) return "ms";
if (std::is_same<Period, std::centi>::value) return "cs";
if (std::is_same<Period, std::deci>::value) return "ds";
if (std::is_same<Period, std::ratio<1>>::value) return "s";
if (std::is_same<Period, std::deca>::value) return "das";
if (std::is_same<Period, std::hecto>::value) return "hs";
if (std::is_same<Period, std::kilo>::value) return "ks";
if (std::is_same<Period, std::mega>::value) return "Ms";
if (std::is_same<Period, std::giga>::value) return "Gs";
if (std::is_same<Period, std::tera>::value) return "Ts";
if (std::is_same<Period, std::peta>::value) return "Ps";
if (std::is_same<Period, std::exa>::value) return "Es";
if (std::is_same<Period, std::ratio<60>>::value) return "m";
if (std::is_same<Period, std::ratio<3600>>::value) return "h";
return nullptr;
}
template <> FMT_CONSTEXPR inline const char* get_units<std::atto>() {
return "as";
}
template <> FMT_CONSTEXPR inline const char* get_units<std::femto>() {
return "fs";
}
template <> FMT_CONSTEXPR inline const char* get_units<std::pico>() {
return "ps";
}
template <> FMT_CONSTEXPR inline const char* get_units<std::nano>() {
return "ns";
}
template <> FMT_CONSTEXPR inline const char* get_units<std::micro>() {
return "µs";
}
template <> FMT_CONSTEXPR inline const char* get_units<std::milli>() {
return "ms";
}
template <> FMT_CONSTEXPR inline const char* get_units<std::centi>() {
return "cs";
}
template <> FMT_CONSTEXPR inline const char* get_units<std::deci>() {
return "ds";
}
template <> FMT_CONSTEXPR inline const char* get_units<std::ratio<1>>() {
return "s";
}
template <> FMT_CONSTEXPR inline const char* get_units<std::deca>() {
return "das";
}
template <> FMT_CONSTEXPR inline const char* get_units<std::hecto>() {
return "hs";
}
template <> FMT_CONSTEXPR inline const char* get_units<std::kilo>() {
return "ks";
}
template <> FMT_CONSTEXPR inline const char* get_units<std::mega>() {
return "Ms";
}
template <> FMT_CONSTEXPR inline const char* get_units<std::giga>() {
return "Gs";
}
template <> FMT_CONSTEXPR inline const char* get_units<std::tera>() {
return "Ts";
}
template <> FMT_CONSTEXPR inline const char* get_units<std::peta>() {
return "Ps";
}
template <> FMT_CONSTEXPR inline const char* get_units<std::exa>() {
return "Es";
}
template <> FMT_CONSTEXPR inline const char* get_units<std::ratio<60>>() {
return "m";
}
template <> FMT_CONSTEXPR inline const char* get_units<std::ratio<3600>>() {
return "h";
}
enum class numeric_system {
standard,
@@ -680,34 +737,50 @@ FMT_CONSTEXPR const Char* parse_chrono_format(const Char* begin,
return ptr;
}
struct chrono_format_checker {
FMT_NORETURN void report_no_date() { FMT_THROW(format_error("no date")); }
template <typename Derived> struct null_chrono_spec_handler {
FMT_CONSTEXPR void unsupported() {
static_cast<Derived*>(this)->unsupported();
}
FMT_CONSTEXPR void on_abbr_weekday() { unsupported(); }
FMT_CONSTEXPR void on_full_weekday() { unsupported(); }
FMT_CONSTEXPR void on_dec0_weekday(numeric_system) { unsupported(); }
FMT_CONSTEXPR void on_dec1_weekday(numeric_system) { unsupported(); }
FMT_CONSTEXPR void on_abbr_month() { unsupported(); }
FMT_CONSTEXPR void on_full_month() { unsupported(); }
FMT_CONSTEXPR void on_24_hour(numeric_system) { unsupported(); }
FMT_CONSTEXPR void on_12_hour(numeric_system) { unsupported(); }
FMT_CONSTEXPR void on_minute(numeric_system) { unsupported(); }
FMT_CONSTEXPR void on_second(numeric_system) { unsupported(); }
FMT_CONSTEXPR void on_datetime(numeric_system) { unsupported(); }
FMT_CONSTEXPR void on_loc_date(numeric_system) { unsupported(); }
FMT_CONSTEXPR void on_loc_time(numeric_system) { unsupported(); }
FMT_CONSTEXPR void on_us_date() { unsupported(); }
FMT_CONSTEXPR void on_iso_date() { unsupported(); }
FMT_CONSTEXPR void on_12_hour_time() { unsupported(); }
FMT_CONSTEXPR void on_24_hour_time() { unsupported(); }
FMT_CONSTEXPR void on_iso_time() { unsupported(); }
FMT_CONSTEXPR void on_am_pm() { unsupported(); }
FMT_CONSTEXPR void on_duration_value() { unsupported(); }
FMT_CONSTEXPR void on_duration_unit() { unsupported(); }
FMT_CONSTEXPR void on_utc_offset() { unsupported(); }
FMT_CONSTEXPR void on_tz_name() { unsupported(); }
};
struct chrono_format_checker : null_chrono_spec_handler<chrono_format_checker> {
FMT_NORETURN void unsupported() { FMT_THROW(format_error("no date")); }
template <typename Char>
FMT_CONSTEXPR void on_text(const Char*, const Char*) {}
FMT_NORETURN void on_abbr_weekday() { report_no_date(); }
FMT_NORETURN void on_full_weekday() { report_no_date(); }
FMT_NORETURN void on_dec0_weekday(numeric_system) { report_no_date(); }
FMT_NORETURN void on_dec1_weekday(numeric_system) { report_no_date(); }
FMT_NORETURN void on_abbr_month() { report_no_date(); }
FMT_NORETURN void on_full_month() { report_no_date(); }
FMT_CONSTEXPR void on_24_hour(numeric_system) {}
FMT_CONSTEXPR void on_12_hour(numeric_system) {}
FMT_CONSTEXPR void on_minute(numeric_system) {}
FMT_CONSTEXPR void on_second(numeric_system) {}
FMT_NORETURN void on_datetime(numeric_system) { report_no_date(); }
FMT_NORETURN void on_loc_date(numeric_system) { report_no_date(); }
FMT_NORETURN void on_loc_time(numeric_system) { report_no_date(); }
FMT_NORETURN void on_us_date() { report_no_date(); }
FMT_NORETURN void on_iso_date() { report_no_date(); }
FMT_CONSTEXPR void on_12_hour_time() {}
FMT_CONSTEXPR void on_24_hour_time() {}
FMT_CONSTEXPR void on_iso_time() {}
FMT_CONSTEXPR void on_am_pm() {}
FMT_CONSTEXPR void on_duration_value() {}
FMT_CONSTEXPR void on_duration_unit() {}
FMT_NORETURN void on_utc_offset() { report_no_date(); }
FMT_NORETURN void on_tz_name() { report_no_date(); }
};
template <typename T, FMT_ENABLE_IF(std::is_integral<T>::value)>
@@ -859,6 +932,7 @@ struct chrono_formatter {
FormatContext& context;
OutputIt out;
int precision;
bool localized = false;
// rep is unsigned to avoid overflow.
using rep =
conditional_t<std::is_integral<Rep>::value && sizeof(Rep) < sizeof(int),
@@ -953,13 +1027,9 @@ struct chrono_formatter {
void format_localized(const tm& time, char format, char modifier = 0) {
if (isnan(val)) return write_nan();
auto locale = context.locale().template get<std::locale>();
auto& facet = std::use_facet<std::time_put<char_type>>(locale);
std::basic_ostringstream<char_type> os;
os.imbue(locale);
facet.put(os, os, ' ', &time, format, modifier);
auto str = os.str();
std::copy(str.begin(), str.end(), out);
const auto& loc = localized ? context.locale().template get<std::locale>()
: std::locale::classic();
out = detail::write(out, time, loc, format, modifier);
}
void on_text(const char_type* begin, const char_type* end) {
@@ -1072,7 +1142,48 @@ struct chrono_formatter {
out = format_duration_unit<char_type, Period>(out);
}
};
} // namespace detail
FMT_END_DETAIL_NAMESPACE
#if defined(__cpp_lib_chrono) && __cpp_lib_chrono >= 201907
using weekday = std::chrono::weekday;
#else
// A fallback version of weekday.
class weekday {
private:
unsigned char value;
public:
weekday() = default;
explicit constexpr weekday(unsigned wd) noexcept
: value(static_cast<unsigned char>(wd != 7 ? wd : 0)) {}
constexpr unsigned c_encoding() const noexcept { return value; }
};
#endif
// A rudimentary weekday formatter.
template <> struct formatter<weekday> {
private:
bool localized = false;
public:
FMT_CONSTEXPR auto parse(format_parse_context& ctx) -> decltype(ctx.begin()) {
auto begin = ctx.begin(), end = ctx.end();
if (begin != end && *begin == 'L') {
++begin;
localized = true;
}
return begin;
}
auto format(weekday wd, format_context& ctx) -> decltype(ctx.out()) {
auto time = std::tm();
time.tm_wday = static_cast<int>(wd.c_encoding());
const auto& loc = localized ? ctx.locale().template get<std::locale>()
: std::locale::classic();
return detail::write(ctx.out(), time, loc, 'a');
}
};
template <typename Rep, typename Period, typename Char>
struct formatter<std::chrono::duration<Rep, Period>, Char> {
@@ -1082,6 +1193,7 @@ struct formatter<std::chrono::duration<Rep, Period>, Char> {
using arg_ref_type = detail::arg_ref<Char>;
arg_ref_type width_ref;
arg_ref_type precision_ref;
bool localized = false;
basic_string_view<Char> format_str;
using duration = std::chrono::duration<Rep, Period>;
@@ -1144,6 +1256,10 @@ struct formatter<std::chrono::duration<Rep, Period>, Char> {
else
handler.on_error("precision not allowed for this argument type");
}
if (begin != end && *begin == 'L') {
++begin;
localized = true;
}
end = parse_chrono_format(begin, end, detail::chrono_format_checker());
return {begin, end};
}
@@ -1178,13 +1294,15 @@ struct formatter<std::chrono::duration<Rep, Period>, Char> {
detail::chrono_formatter<FormatContext, decltype(out), Rep, Period> f(
ctx, out, d);
f.precision = precision_copy;
parse_chrono_format(begin, end, f);
f.localized = localized;
detail::parse_chrono_format(begin, end, f);
}
return detail::write(
ctx.out(), basic_string_view<Char>(buf.data(), buf.size()), specs_copy);
}
};
FMT_MODULE_EXPORT_END
FMT_END_NAMESPACE
#endif // FMT_CHRONO_H_
+19 -16
View File
@@ -18,6 +18,7 @@
#endif
FMT_BEGIN_NAMESPACE
FMT_MODULE_EXPORT_BEGIN
enum class color : uint32_t {
alice_blue = 0xF0F8FF, // rgb(240,248,255)
@@ -205,7 +206,7 @@ struct rgb {
uint8_t b;
};
namespace detail {
FMT_BEGIN_DETAIL_NAMESPACE
// color is a struct of either a rgb color or a terminal color.
struct color_type {
@@ -228,7 +229,8 @@ struct color_type {
uint32_t rgb_color;
} value;
};
} // namespace detail
FMT_END_DETAIL_NAMESPACE
/** A text style consisting of foreground and background colors and emphasis. */
class text_style {
@@ -367,7 +369,7 @@ FMT_CONSTEXPR inline text_style operator|(emphasis lhs,
return text_style(lhs) | rhs;
}
namespace detail {
FMT_BEGIN_DETAIL_NAMESPACE
template <typename Char> struct ansi_color_escape {
FMT_CONSTEXPR ansi_color_escape(detail::color_type text_color,
@@ -375,7 +377,7 @@ template <typename Char> struct ansi_color_escape {
// If we have a terminal color, we need to output another escape code
// sequence.
if (!text_color.is_rgb) {
bool is_background = esc == detail::data::background_color;
bool is_background = esc == string_view("\x1b[48;2;");
uint32_t value = text_color.value.term_color;
// Background ASCII codes are the same as the foreground ones but with
// 10 more.
@@ -428,7 +430,7 @@ template <typename Char> struct ansi_color_escape {
FMT_CONSTEXPR operator const Char*() const FMT_NOEXCEPT { return buffer; }
FMT_CONSTEXPR const Char* begin() const FMT_NOEXCEPT { return buffer; }
FMT_CONSTEXPR const Char* end() const FMT_NOEXCEPT {
FMT_CONSTEXPR_CHAR_TRAITS const Char* end() const FMT_NOEXCEPT {
return buffer + std::char_traits<Char>::length(buffer);
}
@@ -447,13 +449,13 @@ template <typename Char> struct ansi_color_escape {
template <typename Char>
FMT_CONSTEXPR ansi_color_escape<Char> make_foreground_color(
detail::color_type foreground) FMT_NOEXCEPT {
return ansi_color_escape<Char>(foreground, detail::data::foreground_color);
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 {
return ansi_color_escape<Char>(background, detail::data::background_color);
return ansi_color_escape<Char>(background, "\x1b[48;2;");
}
template <typename Char>
@@ -472,18 +474,17 @@ inline void fputs<wchar_t>(const wchar_t* chars, FILE* stream) FMT_NOEXCEPT {
}
template <typename Char> inline void reset_color(FILE* stream) FMT_NOEXCEPT {
fputs(detail::data::reset_color, stream);
fputs("\x1b[0m", stream);
}
template <> inline void reset_color<wchar_t>(FILE* stream) FMT_NOEXCEPT {
fputs(detail::data::wreset_color, stream);
fputs(L"\x1b[0m", stream);
}
template <typename Char>
inline void reset_color(buffer<Char>& buffer) FMT_NOEXCEPT {
const char* begin = data::reset_color;
const char* end = begin + sizeof(data::reset_color) - 1;
buffer.append(begin, end);
auto reset_color = string_view("\x1b[0m");
buffer.append(reset_color.begin(), reset_color.end());
}
template <typename Char>
@@ -509,7 +510,8 @@ void vformat_to(buffer<Char>& buf, const text_style& ts,
detail::vformat_to(buf, format_str, args);
if (has_style) detail::reset_color<Char>(buf);
}
} // namespace detail
FMT_END_DETAIL_NAMESPACE
template <typename S, typename Char = char_t<S>>
void vprint(std::FILE* f, const text_style& ts, const S& format,
@@ -580,8 +582,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 vformat(ts, to_string_view(format_str),
fmt::make_args_checked<Args...>(format_str, args...));
return fmt::vformat(ts, to_string_view(format_str),
fmt::make_args_checked<Args...>(format_str, args...));
}
/**
@@ -592,7 +594,7 @@ template <typename OutputIt, typename Char,
OutputIt vformat_to(
OutputIt out, const text_style& ts, basic_string_view<Char> format_str,
basic_format_args<buffer_context<type_identity_t<Char>>> args) {
decltype(detail::get_buffer<Char>(out)) buf(detail::get_buffer_init(out));
auto&& buf = detail::get_buffer<Char>(out);
detail::vformat_to(buf, ts, format_str, args);
return detail::get_iterator(buf);
}
@@ -619,6 +621,7 @@ inline auto format_to(OutputIt out, const text_style& ts, const S& format_str,
fmt::make_args_checked<Args...>(format_str, args...));
}
FMT_MODULE_EXPORT_END
FMT_END_NAMESPACE
#endif // FMT_COLOR_H_
+176 -495
View File
@@ -8,23 +8,56 @@
#ifndef FMT_COMPILE_H_
#define FMT_COMPILE_H_
#include <algorithm>
#include <vector>
#include "format.h"
#ifndef FMT_USE_NONTYPE_TEMPLATE_PARAMETERS
# if defined(__cpp_nontype_template_parameter_class) && \
(!FMT_GCC_VERSION || FMT_GCC_VERSION >= 903)
# define FMT_USE_NONTYPE_TEMPLATE_PARAMETERS 1
# else
# define FMT_USE_NONTYPE_TEMPLATE_PARAMETERS 0
# endif
#endif
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) {
return it + (end - begin);
}
template <typename OutputIt> class truncating_iterator_base {
protected:
OutputIt out_;
@@ -123,18 +156,16 @@ struct is_compiled_string : std::is_base_of<compiled_string, S> {};
std::string s = fmt::format(FMT_COMPILE("{}"), 42);
\endrst
*/
#define FMT_COMPILE(s) FMT_STRING_IMPL(s, fmt::detail::compiled_string)
#ifdef __cpp_if_constexpr
# define FMT_COMPILE(s) \
FMT_STRING_IMPL(s, fmt::detail::compiled_string, explicit)
#else
# define FMT_COMPILE(s) FMT_STRING(s)
#endif
#if FMT_USE_NONTYPE_TEMPLATE_PARAMETERS
template <typename Char, size_t N> struct fixed_string {
constexpr fixed_string(const Char (&str)[N]) {
copy_str<Char, const Char*, Char*>(static_cast<const Char*>(str), str + N,
data);
}
Char data[N]{};
};
template <typename Char, size_t N, fixed_string<Char, N> Str>
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 {
@@ -148,333 +179,6 @@ const T& first(const T& value, const Tail&...) {
return value;
}
// Part of a compiled format string. It can be either literal text or a
// replacement field.
template <typename Char> struct format_part {
enum class kind { arg_index, arg_name, text, replacement };
struct replacement {
arg_ref<Char> arg_id;
dynamic_format_specs<Char> specs;
};
kind part_kind;
union value {
int arg_index;
basic_string_view<Char> str;
replacement repl;
FMT_CONSTEXPR value(int index = 0) : arg_index(index) {}
FMT_CONSTEXPR value(basic_string_view<Char> s) : str(s) {}
FMT_CONSTEXPR value(replacement r) : repl(r) {}
} val;
// Position past the end of the argument id.
const Char* arg_id_end = nullptr;
FMT_CONSTEXPR format_part(kind k = kind::arg_index, value v = {})
: part_kind(k), val(v) {}
static FMT_CONSTEXPR format_part make_arg_index(int index) {
return format_part(kind::arg_index, index);
}
static FMT_CONSTEXPR format_part make_arg_name(basic_string_view<Char> name) {
return format_part(kind::arg_name, name);
}
static FMT_CONSTEXPR format_part make_text(basic_string_view<Char> text) {
return format_part(kind::text, text);
}
static FMT_CONSTEXPR format_part make_replacement(replacement repl) {
return format_part(kind::replacement, repl);
}
};
template <typename Char> struct part_counter {
unsigned num_parts = 0;
FMT_CONSTEXPR void on_text(const Char* begin, const Char* end) {
if (begin != end) ++num_parts;
}
FMT_CONSTEXPR int on_arg_id() { return ++num_parts, 0; }
FMT_CONSTEXPR int on_arg_id(int) { return ++num_parts, 0; }
FMT_CONSTEXPR int on_arg_id(basic_string_view<Char>) {
return ++num_parts, 0;
}
FMT_CONSTEXPR void on_replacement_field(int, const Char*) {}
FMT_CONSTEXPR const Char* on_format_specs(int, const Char* begin,
const Char* end) {
// Find the matching brace.
unsigned brace_counter = 0;
for (; begin != end; ++begin) {
if (*begin == '{') {
++brace_counter;
} else if (*begin == '}') {
if (brace_counter == 0u) break;
--brace_counter;
}
}
return begin;
}
FMT_CONSTEXPR void on_error(const char*) {}
};
// Counts the number of parts in a format string.
template <typename Char>
FMT_CONSTEXPR unsigned count_parts(basic_string_view<Char> format_str) {
part_counter<Char> counter;
parse_format_string<true>(format_str, counter);
return counter.num_parts;
}
template <typename Char, typename PartHandler>
class format_string_compiler : public error_handler {
private:
using part = format_part<Char>;
PartHandler handler_;
part part_;
basic_string_view<Char> format_str_;
basic_format_parse_context<Char> parse_context_;
public:
FMT_CONSTEXPR format_string_compiler(basic_string_view<Char> format_str,
PartHandler handler)
: handler_(handler),
format_str_(format_str),
parse_context_(format_str) {}
FMT_CONSTEXPR void on_text(const Char* begin, const Char* end) {
if (begin != end)
handler_(part::make_text({begin, to_unsigned(end - begin)}));
}
FMT_CONSTEXPR int on_arg_id() {
part_ = part::make_arg_index(parse_context_.next_arg_id());
return 0;
}
FMT_CONSTEXPR int on_arg_id(int id) {
parse_context_.check_arg_id(id);
part_ = part::make_arg_index(id);
return 0;
}
FMT_CONSTEXPR int on_arg_id(basic_string_view<Char> id) {
part_ = part::make_arg_name(id);
return 0;
}
FMT_CONSTEXPR void on_replacement_field(int, const Char* ptr) {
part_.arg_id_end = ptr;
handler_(part_);
}
FMT_CONSTEXPR const Char* on_format_specs(int, const Char* begin,
const Char* end) {
auto repl = typename part::replacement();
dynamic_specs_handler<basic_format_parse_context<Char>> handler(
repl.specs, parse_context_);
auto it = parse_format_specs(begin, end, handler);
if (*it != '}') on_error("missing '}' in format string");
repl.arg_id = part_.part_kind == part::kind::arg_index
? arg_ref<Char>(part_.val.arg_index)
: arg_ref<Char>(part_.val.str);
auto replacement_part = part::make_replacement(repl);
replacement_part.arg_id_end = begin;
handler_(replacement_part);
return it;
}
};
// Compiles a format string and invokes handler(part) for each parsed part.
template <bool IS_CONSTEXPR, typename Char, typename PartHandler>
FMT_CONSTEXPR void compile_format_string(basic_string_view<Char> format_str,
PartHandler handler) {
parse_format_string<IS_CONSTEXPR>(
format_str,
format_string_compiler<Char, PartHandler>(format_str, handler));
}
template <typename OutputIt, typename Context, typename Id>
void format_arg(
basic_format_parse_context<typename Context::char_type>& parse_ctx,
Context& ctx, Id arg_id) {
auto arg = ctx.arg(arg_id);
if (arg.type() == type::custom_type) {
visit_format_arg(custom_formatter<Context>(parse_ctx, ctx), arg);
} else {
ctx.advance_to(visit_format_arg(
default_arg_formatter<OutputIt, typename Context::char_type>{
ctx.out(), ctx.args(), ctx.locale()},
arg));
}
}
// vformat_to is defined in a subnamespace to prevent ADL.
namespace cf {
template <typename Context, typename OutputIt, typename CompiledFormat>
auto vformat_to(OutputIt out, CompiledFormat& cf,
basic_format_args<Context> args) -> typename Context::iterator {
using char_type = typename Context::char_type;
basic_format_parse_context<char_type> parse_ctx(
to_string_view(cf.format_str_));
Context ctx(out, args);
const auto& parts = cf.parts();
for (auto part_it = std::begin(parts); part_it != std::end(parts);
++part_it) {
const auto& part = *part_it;
const auto& value = part.val;
using format_part_t = format_part<char_type>;
switch (part.part_kind) {
case format_part_t::kind::text: {
const auto text = value.str;
auto output = ctx.out();
auto&& it = reserve(output, text.size());
it = std::copy_n(text.begin(), text.size(), it);
ctx.advance_to(output);
break;
}
case format_part_t::kind::arg_index:
advance_to(parse_ctx, part.arg_id_end);
detail::format_arg<OutputIt>(parse_ctx, ctx, value.arg_index);
break;
case format_part_t::kind::arg_name:
advance_to(parse_ctx, part.arg_id_end);
detail::format_arg<OutputIt>(parse_ctx, ctx, value.str);
break;
case format_part_t::kind::replacement: {
const auto& arg_id_value = value.repl.arg_id.val;
const auto arg = value.repl.arg_id.kind == arg_id_kind::index
? ctx.arg(arg_id_value.index)
: ctx.arg(arg_id_value.name);
auto specs = value.repl.specs;
handle_dynamic_spec<width_checker>(specs.width, specs.width_ref, ctx);
handle_dynamic_spec<precision_checker>(specs.precision,
specs.precision_ref, ctx);
error_handler h;
numeric_specs_checker<error_handler> checker(h, arg.type());
if (specs.align == align::numeric) checker.require_numeric_argument();
if (specs.sign != sign::none) checker.check_sign();
if (specs.alt) checker.require_numeric_argument();
if (specs.precision >= 0) checker.check_precision();
advance_to(parse_ctx, part.arg_id_end);
ctx.advance_to(visit_format_arg(
arg_formatter<OutputIt, typename Context::char_type>(ctx, specs),
arg));
break;
}
}
}
return ctx.out();
}
} // namespace cf
struct basic_compiled_format {};
template <typename S, typename = void>
struct compiled_format_base : basic_compiled_format {
using char_type = char_t<S>;
using parts_container = std::vector<detail::format_part<char_type>>;
parts_container compiled_parts;
explicit compiled_format_base(basic_string_view<char_type> format_str) {
compile_format_string<false>(format_str,
[this](const format_part<char_type>& part) {
compiled_parts.push_back(part);
});
}
const parts_container& parts() const { return compiled_parts; }
};
template <typename Char, unsigned N> struct format_part_array {
format_part<Char> data[N] = {};
FMT_CONSTEXPR format_part_array() = default;
};
template <typename Char, unsigned N>
FMT_CONSTEXPR format_part_array<Char, N> compile_to_parts(
basic_string_view<Char> format_str) {
format_part_array<Char, N> parts;
unsigned counter = 0;
// This is not a lambda for compatibility with older compilers.
struct {
format_part<Char>* parts;
unsigned* counter;
FMT_CONSTEXPR void operator()(const format_part<Char>& part) {
parts[(*counter)++] = part;
}
} collector{parts.data, &counter};
compile_format_string<true>(format_str, collector);
if (counter < N) {
parts.data[counter] =
format_part<Char>::make_text(basic_string_view<Char>());
}
return parts;
}
template <typename T> constexpr const T& constexpr_max(const T& a, const T& b) {
return (a < b) ? b : a;
}
template <typename S>
struct compiled_format_base<S, enable_if_t<is_compile_string<S>::value>>
: basic_compiled_format {
using char_type = char_t<S>;
FMT_CONSTEXPR explicit compiled_format_base(basic_string_view<char_type>) {}
// Workaround for old compilers. Format string compilation will not be
// performed there anyway.
#if FMT_USE_CONSTEXPR
static FMT_CONSTEXPR_DECL const unsigned num_format_parts =
constexpr_max(count_parts(to_string_view(S())), 1u);
#else
static const unsigned num_format_parts = 1;
#endif
using parts_container = format_part<char_type>[num_format_parts];
const parts_container& parts() const {
static FMT_CONSTEXPR_DECL const auto compiled_parts =
compile_to_parts<char_type, num_format_parts>(
detail::to_string_view(S()));
return compiled_parts.data;
}
};
template <typename S, typename... Args>
class compiled_format : private compiled_format_base<S> {
public:
using typename compiled_format_base<S>::char_type;
private:
basic_string_view<char_type> format_str_;
template <typename Context, typename OutputIt, typename CompiledFormat>
friend auto cf::vformat_to(OutputIt out, CompiledFormat& cf,
basic_format_args<Context> args) ->
typename Context::iterator;
public:
compiled_format() = delete;
explicit constexpr compiled_format(basic_string_view<char_type> format_str)
: compiled_format_base<S>(format_str), format_str_(format_str) {}
};
#ifdef __cpp_if_constexpr
template <typename... Args> struct type_list {};
@@ -489,6 +193,12 @@ constexpr const auto& get([[maybe_unused]] const T& first,
return get<N - 1>(rest...);
}
template <typename Char, typename... Args>
constexpr int get_arg_index_by_name(basic_string_view<Char> name,
type_list<Args...>) {
return get_arg_index_by_name<Args...>(name);
}
template <int N, typename> struct get_type_impl;
template <int N, typename... Args> struct get_type_impl<N, type_list<Args...>> {
@@ -529,6 +239,17 @@ template <typename Char> struct code_unit {
}
};
// This ensures that the argument type is convertible to `const T&`.
template <typename T, int N, typename... Args>
constexpr const T& get_arg_checked(const Args&... args) {
const auto& arg = get<N>(args...);
if constexpr (detail::is_named_arg<remove_cvref_t<decltype(arg)>>()) {
return arg.value;
} else {
return arg;
}
}
template <typename Char>
struct is_compiled_format<code_unit<Char>> : std::true_type {};
@@ -538,14 +259,7 @@ template <typename Char, typename T, int N> struct field {
template <typename OutputIt, typename... Args>
constexpr OutputIt format(OutputIt out, const Args&... args) const {
if constexpr (is_named_arg<typename std::remove_cv<T>::type>::value) {
const auto& arg = get<N>(args...).value;
return write<Char>(out, arg);
} else {
// This ensures that the argument type is convertile to `const T&`.
const T& arg = get<N>(args...);
return write<Char>(out, arg);
}
return write<Char>(out, get_arg_checked<T, N>(args...));
}
};
@@ -590,13 +304,12 @@ template <typename Char, typename T, int N> struct spec_field {
formatter<T, Char> fmt;
template <typename OutputIt, typename... Args>
constexpr OutputIt format(OutputIt out, const Args&... args) const {
// This ensures that the argument type is convertile to `const T&`.
const T& arg = get<N>(args...);
constexpr FMT_INLINE OutputIt format(OutputIt out,
const Args&... args) const {
const auto& vargs =
make_format_args<basic_format_context<OutputIt, Char>>(args...);
fmt::make_format_args<basic_format_context<OutputIt, Char>>(args...);
basic_format_context<OutputIt, Char> ctx(out, vargs);
return fmt.format(arg, ctx);
return fmt.format(get_arg_checked<T, N>(args...), ctx);
}
};
@@ -671,24 +384,22 @@ constexpr parse_specs_result<T, Char> parse_specs(basic_string_view<Char> str,
}
template <typename Char> struct arg_id_handler {
constexpr void on_error(const char* message) { throw format_error(message); }
arg_ref<Char> arg_id;
constexpr int on_arg_id() {
constexpr int operator()() {
FMT_ASSERT(false, "handler cannot be used with automatic indexing");
return 0;
}
constexpr int on_arg_id(int id) {
constexpr int operator()(int id) {
arg_id = arg_ref<Char>(id);
return 0;
}
constexpr int operator()(basic_string_view<Char> id) {
arg_id = arg_ref<Char>(id);
return 0;
}
constexpr int on_arg_id(basic_string_view<Char> id) {
arg_id = arg_ref<Char>(id);
return 0;
}
arg_ref<Char> arg_id;
constexpr void on_error(const char* message) { throw format_error(message); }
};
template <typename Char> struct parse_arg_id_result {
@@ -699,17 +410,45 @@ template <typename Char> struct parse_arg_id_result {
template <int ID, typename Char>
constexpr auto parse_arg_id(const Char* begin, const Char* end) {
auto handler = arg_id_handler<Char>{arg_ref<Char>{}};
auto adapter = id_adapter<arg_id_handler<Char>, Char>{handler, 0};
auto arg_id_end = parse_arg_id(begin, end, adapter);
auto arg_id_end = parse_arg_id(begin, end, handler);
return parse_arg_id_result<Char>{handler.arg_id, arg_id_end};
}
template <typename T, typename Enable = void> struct field_type {
using type = remove_cvref_t<T>;
};
template <typename T>
struct field_type<T, enable_if_t<detail::is_named_arg<T>::value>> {
using type = remove_cvref_t<decltype(T::value)>;
};
template <typename T, typename Args, size_t END_POS, int ARG_INDEX, int NEXT_ID,
typename S>
constexpr auto parse_replacement_field_then_tail(S format_str) {
using char_type = typename S::char_type;
constexpr auto str = basic_string_view<char_type>(format_str);
constexpr char_type c = END_POS != str.size() ? str[END_POS] : char_type();
if constexpr (c == '}') {
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 == ':') {
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);
}
}
// Compiles a non-empty format string and returns the compiled representation
// or unknown_format() on unrecognized input.
template <typename Args, size_t POS, int ID, typename S>
constexpr auto compile_format_string(S format_str) {
using char_type = typename S::char_type;
constexpr basic_string_view<char_type> str = format_str;
constexpr auto str = basic_string_view<char_type>(format_str);
if constexpr (str[POS] == '{') {
if constexpr (POS + 1 == str.size())
throw format_error("unmatched '{' in format string");
@@ -718,17 +457,11 @@ constexpr auto compile_format_string(S format_str) {
} else if constexpr (str[POS + 1] == '}' || str[POS + 1] == ':') {
static_assert(ID != manual_indexing_id,
"cannot switch from manual to automatic argument indexing");
using id_type = get_type<ID, Args>;
if constexpr (str[POS + 1] == '}') {
constexpr auto next_id =
ID != manual_indexing_id ? ID + 1 : manual_indexing_id;
return parse_tail<Args, POS + 2, next_id>(
field<char_type, id_type, ID>(), format_str);
} else {
constexpr auto result = parse_specs<id_type>(str, POS + 2, ID + 1);
return parse_tail<Args, result.end, result.next_arg_id>(
spec_field<char_type, id_type, ID>{result.fmt}, format_str);
}
constexpr auto next_id =
ID != manual_indexing_id ? ID + 1 : manual_indexing_id;
return parse_replacement_field_then_tail<get_type<ID, Args>, Args,
POS + 1, ID, next_id>(
format_str);
} else {
constexpr auto arg_id_result =
parse_arg_id<ID>(str.data() + POS + 1, str.data() + str.size());
@@ -741,24 +474,27 @@ constexpr auto compile_format_string(S format_str) {
ID == manual_indexing_id || ID == 0,
"cannot switch from automatic to manual argument indexing");
constexpr auto arg_index = arg_id_result.arg_id.val.index;
using id_type = get_type<arg_index, Args>;
if constexpr (c == '}') {
return parse_tail<Args, arg_id_end_pos + 1, manual_indexing_id>(
field<char_type, id_type, arg_index>(), format_str);
} else if constexpr (c == ':') {
constexpr auto result =
parse_specs<id_type>(str, arg_id_end_pos + 1, 0);
return parse_tail<Args, result.end, result.next_arg_id>(
spec_field<char_type, id_type, arg_index>{result.fmt},
format_str);
}
return parse_replacement_field_then_tail<get_type<arg_index, Args>,
Args, arg_id_end_pos,
arg_index, manual_indexing_id>(
format_str);
} else if constexpr (arg_id_result.arg_id.kind == arg_id_kind::name) {
if constexpr (c == '}') {
return parse_tail<Args, arg_id_end_pos + 1, ID>(
runtime_named_field<char_type>{arg_id_result.arg_id.val.name},
format_str);
} else if constexpr (c == ':') {
return unknown_format(); // no type info for specs parsing
constexpr auto arg_index =
get_arg_index_by_name(arg_id_result.arg_id.val.name, Args{});
if constexpr (arg_index != invalid_arg_index) {
constexpr auto next_id =
ID != manual_indexing_id ? ID + 1 : manual_indexing_id;
return parse_replacement_field_then_tail<
decltype(get_type<arg_index, Args>::value), Args, arg_id_end_pos,
arg_index, next_id>(format_str);
} else {
if constexpr (c == '}') {
return parse_tail<Args, arg_id_end_pos + 1, ID>(
runtime_named_field<char_type>{arg_id_result.arg_id.val.name},
format_str);
} else if constexpr (c == ':') {
return unknown_format(); // no type info for specs parsing
}
}
}
}
@@ -779,10 +515,9 @@ constexpr auto compile_format_string(S format_str) {
}
template <typename... Args, typename S,
FMT_ENABLE_IF(is_compile_string<S>::value ||
detail::is_compiled_string<S>::value)>
FMT_ENABLE_IF(detail::is_compiled_string<S>::value)>
constexpr auto compile(S format_str) {
constexpr basic_string_view<typename S::char_type> str = format_str;
constexpr auto str = basic_string_view<typename S::char_type>(format_str);
if constexpr (str.size() == 0) {
return detail::make_text(str, 0, 0);
} else {
@@ -792,71 +527,36 @@ constexpr auto compile(S format_str) {
return result;
}
}
#else
template <typename... Args, typename S,
FMT_ENABLE_IF(is_compile_string<S>::value)>
constexpr auto compile(S format_str) -> detail::compiled_format<S, Args...> {
return detail::compiled_format<S, Args...>(to_string_view(format_str));
}
#endif // __cpp_if_constexpr
// Compiles the format string which must be a string literal.
template <typename... Args, typename Char, size_t N>
auto compile(const Char (&format_str)[N])
-> detail::compiled_format<const Char*, Args...> {
return detail::compiled_format<const Char*, Args...>(
basic_string_view<Char>(format_str, N - 1));
}
} // namespace detail
// DEPRECATED! use FMT_COMPILE instead.
template <typename... Args>
FMT_DEPRECATED auto compile(const Args&... args)
-> decltype(detail::compile(args...)) {
return detail::compile(args...);
}
FMT_MODULE_EXPORT_BEGIN
#if FMT_USE_CONSTEXPR
# ifdef __cpp_if_constexpr
#ifdef __cpp_if_constexpr
template <typename CompiledFormat, typename... Args,
typename Char = typename CompiledFormat::char_type,
FMT_ENABLE_IF(detail::is_compiled_format<CompiledFormat>::value)>
FMT_INLINE std::basic_string<Char> format(const CompiledFormat& cf,
const Args&... args) {
basic_memory_buffer<Char> buffer;
cf.format(detail::buffer_appender<Char>(buffer), args...);
return to_string(buffer);
auto s = std::basic_string<Char>();
cf.format(std::back_inserter(s), args...);
return s;
}
template <typename OutputIt, typename CompiledFormat, typename... Args,
FMT_ENABLE_IF(detail::is_compiled_format<CompiledFormat>::value)>
constexpr OutputIt format_to(OutputIt out, const CompiledFormat& cf,
const Args&... args) {
constexpr FMT_INLINE OutputIt format_to(OutputIt out, const CompiledFormat& cf,
const Args&... args) {
return cf.format(out, args...);
}
# endif // __cpp_if_constexpr
#endif // FMT_USE_CONSTEXPR
template <typename CompiledFormat, typename... Args,
typename Char = typename CompiledFormat::char_type,
FMT_ENABLE_IF(std::is_base_of<detail::basic_compiled_format,
CompiledFormat>::value)>
std::basic_string<Char> format(const CompiledFormat& cf, const Args&... args) {
basic_memory_buffer<Char> buffer;
using context = buffer_context<Char>;
detail::cf::vformat_to<context>(detail::buffer_appender<Char>(buffer), cf,
make_format_args<context>(args...));
return to_string(buffer);
}
template <typename S, typename... Args,
FMT_ENABLE_IF(detail::is_compiled_string<S>::value)>
FMT_INLINE std::basic_string<typename S::char_type> format(const S&,
Args&&... args) {
#ifdef __cpp_if_constexpr
if constexpr (std::is_same<typename S::char_type, char>::value) {
constexpr basic_string_view<typename S::char_type> str = S();
constexpr auto str = basic_string_view<typename S::char_type>(S());
if constexpr (str.size() == 2 && str[0] == '{' && str[1] == '}') {
const auto& first = detail::first(args...);
if constexpr (detail::is_named_arg<
@@ -867,9 +567,7 @@ FMT_INLINE std::basic_string<typename S::char_type> format(const S&,
}
}
}
#endif
constexpr auto compiled = detail::compile<Args...>(S());
#ifdef __cpp_if_constexpr
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()),
@@ -877,27 +575,12 @@ FMT_INLINE std::basic_string<typename S::char_type> format(const S&,
} else {
return format(compiled, std::forward<Args>(args)...);
}
#else
return format(compiled, std::forward<Args>(args)...);
#endif
}
template <typename OutputIt, typename CompiledFormat, typename... Args,
FMT_ENABLE_IF(std::is_base_of<detail::basic_compiled_format,
CompiledFormat>::value)>
constexpr OutputIt format_to(OutputIt out, const CompiledFormat& cf,
const Args&... args) {
using char_type = typename CompiledFormat::char_type;
using context = format_context_t<OutputIt, char_type>;
return detail::cf::vformat_to<context>(out, cf,
make_format_args<context>(args...));
}
template <typename OutputIt, typename S, typename... Args,
FMT_ENABLE_IF(detail::is_compiled_string<S>::value)>
FMT_CONSTEXPR OutputIt format_to(OutputIt out, const S&, Args&&... args) {
constexpr auto compiled = detail::compile<Args...>(S());
#ifdef __cpp_if_constexpr
if constexpr (std::is_same<remove_cvref_t<decltype(compiled)>,
detail::unknown_format>()) {
return format_to(out,
@@ -906,53 +589,51 @@ FMT_CONSTEXPR OutputIt format_to(OutputIt out, const S&, Args&&... args) {
} else {
return format_to(out, compiled, std::forward<Args>(args)...);
}
#else
return format_to(out, compiled, std::forward<Args>(args)...);
}
#endif
}
template <typename OutputIt, typename CompiledFormat, typename... Args>
auto format_to_n(OutputIt out, size_t n, const CompiledFormat& cf,
const Args&... args) ->
typename std::enable_if<
detail::is_output_iterator<OutputIt,
typename CompiledFormat::char_type>::value &&
std::is_base_of<detail::basic_compiled_format,
CompiledFormat>::value,
format_to_n_result<OutputIt>>::type {
auto it =
format_to(detail::truncating_iterator<OutputIt>(out, n), cf, args...);
return {it.base(), it.count()};
}
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&,
Args&&... args) {
auto it = format_to(detail::truncating_iterator<OutputIt>(out, n), S(),
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)...);
return {it.base(), it.count()};
}
template <typename CompiledFormat, typename... Args,
FMT_ENABLE_IF(std::is_base_of<detail::basic_compiled_format,
CompiledFormat>::value ||
detail::is_compiled_string<CompiledFormat>::value)>
size_t formatted_size(const CompiledFormat& cf, const Args&... args) {
return format_to(detail::counting_iterator(), cf, args...).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();
}
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...);
detail::print(f, {buffer.data(), buffer.size()});
}
template <typename S, typename... Args,
FMT_ENABLE_IF(detail::is_compiled_string<S>::value)>
void print(const S& format_str, const Args&... args) {
print(stdout, format_str, args...);
}
#if FMT_USE_NONTYPE_TEMPLATE_PARAMETERS
inline namespace literals {
template <detail::fixed_string Str>
constexpr detail::udl_compiled_string<remove_cvref_t<decltype(Str.data[0])>,
sizeof(Str.data), Str>
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 {};
}
} // namespace literals
#endif
FMT_MODULE_EXPORT_END
FMT_END_NAMESPACE
#endif // FMT_COMPILE_H_
+1468 -425
View File
File diff suppressed because it is too large Load Diff
+896 -1006
View File
File diff suppressed because it is too large Load Diff
+901 -2108
View File
File diff suppressed because it is too large Load Diff
+2 -64
View File
@@ -1,64 +1,2 @@
// Formatting library for C++ - std::locale support
//
// Copyright (c) 2012 - present, Victor Zverovich
// All rights reserved.
//
// For the license information refer to format.h.
#ifndef FMT_LOCALE_H_
#define FMT_LOCALE_H_
#include <locale>
#include "format.h"
FMT_BEGIN_NAMESPACE
namespace detail {
template <typename Char>
std::basic_string<Char> vformat(
const std::locale& loc, basic_string_view<Char> format_str,
basic_format_args<buffer_context<type_identity_t<Char>>> args) {
basic_memory_buffer<Char> buffer;
detail::vformat_to(buffer, format_str, args, detail::locale_ref(loc));
return fmt::to_string(buffer);
}
} // namespace detail
template <typename S, typename Char = char_t<S>>
inline std::basic_string<Char> vformat(
const std::locale& loc, const S& format_str,
basic_format_args<buffer_context<type_identity_t<Char>>> args) {
return detail::vformat(loc, to_string_view(format_str), args);
}
template <typename S, typename... Args, typename Char = char_t<S>>
inline std::basic_string<Char> format(const std::locale& loc,
const S& format_str, Args&&... args) {
return detail::vformat(loc, to_string_view(format_str),
fmt::make_args_checked<Args...>(format_str, args...));
}
template <typename S, typename OutputIt, typename... Args,
typename Char = char_t<S>,
FMT_ENABLE_IF(detail::is_output_iterator<OutputIt, Char>::value)>
inline OutputIt vformat_to(
OutputIt out, const std::locale& loc, const S& format_str,
basic_format_args<buffer_context<type_identity_t<Char>>> args) {
decltype(detail::get_buffer<Char>(out)) buf(detail::get_buffer_init(out));
vformat_to(buf, to_string_view(format_str), args, detail::locale_ref(loc));
return detail::get_iterator(buf);
}
template <typename OutputIt, typename S, typename... Args,
bool enable = detail::is_output_iterator<OutputIt, char_t<S>>::value>
inline auto format_to(OutputIt out, const std::locale& loc, const S& format_str,
Args&&... args) ->
typename std::enable_if<enable, OutputIt>::type {
const auto& vargs = fmt::make_args_checked<Args...>(format_str, args...);
return vformat_to(out, loc, to_string_view(format_str), vargs);
}
FMT_END_NAMESPACE
#endif // FMT_LOCALE_H_
#include "xchar.h"
#warning fmt/locale.h is deprecated, include fmt/format.h or fmt/xchar.h instead
+86 -63
View File
@@ -9,10 +9,11 @@
#define FMT_OS_H_
#include <cerrno>
#include <clocale> // for locale_t
#include <clocale> // locale_t
#include <cstddef>
#include <cstdio>
#include <cstdlib> // for strtod_l
#include <cstdlib> // strtod_l
#include <system_error> // std::system_error
#if defined __APPLE__ || defined(__FreeBSD__)
# include <xlocale.h> // for LC_NUMERIC_MASK on OS X
@@ -69,6 +70,7 @@
#define FMT_RETRY(result, expression) FMT_RETRY_VAL(result, expression, -1)
FMT_BEGIN_NAMESPACE
FMT_MODULE_EXPORT_BEGIN
/**
\rst
@@ -117,19 +119,28 @@ template <typename Char> class basic_cstring_view {
using cstring_view = basic_cstring_view<char>;
using wcstring_view = basic_cstring_view<wchar_t>;
// An error code.
class error_code {
private:
int value_;
template <typename Char> struct formatter<std::error_code, Char> {
template <typename ParseContext>
FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) {
return ctx.begin();
}
public:
explicit error_code(int value = 0) FMT_NOEXCEPT : value_(value) {}
int get() const FMT_NOEXCEPT { return value_; }
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
namespace detail {
FMT_API const std::error_category& system_category() FMT_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 {
@@ -138,7 +149,7 @@ class utf16_to_utf8 {
public:
utf16_to_utf8() {}
FMT_API explicit utf16_to_utf8(wstring_view s);
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]; }
@@ -147,59 +158,68 @@ class utf16_to_utf8 {
// 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(wstring_view s);
FMT_API int convert(basic_string_view<wchar_t> s);
};
FMT_API void format_windows_error(buffer<char>& out, int error_code,
string_view message) FMT_NOEXCEPT;
} // namespace detail
const char* message) FMT_NOEXCEPT;
FMT_END_DETAIL_NAMESPACE
/** A Windows error. */
class windows_error : public system_error {
private:
FMT_API void init(int error_code, string_view format_str, format_args args);
FMT_API std::system_error vwindows_error(int error_code, string_view format_str,
format_args args);
public:
/**
\rst
Constructs a :class:`fmt::windows_error` object with the description
of the form
/**
\rst
Constructs a :class:`std::system_error` object with the description
of the form
.. parsed-literal::
*<message>*: *<system-message>*
.. parsed-literal::
*<message>*: *<system-message>*
where *<message>* is the formatted message and *<system-message>* is the
system message corresponding to the error code.
*error_code* is a Windows error code as given by ``GetLastError``.
If *error_code* is not a valid error code such as -1, the system message
will look like "error -1".
where *<message>* is the formatted message and *<system-message>* is the
system message corresponding to the error code.
*error_code* is a Windows error code as given by ``GetLastError``.
If *error_code* is not a valid error code such as -1, the system message
will look like "error -1".
**Example**::
**Example**::
// This throws a windows_error with the description
// cannot open file 'madeup': The system cannot find the file specified.
// or similar (system message may vary).
const char *filename = "madeup";
LPOFSTRUCT of = LPOFSTRUCT();
HFILE file = OpenFile(filename, &of, OF_READ);
if (file == HFILE_ERROR) {
throw fmt::windows_error(GetLastError(),
"cannot open file '{}'", filename);
}
\endrst
*/
template <typename... Args>
windows_error(int error_code, string_view message, const Args&... args) {
init(error_code, message, make_format_args(args...));
}
};
// This throws a system_error with the description
// cannot open file 'madeup': The system cannot find the file specified.
// or similar (system message may vary).
const char *filename = "madeup";
LPOFSTRUCT of = LPOFSTRUCT();
HFILE file = OpenFile(filename, &of, OF_READ);
if (file == HFILE_ERROR) {
throw fmt::windows_error(GetLastError(),
"cannot open file '{}'", filename);
}
\endrst
*/
template <typename... Args>
std::system_error windows_error(int error_code, string_view message,
const Args&... args) {
return vwindows_error(error_code, message, fmt::make_format_args(args...));
}
// 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,
string_view message) FMT_NOEXCEPT;
const char* message) FMT_NOEXCEPT;
#else
inline const std::error_category& system_category() FMT_NOEXCEPT {
return std::system_category();
}
#endif // _WIN32
// std::system is not available on some platforms such as iOS (#2248).
#ifdef __OSX__
template <typename S, typename... Args, typename Char = char_t<S>>
void say(const S& format_str, Args&&... args) {
std::system(format("say \"{}\"", format(format_str, args...)).c_str());
}
#endif
// A buffered file.
class buffered_file {
private:
@@ -250,7 +270,7 @@ class buffered_file {
template <typename... Args>
inline void print(string_view format_str, const Args&... args) {
vprint(format_str, make_format_args(args...));
vprint(format_str, fmt::make_format_args(args...));
}
};
@@ -291,7 +311,8 @@ class file {
file(file&& other) FMT_NOEXCEPT : fd_(other.fd_) { other.fd_ = -1; }
file& operator=(file&& other) FMT_NOEXCEPT {
// Move assignment is not noexcept because close may throw.
file& operator=(file&& other) {
close();
fd_ = other.fd_;
other.fd_ = -1;
@@ -327,7 +348,7 @@ class file {
// Makes fd be the copy of this file descriptor, closing fd first if
// necessary.
FMT_API void dup2(int fd, error_code& ec) FMT_NOEXCEPT;
FMT_API void dup2(int fd, std::error_code& ec) FMT_NOEXCEPT;
// Creates a pipe setting up read_end and write_end file objects for reading
// and writing respectively.
@@ -341,7 +362,7 @@ class file {
// Returns the memory page size.
long getpagesize();
namespace detail {
FMT_BEGIN_DETAIL_NAMESPACE
struct buffer_size {
buffer_size() = default;
@@ -370,12 +391,13 @@ struct ostream_params {
this->buffer_size = bs.value;
}
};
} // namespace detail
FMT_END_DETAIL_NAMESPACE
static constexpr detail::buffer_size buffer_size;
/** A fast output stream which is not thread-safe. */
class ostream final : private detail::buffer<char> {
class FMT_API ostream final : private detail::buffer<char> {
private:
file file_;
@@ -385,7 +407,7 @@ class ostream final : private detail::buffer<char> {
clear();
}
FMT_API void grow(size_t) override final;
void grow(size_t) override;
ostream(cstring_view path, const detail::ostream_params& params)
: file_(path, params.oflag) {
@@ -396,6 +418,7 @@ class ostream final : private detail::buffer<char> {
ostream(ostream&& other)
: detail::buffer<char>(other.data(), other.size(), other.capacity()),
file_(std::move(other.file_)) {
other.clear();
other.set(nullptr, 0);
}
~ostream() {
@@ -412,13 +435,12 @@ class ostream final : private detail::buffer<char> {
}
/**
Formats ``args`` according to specifications in ``format_str`` and writes
the output to the file.
Formats ``args`` according to specifications in ``fmt`` and writes the
output to the file.
*/
template <typename S, typename... Args>
void print(const S& format_str, Args&&... args) {
format_to(detail::buffer_appender<char>(*this), format_str,
std::forward<Args>(args)...);
template <typename... T> void print(format_string<T...> fmt, T&&... args) {
vformat_to(detail::buffer_appender<char>(*this), fmt,
fmt::make_format_args(args...));
}
};
@@ -487,6 +509,7 @@ class locale {
};
using Locale FMT_DEPRECATED_ALIAS = locale;
#endif // FMT_LOCALE
FMT_MODULE_EXPORT_END
FMT_END_NAMESPACE
#endif // FMT_OS_H_
+2
View File
@@ -151,6 +151,7 @@ struct fallback_formatter<T, Char, enable_if_t<is_streamable<T, Char>::value>>
};
} // namespace detail
FMT_MODULE_EXPORT
template <typename Char>
void vprint(std::basic_ostream<Char>& os, basic_string_view<Char> format_str,
basic_format_args<buffer_context<type_identity_t<Char>>> args) {
@@ -168,6 +169,7 @@ 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 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) {
+159 -229
View File
@@ -10,11 +10,54 @@
#include <algorithm> // std::max
#include <limits> // std::numeric_limits
#include <ostream>
#include "ostream.h"
#include "format.h"
FMT_BEGIN_NAMESPACE
namespace detail {
FMT_MODULE_EXPORT_BEGIN
template <typename T> struct printf_formatter { printf_formatter() = delete; };
template <typename Char>
class basic_printf_parse_context : public basic_format_parse_context<Char> {
using basic_format_parse_context<Char>::basic_format_parse_context;
};
template <typename OutputIt, typename Char> class basic_printf_context {
private:
OutputIt out_;
basic_format_args<basic_printf_context> args_;
public:
using char_type = Char;
using format_arg = basic_format_arg<basic_printf_context>;
using parse_context_type = basic_printf_parse_context<Char>;
template <typename T> using formatter_type = printf_formatter<T>;
/**
\rst
Constructs a ``printf_context`` object. References to the arguments are
stored in the context object so make sure they have appropriate lifetimes.
\endrst
*/
basic_printf_context(OutputIt out,
basic_format_args<basic_printf_context> args)
: out_(out), args_(args) {}
OutputIt out() { return out_; }
void advance_to(OutputIt it) { out_ = it; }
detail::locale_ref locale() { return {}; }
format_arg arg(int id) const { return args_.get(id); }
FMT_CONSTEXPR void on_error(const char* message) {
detail::error_handler().on_error(message);
}
};
FMT_BEGIN_DETAIL_NAMESPACE
// Checks if a value fits in int - used to avoid warnings about comparing
// signed and unsigned integers.
@@ -178,61 +221,34 @@ template <typename Char> class printf_width_handler {
}
};
template <typename Char, typename Context>
void vprintf(buffer<Char>& buf, basic_string_view<Char> format,
basic_format_args<Context> args) {
Context(buffer_appender<Char>(buf), format, args).format();
}
} // namespace detail
// For printing into memory_buffer.
template <typename Char, typename Context>
FMT_DEPRECATED void printf(detail::buffer<Char>& buf,
basic_string_view<Char> format,
basic_format_args<Context> args) {
return detail::vprintf(buf, format, args);
}
using detail::vprintf;
template <typename Char>
class basic_printf_parse_context : public basic_format_parse_context<Char> {
using basic_format_parse_context<Char>::basic_format_parse_context;
};
template <typename OutputIt, typename Char> class basic_printf_context;
/**
\rst
The ``printf`` argument formatter.
\endrst
*/
// The ``printf`` argument formatter.
template <typename OutputIt, typename Char>
class printf_arg_formatter : public detail::arg_formatter_base<OutputIt, Char> {
class printf_arg_formatter : public arg_formatter<Char> {
private:
using base = detail::arg_formatter_base<OutputIt, Char>;
using base = arg_formatter<Char>;
using context_type = basic_printf_context<OutputIt, Char>;
using format_specs = typename base::format_specs;
using format_specs = basic_format_specs<Char>;
context_type& context_;
OutputIt write_null_pointer(bool is_string = false) {
auto s = this->specs();
auto s = this->specs;
s.type = 0;
return detail::write(this->out(),
string_view(is_string ? "(null)" : "(nil)"), s);
return write_bytes(this->out, is_string ? "(null)" : "(nil)", s);
}
public:
printf_arg_formatter(OutputIt iter, format_specs& specs, context_type& ctx)
: base(iter, specs, detail::locale_ref()), context_(ctx) {}
printf_arg_formatter(OutputIt iter, format_specs& s, context_type& ctx)
: base{iter, s, locale_ref()}, context_(ctx) {}
OutputIt operator()(monostate value) { return base::operator()(value); }
template <typename T, FMT_ENABLE_IF(fmt::detail::is_integral<T>::value)>
template <typename T, FMT_ENABLE_IF(detail::is_integral<T>::value)>
OutputIt operator()(T value) {
// 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 fmt_specs = this->specs;
if (fmt_specs.type && fmt_specs.type != 'c')
return (*this)(static_cast<int>(value));
fmt_specs.sign = sign::none;
@@ -242,7 +258,7 @@ class printf_arg_formatter : public detail::arg_formatter_base<OutputIt, Char> {
// ignored for non-numeric types
if (fmt_specs.align == align::none || fmt_specs.align == align::numeric)
fmt_specs.align = align::right;
return write_char(this->out(), static_cast<Char>(value), fmt_specs);
return write<Char>(this->out, static_cast<Char>(value), fmt_specs);
}
return base::operator()(value);
}
@@ -255,13 +271,13 @@ class printf_arg_formatter : public detail::arg_formatter_base<OutputIt, Char> {
/** Formats a null-terminated C string. */
OutputIt operator()(const char* value) {
if (value) return base::operator()(value);
return write_null_pointer(this->specs().type != 'p');
return write_null_pointer(this->specs.type != 'p');
}
/** Formats a null-terminated wide C string. */
OutputIt operator()(const wchar_t* value) {
if (value) return base::operator()(value);
return write_null_pointer(this->specs().type != 'p');
return write_null_pointer(this->specs.type != 'p');
}
OutputIt operator()(basic_string_view<Char> value) {
@@ -275,88 +291,16 @@ class printf_arg_formatter : public detail::arg_formatter_base<OutputIt, Char> {
/** Formats an argument of a custom (user-defined) type. */
OutputIt operator()(typename basic_format_arg<context_type>::handle handle) {
handle.format(context_.parse_context(), context_);
return this->out();
auto parse_ctx =
basic_printf_parse_context<Char>(basic_string_view<Char>());
handle.format(parse_ctx, context_);
return this->out;
}
};
template <typename T> struct printf_formatter {
printf_formatter() = delete;
template <typename ParseContext>
auto parse(ParseContext& ctx) -> decltype(ctx.begin()) {
return ctx.begin();
}
template <typename FormatContext>
auto format(const T& value, FormatContext& ctx) -> decltype(ctx.out()) {
detail::format_value(detail::get_container(ctx.out()), value);
return ctx.out();
}
};
/**
This template formats data and writes the output through an output iterator.
*/
template <typename OutputIt, typename Char> class basic_printf_context {
public:
/** The character type for the output. */
using char_type = Char;
using iterator = OutputIt;
using format_arg = basic_format_arg<basic_printf_context>;
using parse_context_type = basic_printf_parse_context<Char>;
template <typename T> using formatter_type = printf_formatter<T>;
private:
using format_specs = basic_format_specs<char_type>;
OutputIt out_;
basic_format_args<basic_printf_context> args_;
parse_context_type parse_ctx_;
static void parse_flags(format_specs& specs, const Char*& it,
const Char* end);
// Returns the argument with specified index or, if arg_index is -1, the next
// argument.
format_arg get_arg(int arg_index = -1);
// Parses argument index, flags and width and returns the argument index.
int parse_header(const Char*& it, const Char* end, format_specs& specs);
public:
/**
\rst
Constructs a ``printf_context`` object. References to the arguments are
stored in the context object so make sure they have appropriate lifetimes.
\endrst
*/
basic_printf_context(OutputIt out, basic_string_view<char_type> format_str,
basic_format_args<basic_printf_context> args)
: out_(out), args_(args), parse_ctx_(format_str) {}
OutputIt out() { return out_; }
void advance_to(OutputIt it) { out_ = it; }
detail::locale_ref locale() { return {}; }
format_arg arg(int id) const { return args_.get(id); }
parse_context_type& parse_context() { return parse_ctx_; }
FMT_CONSTEXPR void on_error(const char* message) {
parse_ctx_.on_error(message);
}
/** Formats stored arguments and writes the output to the range. */
template <typename ArgFormatter = printf_arg_formatter<OutputIt, Char>>
OutputIt format();
};
template <typename OutputIt, typename Char>
void basic_printf_context<OutputIt, Char>::parse_flags(format_specs& specs,
const Char*& it,
const Char* end) {
template <typename Char>
void parse_flags(basic_format_specs<Char>& specs, const Char*& it,
const Char* end) {
for (; it != end; ++it) {
switch (*it) {
case '-':
@@ -382,35 +326,24 @@ void basic_printf_context<OutputIt, Char>::parse_flags(format_specs& specs,
}
}
template <typename OutputIt, typename Char>
typename basic_printf_context<OutputIt, Char>::format_arg
basic_printf_context<OutputIt, Char>::get_arg(int arg_index) {
if (arg_index < 0)
arg_index = parse_ctx_.next_arg_id();
else
parse_ctx_.check_arg_id(--arg_index);
return detail::get_arg(*this, arg_index);
}
template <typename OutputIt, typename Char>
int basic_printf_context<OutputIt, Char>::parse_header(const Char*& it,
const Char* end,
format_specs& specs) {
template <typename Char, typename GetArg>
int parse_header(const Char*& it, const Char* end,
basic_format_specs<Char>& specs, GetArg get_arg) {
int arg_index = -1;
char_type c = *it;
Char c = *it;
if (c >= '0' && c <= '9') {
// Parse an argument index (if followed by '$') or a width possibly
// preceded with '0' flag(s).
detail::error_handler eh;
int value = parse_nonnegative_int(it, end, eh);
int value = parse_nonnegative_int(it, end, -1);
if (it != end && *it == '$') { // value is an argument index
++it;
arg_index = value;
arg_index = value != -1 ? value : max_value<int>();
} else {
if (c == '0') specs.fill[0] = '0';
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"));
specs.width = value;
return arg_index;
}
@@ -420,30 +353,44 @@ int basic_printf_context<OutputIt, Char>::parse_header(const Char*& it,
// Parse width.
if (it != end) {
if (*it >= '0' && *it <= '9') {
detail::error_handler eh;
specs.width = parse_nonnegative_int(it, end, eh);
specs.width = parse_nonnegative_int(it, end, -1);
if (specs.width == -1) FMT_THROW(format_error("number is too big"));
} else if (*it == '*') {
++it;
specs.width = static_cast<int>(visit_format_arg(
detail::printf_width_handler<char_type>(specs), get_arg()));
detail::printf_width_handler<Char>(specs), get_arg(-1)));
}
}
return arg_index;
}
template <typename OutputIt, typename Char>
template <typename ArgFormatter>
OutputIt basic_printf_context<OutputIt, Char>::format() {
auto out = this->out();
const Char* start = parse_ctx_.begin();
const Char* end = parse_ctx_.end();
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);
auto parse_ctx = basic_printf_parse_context<Char>(format);
// Returns the argument with specified index or, if arg_index is -1, the next
// argument.
auto get_arg = [&](int arg_index) {
if (arg_index < 0)
arg_index = parse_ctx.next_arg_id();
else
parse_ctx.check_arg_id(--arg_index);
return detail::get_arg(context, arg_index);
};
const Char* start = parse_ctx.begin();
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 '%'
break;
}
char_type c = *it++;
Char c = *it++;
if (it != end && *it == c) {
out = detail::write(
out, basic_string_view<Char>(start, detail::to_unsigned(it - start)));
@@ -453,30 +400,29 @@ OutputIt basic_printf_context<OutputIt, Char>::format() {
out = detail::write(out, basic_string_view<Char>(
start, detail::to_unsigned(it - 1 - start)));
format_specs specs;
basic_format_specs<Char> specs;
specs.align = align::right;
// Parse argument index, flags and width.
int arg_index = parse_header(it, end, specs);
if (arg_index == 0) on_error("argument not found");
int arg_index = parse_header(it, end, specs, get_arg);
if (arg_index == 0) parse_ctx.on_error("argument not found");
// Parse precision.
if (it != end && *it == '.') {
++it;
c = it != end ? *it : 0;
if ('0' <= c && c <= '9') {
detail::error_handler eh;
specs.precision = parse_nonnegative_int(it, end, eh);
specs.precision = parse_nonnegative_int(it, end, 0);
} else if (c == '*') {
++it;
specs.precision = static_cast<int>(
visit_format_arg(detail::printf_precision_handler(), get_arg()));
visit_format_arg(detail::printf_precision_handler(), get_arg(-1)));
} else {
specs.precision = 0;
}
}
format_arg arg = get_arg(arg_index);
auto arg = get_arg(arg_index);
// For d, i, o, u, x, and X conversion specifiers, if a precision is
// specified, the '0' flag is ignored
if (specs.precision >= 0 && arg.is_integral())
@@ -486,9 +432,10 @@ OutputIt basic_printf_context<OutputIt, Char>::format() {
auto str = visit_format_arg(detail::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>(basic_string_view<Char>(
str,
detail::to_unsigned(nul != str_end ? nul - str : specs.precision)));
arg = detail::make_arg<basic_printf_context<OutputIt, Char>>(
basic_string_view<Char>(
str, detail::to_unsigned(nul != str_end ? nul - str
: specs.precision)));
}
if (specs.alt && visit_format_arg(detail::is_zero_int(), arg))
specs.alt = false;
@@ -502,7 +449,7 @@ OutputIt basic_printf_context<OutputIt, Char>::format() {
// Parse length and convert the argument to the required type.
c = it != end ? *it++ : 0;
char_type t = it != end ? *it : 0;
Char t = it != end ? *it : 0;
using detail::convert_arg;
switch (c) {
case 'h':
@@ -552,8 +499,9 @@ OutputIt basic_printf_context<OutputIt, Char>::format() {
specs.type = 'd';
break;
case 'c':
visit_format_arg(detail::char_converter<basic_printf_context>(arg),
arg);
visit_format_arg(
detail::char_converter<basic_printf_context<OutputIt, Char>>(arg),
arg);
break;
}
}
@@ -561,11 +509,12 @@ OutputIt basic_printf_context<OutputIt, Char>::format() {
start = it;
// Format argument.
out = visit_format_arg(ArgFormatter(out, specs, *this), arg);
out = visit_format_arg(
detail::printf_arg_formatter<OutputIt, Char>(out, specs, context), arg);
}
return detail::write(
out, basic_string_view<Char>(start, detail::to_unsigned(it - start)));
detail::write(out, basic_string_view<Char>(start, to_unsigned(it - start)));
}
FMT_END_DETAIL_NAMESPACE
template <typename Char>
using basic_printf_context_t =
@@ -583,9 +532,9 @@ using wprintf_args = basic_format_args<wprintf_context>;
arguments and can be implicitly converted to `~fmt::printf_args`.
\endrst
*/
template <typename... Args>
inline format_arg_store<printf_context, Args...> make_printf_args(
const Args&... args) {
template <typename... T>
inline auto make_printf_args(const T&... args)
-> format_arg_store<printf_context, T...> {
return {args...};
}
@@ -595,18 +544,19 @@ inline format_arg_store<printf_context, Args...> make_printf_args(
arguments and can be implicitly converted to `~fmt::wprintf_args`.
\endrst
*/
template <typename... Args>
inline format_arg_store<wprintf_context, Args...> make_wprintf_args(
const Args&... args) {
template <typename... T>
inline auto make_wprintf_args(const T&... args)
-> format_arg_store<wprintf_context, T...> {
return {args...};
}
template <typename S, typename Char = char_t<S>>
inline std::basic_string<Char> vsprintf(
const S& format,
basic_format_args<basic_printf_context_t<type_identity_t<Char>>> args) {
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(format), args);
vprintf(buffer, to_string_view(fmt), args);
return to_string(buffer);
}
@@ -619,19 +569,20 @@ inline std::basic_string<Char> vsprintf(
std::string message = fmt::sprintf("The answer is %d", 42);
\endrst
*/
template <typename S, typename... Args,
template <typename S, typename... T,
typename Char = enable_if_t<detail::is_string<S>::value, char_t<S>>>
inline std::basic_string<Char> sprintf(const S& format, const Args&... args) {
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(format), make_format_args<context>(args...));
return vsprintf(to_string_view(fmt), fmt::make_format_args<context>(args...));
}
template <typename S, typename Char = char_t<S>>
inline int vfprintf(
std::FILE* f, const S& format,
basic_format_args<basic_printf_context_t<type_identity_t<Char>>> args) {
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(format), args);
vprintf(buffer, to_string_view(fmt), args);
size_t size = buffer.size();
return std::fwrite(buffer.data(), sizeof(Char), size, f) < size
? -1
@@ -647,19 +598,19 @@ inline int vfprintf(
fmt::fprintf(stderr, "Don't %s!", "panic");
\endrst
*/
template <typename S, typename... Args,
typename Char = enable_if_t<detail::is_string<S>::value, char_t<S>>>
inline int fprintf(std::FILE* f, const S& format, const Args&... args) {
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(format),
make_format_args<context>(args...));
return vfprintf(f, to_string_view(fmt),
fmt::make_format_args<context>(args...));
}
template <typename S, typename Char = char_t<S>>
inline int vprintf(
const S& format,
basic_format_args<basic_printf_context_t<type_identity_t<Char>>> args) {
return vfprintf(stdout, to_string_view(format), args);
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);
}
/**
@@ -671,52 +622,31 @@ inline int vprintf(
fmt::printf("Elapsed time: %.2f seconds", 1.23);
\endrst
*/
template <typename S, typename... Args,
FMT_ENABLE_IF(detail::is_string<S>::value)>
inline int printf(const S& format_str, const Args&... args) {
using context = basic_printf_context_t<char_t<S>>;
return vprintf(to_string_view(format_str),
make_format_args<context>(args...));
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),
fmt::make_format_args<basic_printf_context_t<char_t<S>>>(args...));
}
template <typename S, typename Char = char_t<S>>
inline int vfprintf(
std::basic_ostream<Char>& os, const S& format,
basic_format_args<basic_printf_context_t<type_identity_t<Char>>> args) {
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(format), args);
detail::write_buffer(os, buffer);
vprintf(buffer, to_string_view(fmt), args);
os.write(buffer.data(), static_cast<std::streamsize>(buffer.size()));
return static_cast<int>(buffer.size());
}
/** Formats arguments and writes the output to the range. */
template <typename ArgFormatter, typename Char,
typename Context =
basic_printf_context<typename ArgFormatter::iterator, Char>>
typename ArgFormatter::iterator vprintf(
detail::buffer<Char>& out, basic_string_view<Char> format_str,
basic_format_args<type_identity_t<Context>> args) {
typename ArgFormatter::iterator iter(out);
Context(iter, format_str, args).template format<ArgFormatter>();
return iter;
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...));
}
/**
\rst
Prints formatted data to the stream *os*.
**Example**::
fmt::fprintf(cerr, "Don't %s!", "panic");
\endrst
*/
template <typename S, typename... Args, typename Char = char_t<S>>
inline int fprintf(std::basic_ostream<Char>& os, const S& format_str,
const Args&... args) {
using context = basic_printf_context_t<Char>;
return vfprintf(os, to_string_view(format_str),
make_format_args<context>(args...));
}
FMT_MODULE_EXPORT_END
FMT_END_NAMESPACE
#endif // FMT_PRINTF_H_
+61 -61
View File
@@ -17,33 +17,31 @@
#include "format.h"
// output only up to N items from the range.
#ifndef FMT_RANGE_OUTPUT_LENGTH_LIMIT
# define FMT_RANGE_OUTPUT_LENGTH_LIMIT 256
#endif
FMT_BEGIN_NAMESPACE
template <typename Char> struct formatting_base {
template <typename Char, typename Enable = void> struct formatting_range {
#ifdef FMT_DEPRECATED_BRACED_RANGES
Char prefix = '{';
Char postfix = '}';
#else
Char prefix = '[';
Char postfix = ']';
#endif
template <typename ParseContext>
FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) {
return ctx.begin();
}
};
template <typename Char, typename Enable = void>
struct formatting_range : formatting_base<Char> {
static FMT_CONSTEXPR_DECL const size_t range_length_limit =
FMT_RANGE_OUTPUT_LENGTH_LIMIT; // output only up to N items from the
// range.
Char prefix = '{';
Char postfix = '}';
};
template <typename Char, typename Enable = void>
struct formatting_tuple : formatting_base<Char> {
template <typename Char, typename Enable = void> struct formatting_tuple {
Char prefix = '(';
Char postfix = ')';
template <typename ParseContext>
FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) {
return ctx.begin();
}
};
namespace detail {
@@ -67,8 +65,14 @@ OutputIterator copy(char ch, OutputIterator out) {
return out;
}
template <typename OutputIterator>
OutputIterator copy(wchar_t ch, OutputIterator out) {
*out++ = ch;
return out;
}
/// Return true value if T has std::string interface, like std::string_view.
template <typename T> class is_like_std_string {
template <typename T> class is_std_string_like {
template <typename U>
static auto check(U* p)
-> decltype((void)p->find('a'), p->length(), (void)p->data(), int());
@@ -80,7 +84,7 @@ template <typename T> class is_like_std_string {
};
template <typename Char>
struct is_like_std_string<fmt::basic_string_view<Char>> : std::true_type {};
struct is_std_string_like<fmt::basic_string_view<Char>> : std::true_type {};
template <typename... Ts> struct conditional_helper {};
@@ -249,7 +253,7 @@ template <typename OutputIt> OutputIt write_delimiter(OutputIt out) {
template <
typename Char, typename OutputIt, typename Arg,
FMT_ENABLE_IF(is_like_std_string<typename std::decay<Arg>::type>::value)>
FMT_ENABLE_IF(is_std_string_like<typename std::decay<Arg>::type>::value)>
OutputIt write_range_entry(OutputIt out, const Arg& v) {
*out++ = '"';
out = write<Char>(out, v);
@@ -268,7 +272,7 @@ OutputIt write_range_entry(OutputIt out, const Arg v) {
template <
typename Char, typename OutputIt, typename Arg,
FMT_ENABLE_IF(!is_like_std_string<typename std::decay<Arg>::type>::value &&
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);
@@ -293,8 +297,8 @@ struct formatter<TupleT, Char, enable_if_t<fmt::is_tuple_like<TupleT>::value>> {
}
formatting_tuple<Char>& formatting;
size_t& i;
typename std::add_lvalue_reference<decltype(
std::declval<FormatContext>().out())>::type out;
typename std::add_lvalue_reference<
decltype(std::declval<FormatContext>().out())>::type out;
};
public:
@@ -320,7 +324,7 @@ struct formatter<TupleT, Char, enable_if_t<fmt::is_tuple_like<TupleT>::value>> {
template <typename T, typename Char> struct is_range {
static FMT_CONSTEXPR_DECL const bool value =
detail::is_range_<T>::value && !detail::is_like_std_string<T>::value &&
detail::is_range_<T>::value && !detail::is_std_string_like<T>::value &&
!std::is_convertible<T, std::basic_string<Char>>::value &&
!std::is_constructible<detail::std_string_view<Char>, T>::value;
};
@@ -328,15 +332,14 @@ template <typename T, typename Char> struct is_range {
template <typename T, typename Char>
struct formatter<
T, Char,
enable_if_t<fmt::is_range<T, Char>::value
enable_if_t<
fmt::is_range<T, Char>::value
// Workaround a bug in MSVC 2017 and earlier.
#if !FMT_MSC_VER || FMT_MSC_VER >= 1927
&&
(has_formatter<detail::value_type<T>, format_context>::value ||
detail::has_fallback_formatter<detail::value_type<T>,
format_context>::value)
&& (has_formatter<detail::value_type<T>, format_context>::value ||
detail::has_fallback_formatter<detail::value_type<T>, Char>::value)
#endif
>> {
>> {
formatting_range<Char> formatting;
template <typename ParseContext>
@@ -354,59 +357,58 @@ struct formatter<
for (; it != end; ++it) {
if (i > 0) out = detail::write_delimiter(out);
out = detail::write_range_entry<Char>(out, *it);
if (++i > formatting.range_length_limit) {
out = format_to(out, FMT_STRING("{}"), " ... <other elements>");
break;
}
++i;
}
return detail::copy(formatting.postfix, out);
}
};
template <typename Char, typename... T> struct tuple_arg_join : detail::view {
template <typename Char, typename... T> struct tuple_join_view : detail::view {
const std::tuple<T...>& tuple;
basic_string_view<Char> sep;
tuple_arg_join(const std::tuple<T...>& t, basic_string_view<Char> s)
tuple_join_view(const std::tuple<T...>& t, basic_string_view<Char> s)
: tuple(t), sep{s} {}
};
template <typename Char, typename... T>
struct formatter<tuple_arg_join<Char, T...>, Char> {
using tuple_arg_join = tuple_join_view<Char, T...>;
template <typename Char, typename... T>
struct formatter<tuple_join_view<Char, T...>, Char> {
template <typename ParseContext>
FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) {
return ctx.begin();
}
template <typename FormatContext>
typename FormatContext::iterator format(
const tuple_arg_join<Char, T...>& value, FormatContext& ctx) {
auto format(const tuple_join_view<Char, T...>& value, FormatContext& ctx) ->
typename FormatContext::iterator {
return format(value, ctx, detail::make_index_sequence<sizeof...(T)>{});
}
private:
template <typename FormatContext, size_t... N>
typename FormatContext::iterator format(
const tuple_arg_join<Char, T...>& value, FormatContext& ctx,
detail::index_sequence<N...>) {
auto format(const tuple_join_view<Char, T...>& value, FormatContext& ctx,
detail::index_sequence<N...>) ->
typename FormatContext::iterator {
return format_args(value, ctx, std::get<N>(value.tuple)...);
}
template <typename FormatContext>
typename FormatContext::iterator format_args(
const tuple_arg_join<Char, T...>&, FormatContext& ctx) {
auto format_args(const tuple_join_view<Char, T...>&, FormatContext& ctx) ->
typename FormatContext::iterator {
// NOTE: for compilers that support C++17, this empty function instantiation
// can be replaced with a constexpr branch in the variadic overload.
return ctx.out();
}
template <typename FormatContext, typename Arg, typename... Args>
typename FormatContext::iterator format_args(
const tuple_arg_join<Char, T...>& value, FormatContext& ctx,
const Arg& arg, const Args&... args) {
auto format_args(const tuple_join_view<Char, T...>& value, FormatContext& ctx,
const Arg& arg, const Args&... args) ->
typename FormatContext::iterator {
using base = formatter<typename std::decay<Arg>::type, Char>;
auto out = ctx.out();
out = base{}.format(arg, ctx);
auto out = base().format(arg, ctx);
if (sizeof...(Args) > 0) {
out = std::copy(value.sep.begin(), value.sep.end(), out);
ctx.advance_to(out);
@@ -416,6 +418,8 @@ struct formatter<tuple_arg_join<Char, T...>, Char> {
}
};
FMT_MODULE_EXPORT_BEGIN
/**
\rst
Returns an object that formats `tuple` with elements separated by `sep`.
@@ -428,14 +432,15 @@ struct formatter<tuple_arg_join<Char, T...>, Char> {
\endrst
*/
template <typename... T>
FMT_CONSTEXPR tuple_arg_join<char, T...> join(const std::tuple<T...>& tuple,
string_view sep) {
FMT_CONSTEXPR auto join(const std::tuple<T...>& tuple, string_view sep)
-> tuple_join_view<char, T...> {
return {tuple, sep};
}
template <typename... T>
FMT_CONSTEXPR tuple_arg_join<wchar_t, T...> join(const std::tuple<T...>& tuple,
wstring_view sep) {
FMT_CONSTEXPR auto join(const std::tuple<T...>& tuple,
basic_string_view<wchar_t> sep)
-> tuple_join_view<wchar_t, T...> {
return {tuple, sep};
}
@@ -451,17 +456,12 @@ FMT_CONSTEXPR tuple_arg_join<wchar_t, T...> join(const std::tuple<T...>& tuple,
\endrst
*/
template <typename T>
arg_join<const T*, const T*, char> join(std::initializer_list<T> list,
string_view sep) {
return join(std::begin(list), std::end(list), sep);
}
template <typename T>
arg_join<const T*, const T*, wchar_t> join(std::initializer_list<T> list,
wstring_view sep) {
auto join(std::initializer_list<T> list, string_view sep)
-> join_view<const T*, const T*> {
return join(std::begin(list), std::end(list), sep);
}
FMT_MODULE_EXPORT_END
FMT_END_NAMESPACE
#endif // FMT_RANGES_H_
+236
View File
@@ -0,0 +1,236 @@
// 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_WCHAR_H_
#define FMT_WCHAR_H_
#include <cwchar>
#include <tuple>
#include "format.h"
FMT_BEGIN_NAMESPACE
namespace detail {
template <typename T>
using is_exotic_char = bool_constant<!std::is_same<T, char>::value>;
}
FMT_MODULE_EXPORT_BEGIN
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;
#else
template <typename... Args>
using wformat_string = basic_format_string<wchar_t, type_identity_t<Args>...>;
#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 {
constexpr auto operator"" _format(const wchar_t* s, size_t n)
-> detail::udl_formatter<wchar_t> {
return {{s, n}};
}
#if FMT_USE_USER_DEFINED_LITERALS && !FMT_USE_NONTYPE_TEMPLATE_PARAMETERS
constexpr detail::udl_arg<wchar_t> operator"" _a(const wchar_t* s, size_t) {
return {s};
}
#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);
}
// 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)>
auto format(const S& format_str, Args&&... args) -> std::basic_string<Char> {
const auto& vargs = fmt::make_args_checked<Args...>(format_str, args...);
return vformat(to_string_view(format_str), vargs);
}
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, 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, to_string_view(format_str),
fmt::make_args_checked<Args...>(format_str, 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, to_string_view(format_str), args);
return detail::get_iterator(buf);
}
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 {
const auto& vargs = fmt::make_args_checked<Args...>(fmt, args...);
return vformat_to(out, to_string_view(fmt), vargs);
}
template <typename S, typename... Args, typename Char, size_t SIZE,
typename Allocator, FMT_ENABLE_IF(detail::is_string<S>::value)>
FMT_DEPRECATED auto format_to(basic_memory_buffer<Char, SIZE, Allocator>& buf,
const S& format_str, Args&&... args) ->
typename buffer_context<Char>::iterator {
const auto& vargs = fmt::make_args_checked<Args...>(format_str, args...);
detail::vformat_to(buf, to_string_view(format_str), vargs);
return detail::buffer_appender<Char>(buf);
}
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, to_string_view(format_str), args, detail::locale_ref(loc));
return detail::get_iterator(buf);
}
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 {
const auto& vargs = fmt::make_args_checked<Args...>(format_str, args...);
return vformat_to(out, loc, to_string_view(format_str), vargs);
}
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> {
const auto& vargs = fmt::make_args_checked<Args...>(fmt, args...);
return vformat_to_n(out, n, to_string_view(fmt), vargs);
}
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;
const auto& vargs = fmt::make_args_checked<Args...>(fmt, args...);
detail::vformat_to(buf, to_string_view(fmt), vargs);
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), make_wformat_args(args...));
}
template <typename... T> void print(wformat_string<T...> fmt, T&&... args) {
return vprint(wstring_view(fmt), make_wformat_args(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_MODULE_EXPORT_END
FMT_END_NAMESPACE
#endif // FMT_WCHAR_H_